MCP Hub
Back to servers

Agentic MCP Itinerary

An MCP server that orchestrates parallel searches for flights, hotels, and activities to generate travel itineraries. It features persistent state management via LangGraph and SQLite while ensuring the total cost remains within a specified budget.

glama
Updated
Mar 26, 2026

Agentic MCP Itinerary — PoC

Un MCP server que internamente corre un agente LLM (Gemini Flash + LangGraph) y orquesta múltiples MCP servers downstream. El cliente (Claude Desktop, ChatGPT) ve una interfaz limpia con estado persistente entre iteraciones.

Concepto

Claude Desktop / ChatGPT
        │
        │  MCP (HTTP/SSE + OAuth 2.1)
        ▼
┌─────────────────────────────────────┐
│         travel-agent (este repo)    │
│  FastMCP server + LangGraph agent   │
│                                     │
│  ┌──────┐  ┌────────┐  ┌──────────┐│
│  │Vuelos│  │Hoteles │  │Actividad.││  ← MCP mocks STDIO
│  └──────┘  └────────┘  └──────────┘│
└─────────────────────────────────────┘

¿Por qué esto es difer? Ninguna empresa ofrece todavía un "agente vertical empaquetado como MCP server". Este PoC demuestra el patrón: el cliente solo ve 4-5 tools limpias, pero detrás hay un agente con memoria, fan-out paralelo y estado persistente.


Stack

ComponenteTecnología
Servidor MCP expuestoFastMCP 3.1.1 (streamable-http)
Agente internoLangGraph (StateGraph + fan-out paralelo)
Modelo LLMGemini Flash (gemini-2.0-flash)
AuthOAuth 2.1 Authorization Code Flow + JWT HS256
CheckpointingMemorySaver (en memoria, suficiente para PoC)
MCP downstreamMCP SDK oficial (mcp.client.stdio)
Mocks3 FastMCP servers STDIO (vuelos, hoteles, actividades)
DeployRailway (RAILPACK + pyproject.toml)

Tools expuestas (API pública)

ToolParámetrosDescripción
create_itineraryrequirements: strCrea un draft completo (vuelos + hotel + actividades en paralelo)
refine_itineraryitinerary_id: str, change_request: strRefina un borrador existente
get_itineraryitinerary_id: strRecupera el estado actual
list_itinerariesLista todos los itinerarios activos
confirm_itineraryitinerary_id: strConfirma y genera confirmation_code

Deploy en Railway

URLs

IDs Railway

  • Proyecto: e50da57f-ee0b-47a3-81a3-55556fe6de0d
  • Servicio: 09065312-ac84-4876-b9c9-dd5d6439f1d4
  • Environment: 09b3f0c9-e5ad-4f61-b351-275bbcffd5ad

Variables de entorno requeridas

VariableDescripción
GEMINI_API_KEYAPI key de Google Gemini
MCP_USERNAMEUsuario para el login OAuth
MCP_PASSWORDContraseña para el login OAuth
MCP_JWT_SECRETSecreto para firmar JWT (generado con secrets.token_urlsafe(32))
MCP_BASE_URLURL pública del servidor (para construir redirect URIs)

Auth: OAuth 2.1 Authorization Code Flow

Flujo completo

1. Claude Desktop detecta el MCP server
2. Descubre /.well-known/oauth-authorization-server
3. Redirige al usuario a /authorize
4. El servidor redirige a /oauth/authorize (form de login HTML)
5. Usuario introduce user/pass → POST /oauth/authorize
6. Servidor valida credenciales (MCP_USERNAME / MCP_PASSWORD)
7. Emite auth code → redirect a Claude Desktop
8. Claude Desktop intercambia code → JWT en /token
9. JWT usado como Bearer en todas las llamadas MCP

Implementación

  • server/auth.py: SimpleOAuthProvider (extiende OAuthProvider de FastMCP)
  • JWT HS256, 1h de validez
  • Auth codes: 5 min de validez
  • PKCE (S256) soportado
  • /health permanece público sin auth

Configurar Claude Desktop

Edita ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "travel-agent": {
      "type": "http",
      "url": "https://travel-agent-production-c1c4.up.railway.app/mcp"
    }
  }
}

Sin headers — Claude Desktop gestiona el OAuth flow automáticamente. La primera vez abrirá el browser para el login.


Desarrollo local

Requisitos

pip install -e ".[dev]"

Arrancar servidor

PYTHONPATH=server MCP_USERNAME=alexguerra MCP_PASSWORD=tu_pass \
  MCP_JWT_SECRET=dev_secret python3 server/main.py

Smoke test

PYTHONPATH=server python3 tests/smoke_test.py

Verificar sintaxis

PYTHONPATH=server python3 -m py_compile server/main.py server/auth.py server/agent.py

Estructura del proyecto

agentic-mcp-itinerary/
├── server/
│   ├── main.py          # FastMCP server (4 tools + OAuth + /health)
│   ├── auth.py          # SimpleOAuthProvider (OAuth 2.1 + JWT)
│   ├── agent.py         # LangGraph graph con fan-out paralelo
│   ├── state.py         # ItineraryState TypedDict + checkpointer
│   └── tools/
│       ├── flights.py   # Cliente MCP → mock vuelos
│       ├── hotels.py    # Cliente MCP → mock hoteles
│       └── activities.py # Cliente MCP → mock actividades
├── mocks/
│   ├── flights_mcp.py   # Mock server vuelos (FastMCP STDIO)
│   ├── hotels_mcp.py    # Mock server hoteles (FastMCP STDIO)
│   └── activities_mcp.py # Mock server actividades (FastMCP STDIO)
├── tests/
│   └── smoke_test.py    # Test end-to-end básico
├── docs/
│   └── OAUTH_PLAN.md    # Spec del OAuth (referencia de diseño)
├── pyproject.toml       # Deps para RAILPACK
├── railway.toml         # Builder=RAILPACK, startCommand
└── claude_desktop_config.json  # Config para Claude Desktop (sin Bearer manual)

Historial de decisiones clave

DecisiónAlternativa descartadaMotivo
RAILPACK + pyproject.tomlnixpacksnixpacks falla en pip dentro de env inmutable
OAuth 2.1 Authorization CodeStatic Bearer tokenClaude Desktop gestiona OAuth nativo; más producción-ready
JWT HS256 en memoriaDB de tokensPoC — sin estado persistente entre reinicios
FastMCP 3.1.1 OAuthProviderAuth manual con StarletteFastMCP integra el flow con el transport MCP
MemorySaverSQLite/RedisSuficiente para PoC local; fácil migrar a SqliteSaver
Gemini FlashClaude HaikuCodex tenía conflicto de credenciales con Anthropic

Próximos pasos (post-PoC)

  • Test en Claude Desktop — verificar OAuth flow completo
  • Persistencia realSqliteSaver o Postgres para estado entre reinicios
  • Downstream MCP reales — reemplazar mocks por APIs reales (Amadeus, Booking, etc.)
  • Multi-usuario — DB de users en lugar de env vars
  • Rate limiting — por token JWT
  • Telemetría — LangSmith o similar para trazar el agente interno

Reviews

No reviews yet

Sign in to write a review