Custom MCP server exposing domain-shaped travel concierge tools that encode travel-specific business logic on top of Aurora PostgreSQL. Provides tools for package comparison, seasonal pricing, inventory management, currency conversion, and loyalty balances.
This MCP server exposes 12 tools with generally well-structured schemas and descriptions. Most tools have explicit input schemas with proper types and field descriptions. However, there are significant gaps: output schemas are NOT documented anywhere in the visible code, descriptions vary widely in quality (some excellent, some generic), and error handling guidance is largely absent. The server demonstrates domain expertise (travel concierge, loyalty programs, memory/preference persistence) but lacks the rigor expected of production-grade agent tools. Most tools are READ_ONLY and follow a consistent pattern, which helps consistency. The main weaknesses are missing output documentation, incomplete error recovery guidance, and lack of tool annotations (destructiveHint, readOnlyHint, idempotentHint).
Return aligned comparison rows for up to 4 trip packages. Each row includes pricing, duration list, and the top 3 highlights so an agent can answer "compare these" without writing comparison SQL itself.
Convert an amount between supported currencies using indicative rates. Returns the converted amount + the rate used. Indicative only - not for settlement.
Self-describe — what this server exposes. Useful when an agent first connects.
Return loyalty points balance + tier for a traveler. The profile is read inside scoped_session so the workload must hold an active grant for traveler_id before any row is touched; a traveler the workload is not bound to is refused with a structured error rather than a balance. A missing program or balance is unavailable; it must never be replaced by a generated points total.
Upsert a single durable preference fact for the traveler. `preference_id` is the table's primary key with no default, so a new fact needs one allocated here. On conflict the existing row keeps its id.
Output schemas NOT documented for any of the 12 tools. LLMs cannot know what fields to expect in responses, forcing them to guess and leading to downstream parsing errors or missed data.
No error handling guidance in any tool description. When a tool fails (e.g. traveler not found, invalid currency, embedding timeout), the description does not tell the LLM what to do next or whether to retry.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 68 | <=2025-11-25 | v2 |
| 2026-03-09 | F | 43 | - | v1 |
Append a single turn to conversation_messages, with embedding when possible.
Return durable preference facts for the traveler, ordered by confidence.
Return the last N turns from conversation_messages. `traveler_id` is required because the conversation row is RLS-scoped to the owning traveler — passing the wrong id would return zero rows.
Return the traveler's profile row (name, home airport, party size, budget, dietary notes). All reads are scoped via Aurora RLS to the supplied `traveler_id`. Returns an empty dict if the traveler does not exist or RLS rejects the row.
Return how many distinct trip packages we sell in a region, with aggregate availability across all duration options.
Return seasonal price band (low / median / high) for a destination. The band is computed from current trip_packages prices for the destination, modulated by a deterministic seasonal multiplier so the same query returns the same answer across demo runs.
Return past trip interactions whose embedding is closest to the query. Uses cohere.embed-v4:0 (1024 dims) → pgvector cosine via Aurora.
No tool annotations present (readOnlyHint, destructiveHint, idempotentHint). The 2 WRITE tools (persist_turn, persist_preference) should be marked destructiveHint:false (they are safe writes, not deletes) or idempotentHint:true to guide agent retry logic. All READ_ONLY tools should be marked to signal they are safe to call freely.
Enum constraints missing for categorical parameters. region_inventory.trip_type, loyalty_balance.program, and recall_recent_turns do not declare valid values as enums; descriptions mention examples ('City Breaks', 'Wellness & Luxury', 'Marriott Bonvoy') but JSON Schema enums are not enforced, letting LLMs hallucinate invalid values.
No pagination guidance. Tools returning lists (region_inventory, recall_preferences, recall_recent_turns, semantic_recall_interactions) accept a 'limit' parameter but do not document whether there are more results available, how to fetch the next page, or what the maximum result set size is without pagination.
persist_turn and persist_preference lack idempotency guarantees. If an LLM retries after a network hiccup, will duplicate turns or preferences be created? Descriptions do not clarify. For persist_preference, upsert semantics are mentioned but not for persist_turn.
semantic_recall_interactions relies on external embedding service (cohere.embed-v4:0) with no documented timeout or fallback. If Cohere is down, does the tool fail, return empty results, or hang? No error guidance provided.
RLS (Row-Level Security) scoping is mentioned in descriptions but not consistently. recall_traveler_profile and recall_recent_turns explicitly explain RLS behavior; loyalty_balance mentions scoped_session but is less clear; other tools do not mention it at all. Inconsistent documentation invites misuse.