使用说明
代理mcp行程
打包为MCP服务器的LLM代理。客户(Claude Desktop,ChatGPT)看到5个干净的工具;内部运行带有并行扇出、持续状态和OAuth 2.1的LangGraph图表。
部署在铁路上: https://<your-railway-url>/mcp
演示
这是与连接到服务器的Claude Desktop的真实对话:
Usuario: Quiero 5 días en Roma para 2 personas, presupuesto 2000€,
saliendo el 15 de junio
Agente: [llama create_itinerary]
## Itinerario Roma — 15–19 Jun · 2 personas
### Vuelos
| Tramo | Vuelo | Salida | Llegada | Precio |
|----------------|-------|--------|---------|--------|
| MAD → FCO | IB610 | 07:30 | 10:15 | 180€ |
| FCO → MAD | IB611 | 19:45 | 22:30 | 195€ |
### Alojamiento
Hotel Artemide ★★★★ — Via Nazionale, Roma — 4 noches · 140€/noche
### Actividades
Día 1: Coliseo + Foro Romano (35€/p.) · Día 2: Vaticano (25€/p.)
Día 3: Trastevere libre · Día 4: Borghese + Trevi
**Total: 1.855€** (margen: 145€)
ID: `itn_a3f9c2`
Usuario: Cambia el hotel por algo más céntrico
Agente: [llama refine_itinerary con itn_a3f9c2]
Retomando estado... hotel actualizado.
Hotel Nazionale ★★★★ — Piazza Montecitorio — 155€/noche
**Total actualizado: 1.875€**
Usuario: Perfecto, confírmalo
Agente: [llama confirm_itinerary]
✅ Itinerario confirmado
🔖 Código: `CONF-7X2K9P`
建筑
Claude Desktop / ChatGPT
│
│ MCP (HTTP/SSE + OAuth 2.1)
│
▼
┌─────────────────────────────────────┐
│ travel-agent (este repo) │
│ FastMCP server + LangGraph agent │
│ │
│ ┌──────┐ ┌────────┐ ┌──────────┐│
│ │Vuelos│ │Hoteles │ │Actividad.││ ← MCP mocks STDIO
│ └──────┘ └────────┘ └──────────┘│
└─────────────────────────────────────┘
为什么这是不同的: 客户只看到5个干净的工具,但背后有一个具有内存、与3个服务并行的扇出和轮班之间的持久状态的代理。目前还没有公司提供“封装为MCP服务器的垂直代理”。
堆栈
| 组件 | 技术 |
|---|---|
| 暴露的MCP服务器 | FastMCP 3.1.1(streamable-http) |
| 内部代理 | LangGraph (StateGraph +平行扇出)。 |
| LLM型号 | 双子座闪光灯(gemini-2.0-flash) |
| Auth | OAuth 2.1授权码流+JWT HS256 |
| 检查点 | MemorySaver (轮班之间的状态) |
| MCP下游 | MCP SDK官方(mcp.client.stdio) |
| MOCKS | 3 FASTMCP服务器STDIO(航班、酒店、活动) |
| 部署 | 铁路(RAILPACK+pyproject.toml) |
暴露的工具
| 工具 | 参数 | 描述 |
|---|---|---|
create_itinerary |
requirements: str |
并行创建完整的草稿 |
refine_itinerary |
itinerary_id, change_request |
修改而不重新计划一切 |
get_itinerary |
itinerary_id |
恢复当前状态 |
list_itineraries |
- | 列出会议日程安排 |
confirm_itinerary |
itinerary_id |
确认并生成 confirmation_code |
快速启动
本地
pip install -e ".[dev]"
PYTHONPATH=server \
MCP_USERNAME=user MCP_PASSWORD=pass \
MCP_JWT_SECRET=dev_secret \
python3 server/main.py
Conectar克劳德桌面
编辑 ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"travel-agent": {
"type": "http",
"url": "https://<your-railway-url>/mcp"
}
}
}
Claude Desktop第一次自动管理OAuth流。
环境变量
| 变量 | 描述 |
|---|---|
GEMINI_API_KEY |
API密钥de Google Gemini |
MCP_USERNAME |
OAUTH登录用户 |
MCP_PASSWORD |
OAuth登录密码 |
MCP_JWT_SECRET |
秘密JWT(secrets.token_urlsafe(32)) |
MCP_BASE_URL |
服务器的公共URL |
结构
agentic-mcp-itinerary/
├── server/
│ ├── main.py # FastMCP server (5 tools + OAuth + /health)
│ ├── auth.py # OAuth 2.1 + JWT HS256
│ ├── agent.py # LangGraph graph con fan-out paralelo
│ ├── state.py # ItineraryState TypedDict + checkpointer
│ └── tools/
│ ├── flights.py
│ ├── hotels.py
│ └── activities.py
├── mocks/
│ ├── flights_mcp.py
│ ├── hotels_mcp.py
│ └── activities_mcp.py
├── tests/
│ └── smoke_test.py
├── pyproject.toml
└── railway.toml
设计决策
| 决定 | 排除的替代方案 | 原因 |
|---|---|---|
| railpack+pyproject.toml | nixpacks | nixpacks在env不变的情况下在pip中失败 |
| OAuth 2.1授权码 | 静态承载令牌 | 克劳德桌面管理OAuth nativo |
| 内存中的JWT HS256 | 令牌DB | POC-重启之间没有持久状态 |
FastMCP OAuthProvider |
与Starlette的Auth Manual | 将Flow与Transport MCP集成 |
MemorySaver |
SQLite/Redis | 适用于 PoC;易于迁移。 |
| 双子座闪光 | 克劳德俳句 | 环境中的人类证书冲突 |
下一步
- []下游MCP reales--Amadeus,预订,国际
- []真正的持久性-
SqliteSaver或Postgres - []多用户-DB用户而不是ENV VAR
- []JWT代币限速
- []遥测-langsmith跟踪代理