Agentic Travel Recommendations Service
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Agentic Travel Recommendations ServiceWhat are the top travel recommendations for MEMBER-001?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Agentic Travel Recommendations Service
This project is a TypeScript and Node.js proof-of-concept for a multi-tenant travel recommendation service. It exposes shared recommendation capabilities through a REST API, Streamable HTTP MCP endpoint, and command-line interface.
Key Features
REST API for health, member profiles, and recommendations
Streamable HTTP MCP endpoint
MCP tool:
get_member_profileMCP tool:
get_recommendationsAuthoritative, member-derived tenant resolution
Partner-specific recommendation caps
Partner-specific category exclusions
Deterministic recommendation generation
Fail-closed partner configuration behavior
Request IDs and structured JSON logs
Minimal CLI demonstration
Multi-stage Docker build
Automated tests
Architecture at a Glance
REST / MCP / CLI
|
v
RecommendationService
|
v
MemberDataService
|
| member.partnerId
v
PartnerConfigurationService
|
v
CandidateGenerator
|
v
RecommendationPolicy
|
| exclusions then cap
v
Final RecommendationsCallers provide only memberId; they do not select the authoritative partnerId. The member profile determines the partner configuration, and REST, MCP, and CLI all reuse the same business layer.
Quick Start
npm ci
npm run devThe service is available by default at http://localhost:3000.
Type Checking
npm run typecheck
npm run typecheck:test
npm run typecheck:allTests
npm testThe current verified baseline is 46 passing tests across 6 files.
Production Build
npm run build
npm startREST API
GET /health
GET /api/members/:memberId
GET /api/recommendations/:memberIdExample requests:
curl http://localhost:3000/api/members/MEMBER-001
curl http://localhost:3000/api/recommendations/MEMBER-001MCP
The MCP server is exposed through:
POST /mcpIt provides these tools:
get_member_profileget_recommendations
Both tools accept only the member identifier:
{
"memberId": "MEMBER-001"
}The implementation uses the official @modelcontextprotocol/sdk Streamable HTTP transport. The caller does not supply partnerId; it is resolved from the authoritative member profile.
CLI
npm run cli -- MEMBER-001Demo members:
MEMBER-001→BANK_AMEMBER-002→BANK_BMEMBER-003→CREDIT_UNION_C
Docker
docker build -t agentic-travel-recommendations .
docker run --rm -p 3000:3000 agentic-travel-recommendationsThe image uses a multi-stage build, a Node 24 runtime, and a non-root runtime user. The HTTP server handles graceful shutdown signals.
Section A — Architecture & Trade-offs
Architecture Overview
The service is a stateless TypeScript and Node.js application that exposes the same recommendation workflow through REST, Streamable HTTP MCP, and a CLI. Each transport validates its input and delegates to the shared RecommendationService; transport handlers do not implement partner policy themselves.
The authoritative tenant flow is:
memberId
→ MemberDataService
→ MemberProfile.partnerId
→ PartnerConfigurationService
→ CandidateGenerator
→ RecommendationPolicy
→ final recommendationsCallers supply memberId and never select the authoritative partnerId. The member profile returned by MemberDataService determines which partner configuration is retrieved. Both upstream services also correlate the identity embedded in a response with the identity requested, and RecommendationService performs an additional partner-identity check before generation.
Candidate generation is intentionally independent of partner policy. The deterministic generator first produces raw candidates from the member profile; the generic policy layer then removes candidates in excludedCategories and applies recommendationCap, in that order. Only the resulting recommendations are returned. The Member Data Service and Partner Configuration Service are mocked for this proof-of-concept, and partner configuration access is read-only.
Design Trade-offs
Correctness over availability. If authoritative partner configuration is missing, unavailable, schema-invalid, or identity-mismatched, the request fails closed. The service does not substitute permissive defaults or return unrestricted recommendations. This may reduce availability during an upstream failure, but prevents recommendations from escaping the correct tenant policy.
Fresh configuration over caching. The first release retrieves partner configuration for every recommendation request rather than adding cache infrastructure. This keeps behavior simple and ensures each successful request uses current policy. It accepts additional upstream latency and load; a short-lived cache is appropriate later only if measured performance justifies the consistency trade-off.
Deterministic generation over an external LLM. Candidate generation is reproducible, testable, cost-free, and operationally predictable. This limits the sophistication of personalization, but makes policy behavior and assessment results easy to verify. A future LLM or ranking component could replace candidate generation without changing deterministic policy enforcement.
Handling Partner Configuration Changes
The Partner Configuration Service is a read-only dependency. If a partner changes its recommendation cap from unlimited to 3, or adds cruise to excludedCategories, the recommendation service needs no code change and no tenant-specific branch. The next successful request reads the current configuration, and the generic policy logic applies the new exclusion and cap values.
As long as the configuration remains compatible with the existing schema, the application does not require redeployment. If configuration caching is introduced later, it must have an intentionally short TTL or reliable invalidation strategy because stale configuration could temporarily violate the partner’s current policy.
Section B — Production Readiness & Incident Response
Incident Runbook Entry
Scenario: A member reports that the AI Concierge displayed a cruise recommendation even though their partner excludes cruises.
Identify and correlate. Obtain the request or correlation ID from the report when available and locate the corresponding structured logs. Record the
operation,memberId, authoritativepartnerIdonce resolved,resultCode, and HTTP status where applicable. Travel history and recommendation payloads are intentionally not logged, so use identifiers and outcome metadata for correlation.Verify the authoritative tenant. Retrieve the affected member through
MemberDataServiceand confirm that the requestedmemberIdequals the returnedmember.memberId. Derive the tenant only frommember.partnerId. Do not trust a partner ID supplied by a frontend, MCP caller, query parameter, or support report.Verify partner configuration. Retrieve configuration using
member.partnerId, then confirmconfiguration.partnerId === member.partnerId. InspectexcludedCategoriesandrecommendationCap, and determine whethercruiseis excluded in the current authoritative configuration. Missing, unavailable, malformed, or identity-mismatched configuration must cause the service to fail closed rather than use permissive defaults.Reproduce the pipeline. Run the member through the same recommendation workflow. A cruise in raw
CandidateGeneratoroutput is not itself a defect because generation deliberately ignores partner policy. Verify thatRecommendationPolicyprocessesraw candidates → remove excluded categories → apply recommendation cap → final recommendations, and confirm that cruises are absent from the final result.Isolate the failure location. If cruises appear in raw candidates but not final recommendations, policy is operating correctly. Investigate a stale client response, a response associated with the wrong member, another consumer or endpoint bypassing the expected workflow, or a difference between the reported time and current configuration. If a cruise survives
RecommendationPolicy, inspect category comparison or normalization, authoritative configuration contents and identity, and recent policy changes or regressions.Contain. If the correct partner policy cannot be established or safely reproduced, fail closed instead of returning potentially non-compliant recommendations. Do not attempt to modify the read-only Partner Configuration Service from this application.
Fix and verify. Correct the defect in the responsible layer and add a regression test reproducing the exact failure. Run:
npm run typecheck:all npm test npm run buildVerify the affected partner, at least one unaffected tenant, REST behavior, and MCP behavior when relevant.
Follow up. Record the root cause, affected partner and member scope, impact window, remediation, regression coverage, and preventive action.
Part B2 — Required Reasoning Question
An AI coding assistant could plausibly produce an implementation that Zod-validates upstream member and partner records but never correlates returned identities with requested identities. The code would be type-safe, schema validation and happy-path tests would pass, and a superficial review would see reasonable defensive validation. The missing cross-tenant invariant would still create a serious policy risk.
For example, MEMBER-001 belongs to BANK_A. RecommendationService requests BANK_A’s configuration, but a buggy or misrouted upstream service returns a completely schema-valid BANK_B configuration with an unlimited cap and no category exclusions. Zod correctly accepts its shape, but applying that policy to MEMBER-001 could bypass BANK_A’s restrictions.
I would catch this with an adversarial regression test: request BANK_A while a deliberately faulty configuration-service double returns the valid BANK_B configuration. I would expect InvalidUpstreamDataError, assert that no recommendation result is produced, and explicitly verify that CandidateGenerator.generate was not called. I would add the corresponding member-data test proving that the requested memberId must equal the returned member.memberId.
Before acting on AI-generated code, I would trace authority and execution order rather than rely on types alone. I would verify that member.partnerId, never caller input, selects the tenant; both returned identities match their authoritative requests; and missing, unavailable, or mismatched configuration fails closed without a permissive fallback. I would also confirm that generation cannot begin before policy identity is safely established and that negative, adversarial tests cover these cases alongside normal happy paths.
Section C — AI Usage Log
Interaction 1 — Architecture Review
What I asked
I asked the AI coding assistant to review the challenge and help design a minimal architecture that one engineer could realistically implement. The requested scope included domain models, mocked upstream services, recommendation logic, REST, MCP, CLI, automated testing, and containerization, while avoiding infrastructure that the proof-of-concept did not require.
What the AI provided
It proposed separating domain models, service contracts, mocked upstream services, candidate generation, partner policy, orchestration, and transport adapters. It initially suggested stdio as the simplest MCP transport.
What I kept, changed, or rejected
I kept the layered separation because it lets REST, MCP, and CLI call one business layer instead of implementing rules independently. I rejected stdio as the primary MCP transport and redirected the design to the official MCP SDK’s Streamable HTTP transport at POST /mcp. The assignment describes an internal API that agents should discover and invoke, and HTTP fits the containerized service architecture. I made that choice after comparing the proposal with the assignment’s integration requirements rather than accepting the simplest option automatically.
Interaction 2 — Incremental Implementation
What I asked
I did not ask for the entire application in one prompt. I divided implementation into bounded steps: domain models, schemas and errors, upstream contracts and mocks, candidate generation, recommendation policy, orchestration, REST, MCP, CLI, observability, and Docker. After each step, I reviewed the reported behavior and required typechecking and tests before proceeding.
What the AI provided
The assistant implemented each bounded component with focused tests and reported the files changed and verification results. This made individual design choices visible and reviewable instead of hiding them inside a large generated patch.
What I kept, changed, or rejected
I kept the shared RecommendationService, deterministic CandidateGenerator, separate RecommendationPolicy, member-derived tenant resolution, read-only configuration contract, and shared REST/MCP/CLI business logic. This structure makes tenant policy independently testable and prevents transport-specific rule implementations. I also intentionally retained deterministic generation instead of adding an external LLM dependency. The assessment focuses on service design and policy enforcement, and reproducible output is easier to test, debug, and demonstrate. Each increment was accepted only after its behavior matched the architectural invariants and the checks passed.
Interaction 3 — Production and Security Audit
What I asked
Once the application worked, I asked the AI to stop adding features and audit the repository from the perspectives of a senior engineer, multi-tenant security reviewer, on-call production owner, and REST/MCP API reviewer.
What the AI provided
The audit found that schema-valid upstream responses were not originally correlated with the member or partner identity requested. It also found that malformed JSON could fail before request-ID middleware established request context. Additional lower-priority improvements were suggested.
What I kept, changed, or rejected
I accepted both high-value findings because they affected tenant correctness and safe operations. For identity correlation, the implementation now verifies that a returned memberId matches the requested member, that configuration.partnerId matches the authoritative member.partnerId, and that RecommendationService repeats the configuration identity check defensively. Mismatches fail closed, and adversarial regression tests verify that candidate generation never starts when authoritative configuration cannot be established.
For malformed JSON, request context and the request ID are now established before parsing. Invalid bodies receive a safe structured 400 response without parser details, stack traces, filesystem paths, or raw request content.
I deferred lower-priority ideas such as persistent MCP sessions, additional distributed infrastructure, and more advanced observability because they were unnecessary for the four-week proof-of-concept and would increase operational scope. I evaluated each recommendation against the assignment requirements, tenant correctness, testability, operational risk, and delivery scope. The assistant supplied options and implementation help, but I reviewed the reasoning, selected the changes, and verified them through focused tests and end-to-end checks.
Four-Week First Step
What Ships First
The four-week target is a first shippable internal proof-of-concept. It demonstrates the required workflow with safe tenant enforcement and operational basics; it is not a claim that every capability needed for a broad production rollout is complete.
Week 1 — Service Foundation
Establish the TypeScript and Node.js service foundation.
Define domain models, strict Zod boundary validation, and typed errors.
Add the
MemberDataServicecontract and mock implementation.Add the read-only
PartnerConfigurationServicecontract and mock implementation.Establish authoritative, member-derived tenant resolution and the initial unit-test foundation.
Goal: Establish safe service boundaries and tenant authority before implementing recommendation logic.
Week 2 — Recommendation Workflow
Implement the deterministic
CandidateGeneratorindependently of partner rules.Implement
RecommendationPolicy, including category exclusions and recommendation caps.Enforce the required order: exclusions first, then the cap.
Add
RecommendationServiceorchestration and fail-closed configuration behavior.Cover policy and orchestration with focused unit tests.
Goal: Prove that contractual partner rules are deterministic and independent of candidate generation.
Week 3 — Interfaces and End-to-End Flow
Expose the REST endpoints and Streamable HTTP MCP endpoint.
Provide the MCP tools
get_member_profileandget_recommendations.Add the CLI demonstration.
Route REST, MCP, and CLI through the shared business layer.
Add REST/MCP integration tests and tenant-override tests.
Goal: Demonstrate the complete recommendation workflow through the interfaces required by the assignment.
Week 4 — Production Readiness and Delivery
Add request IDs, correlation fields, structured JSON logging, and safe error handling.
Handle malformed JSON safely and implement graceful shutdown.
Add the Docker multi-stage build and non-root runtime.
Typecheck production source and tests separately.
Perform the production/security audit and add adversarial identity-correlation tests.
Complete final end-to-end and container verification.
Prepare the README, incident runbook, and demonstration video.
Goal: Make the proof-of-concept supportable by the team that owns it on call.
What Comes Later
The following work is deliberately deferred until after the first four-week release:
Real upstream integrations. Replace the mocked
MemberDataServiceandPartnerConfigurationServiceimplementations with real arrivia REST clients while preserving the existing service contracts and identity-correlation invariants.Existing authentication and authorization integration. Integrate with arrivia’s existing identity and gateway mechanisms rather than introducing a new identity platform. Authorization must preserve member-derived tenant authority.
Network resilience. For real upstream HTTP dependencies, validate and configure request and connection timeouts, bounded retries where operations are safe to retry, and explicit failure behavior. Configuration uncertainty must continue to fail closed.
Performance validation. Run realistic load and performance tests before optimizing. Consider short-lived partner-configuration caching only if measurements justify it. Stale policy is a correctness risk, so any cache requires a clear freshness and invalidation strategy.
Recommendation intelligence. Potentially replace or augment the deterministic
CandidateGeneratorwith an LLM, ranking model, or richer personalization.RecommendationPolicymust remain deterministic and outside the model so generated output cannot override partner rules.Production observability. Connect the existing structured events and correlation IDs to arrivia’s approved metrics, tracing, alerting, and operational tooling.
MCP evolution. Consider stateful or resumable MCP behavior only when a concrete product requirement needs cross-request state. The current stateless Streamable HTTP implementation is intentional for this service.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Hotel booking MCP server. Search, book, and manage reservations across 250K+ properties worldwide.
AI marketplace — flights, tours, activities, transport & more via MCP. No auth required.
MCP server exposing the Backtest360 engine API as tools for AI agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Varma904/agentic-travel-recommendations'
If you have feedback or need assistance with the MCP directory API, please join our Discord server