Skip to main content
Glama

Agentic MCP Itinerary — PoC

LLMエージェント(Gemini Flash + LangGraph)を内部で実行し、複数のダウンストリームMCPサーバーを調整するMCPサーバーです。クライアント(Claude Desktop、ChatGPT)には、反復間で状態が保持されるクリーンなインターフェースが表示されます。

コンセプト

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

なぜこれが異なるのか? まだどの企業も「MCPサーバーとしてパッケージ化された垂直型エージェント」を提供していません。このPoCは、クライアントには4〜5個のクリーンなツールしか見えないものの、その裏側にはメモリ、並列ファンアウト、永続的な状態を持つエージェントが存在するというパターンを実証しています。


Related MCP server: ts-travel-mcp-server

スタック

コンポーネント

テクノロジー

公開MCPサーバー

FastMCP 3.1.1 (streamable-http)

内部エージェント

LangGraph (StateGraph + 並列ファンアウト)

LLMモデル

Gemini Flash (gemini-2.0-flash)

認証

OAuth 2.1 Authorization Code Flow + JWT HS256

チェックポイント

MemorySaver (インメモリ、PoCには十分)

ダウンストリームMCP

公式MCP SDK (mcp.client.stdio)

モック

3つのFastMCPサーバー STDIO (フライト、ホテル、アクティビティ)

デプロイ

Railway (RAILPACK + pyproject.toml)


公開ツール (パブリックAPI)

ツール

パラメータ

説明

create_itinerary

requirements: str

完全なドラフトを作成(フライト + ホテル + アクティビティを並列で)

refine_itinerary

itinerary_id: str, change_request: str

既存のドラフトを修正

get_itinerary

itinerary_id: str

現在の状態を取得

list_itineraries

すべてのアクティブな旅程をリストアップ

confirm_itinerary

itinerary_id: str

確定し、confirmation_codeを生成


Railwayへのデプロイ

URL

Railway ID

  • プロジェクト: e50da57f-ee0b-47a3-81a3-55556fe6de0d

  • サービス: 09065312-ac84-4876-b9c9-dd5d6439f1d4

  • 環境: 09b3f0c9-e5ad-4f61-b351-275bbcffd5ad

必要な環境変数

変数

説明

GEMINI_API_KEY

Google GeminiのAPIキー

MCP_USERNAME

OAuthログイン用ユーザー名

MCP_PASSWORD

OAuthログイン用パスワード

MCP_JWT_SECRET

JWT署名用シークレット (secrets.token_urlsafe(32)で生成)

MCP_BASE_URL

サーバーのパブリックURL (リダイレクトURI構築用)


認証: OAuth 2.1 Authorization Code Flow

全体フロー

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

実装

  • server/auth.py: SimpleOAuthProvider (FastMCPのOAuthProviderを拡張)

  • JWT HS256、有効期限1時間

  • 認証コード: 有効期限5分

  • PKCE (S256) 対応

  • /healthは認証なしで公開状態を維持


Claude Desktopの設定

~/Library/Application Support/Claude/claude_desktop_config.jsonを編集します:

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

headersなし — Claude DesktopはOAuthフローを自動的に管理します。初回起動時にログイン用のブラウザが開きます。


ローカル開発

要件

pip install -e ".[dev]"

サーバーの起動

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

スモークテスト

PYTHONPATH=server python3 tests/smoke_test.py

構文チェック

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

プロジェクト構造

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)

主要な決定事項の履歴

決定事項

却下した代替案

理由

RAILPACK + pyproject.toml

nixpacks

nixpacksはイミュータブルな環境内でのpipで失敗するため

OAuth 2.1 Authorization Code

静的Bearerトークン

Claude DesktopがネイティブOAuthを管理するため、より本番環境向け

インメモリJWT HS256

トークンDB

PoCのため — 再起動間の永続状態は不要

FastMCP 3.1.1 OAuthProvider

Starletteによる手動認証

FastMCPがMCPトランスポートとフローを統合しているため

MemorySaver

SQLite/Redis

ローカルPoCには十分。SqliteSaverへの移行も容易

Gemini Flash

Claude Haiku

CodexでAnthropicの認証情報と競合が発生したため


今後のステップ (PoC後)

  • [ ] Claude Desktopでのテスト — 完全なOAuthフローの検証

  • [ ] 真の永続化 — 再起動間の状態保持のためにSqliteSaverまたはPostgresを使用

  • [ ] 実際のダウンストリームMCP — モックを実際のAPI(Amadeus、Bookingなど)に置き換え

  • [ ] マルチユーザー対応 — 環境変数ではなくユーザーDBを使用

  • [ ] レート制限 — JWTトークン単位で実施

  • [ ] テレメトリ — 内部エージェントを追跡するためにLangSmithなどを導入

Related MCP Connectors

Related MCP Servers