English | 日本語
📖 はじめての方はユーザーガイドへ——「つなぐ」「公開する」のやりたいこと別ドキュメントです。この README は網羅的なリファレンスです。
双方向の stdio↔HTTP MCP ゲートウェイです — 同じバイナリが MCP-over-HTTP の
クライアントゲートウェイにも、serve モードによって(OAuth 2.1 認可サーバー
内蔵オプション付きの)フル機能の MCP-over-HTTP サーバーにもなるため、MCP
接続のどちら側にも立てます。
クライアントゲートウェイ(既定モード) — stdio → HTTP。 MCP クライアント(Claude Desktop, Claude Code)に対してローカルで稼働するセルフホスト MCP サーバのように振る舞いつつ、各種認証でリモート MCP サーバーへの接続を橋渡しします:
flowchart BT
A[Claude<br>Desktop/Code] <-- stdio --> B(mcp-stdio)
B <== "<b>HTTPS</b><br>Streamable HTTP / SSE<br>Bearer Token<br>Header<br>OAuth" ==> C[Remote<br>MCP Server]
B -. "OAuth 2.1<br>(PKCE)" .-> D[Authorization<br>Server]
D -. callback .-> B
style B fill:#4a5,stroke:#333,color:#fff
Bearer token、カスタムヘッダー、OAuth 2.1 認証情報をリモートサーバーへ転送します。
逆ゲートウェイ(mcp-stdio serve) — HTTP → stdio。 鏡像の関係にあるモードです。
ローカルの stdio MCP サーバー(言語・フレームワーク問わず)を、bearer トークンや
埋め込み OAuth 2.1 認証、OAuth 有効時のユーザー単位バックエンド分離、再起動耐性の
あるトークンストアと
いったオプション付きで Streamable HTTP エンドポイントとして公開します — 詳細は
下記逆ゲートウェイ: serve モードを参照。ツール定義
自体は既に stdio で問題なく動く場合、フレームワーク自前の HTTP/OAuth ホスティング層
(の一部だけ)の代わりに mcp-stdio を据える、という使い方もできます。
- 両 MCP トランスポート対応 — Streamable HTTP(現行仕様、デフォルト)と SSE(MCP 2024-11-05 レガシー)を
--transportで切り替え。SSE パーサは WHATWG Server-Sent Events 仕様 に準拠。 - OAuth 2.1 クライアント — 認可コードフロー(PKCE)、動的クライアント登録、トークンリフレッシュ、安全なトークン永続化を内蔵。MCP 認可仕様の関連 RFC にセクション単位で対応:
- RFC 9728 Protected Resource Metadata
- §3
/.well-known/oauth-protected-resourceによる認可サーバー検出 - §3.1 パスベースのリバースプロキシ配下に対応した well-known URL 構築(ホストルートへのフォールバック付き)。リソース URL の query component も構築後の metadata URL に保持する
- §3.3
resourceフィールド検証(不一致は警告して続行) - §5.1
WWW-Authenticate: Bearer resource_metadata=ヒント — discovery 前にサーバーへ probe を送り、well-known パスの推測に頼らず PRM の所在を直接特定する
- §3
- RFC 8414 Authorization Server Metadata
- §3.1 well-known URL 構築。パス付き issuer のパス挿入ルール対応
- §3.3
issuer検証(クロスオリジンの issuer は AS mix-up 対策で拒否、同一オリジンの差異〔trailing slash / path / case〕は警告して続行) - §3 OpenID Connect Discovery 1.0 フォールバック(OAuth の well-known が 404 のとき
/.well-known/openid-configuration〔path-append / path-insertion〕を試行。OIDC 形式のみ公開する AS〔Auth0・Okta・Azure AD・Google〕に対応)
- RFC 8707 Resource Indicators
- §2
resourceパラメータを認可リクエスト・トークン交換・リフレッシュに送信
- §2
- RFC 7636 PKCE
- §4.1–4.2 S256
code_challenge_method、86 文字のcode_verifier
- §4.1–4.2 S256
- RFC 8628 Device Authorization Grant
- §3.1
resourceインジケータ付きデバイス認可リクエスト(RFC 8707) - §3.4–3.5
authorization_pending/slow_down(interval +=5 s)/expired_token/access_deniedハンドリング - DCR の
grant_typesにurn:ietf:params:oauth:grant-type:device_codeを登録(RFC 7591 §2)
- §3.1
- RFC 7591 Dynamic Client Registration
- §3 クライアント登録リクエスト。AS メタデータの
token_endpoint_auth_methods_supportedから最適な認証方式を選択(none→client_secret_post→client_secret_basicの優先順) - §3.2.1
client_secret_expires_atに対応、期限切れ時に自動再登録 - DCR に
application_type: "native"(RFC 8252 §8.4 / MCP SEP-837)。loopback 認可コード・ヘッドレス device フローは native クライアントなので、RFC 7591 既定の"web"扱いで loopback redirect が拒否されるのを防ぐ
- §3 クライアント登録リクエスト。AS メタデータの
- Client ID Metadata Documents(MCP 2025-11-25 / draft-ietf-oauth-client-id-metadata-document-00)
--client-metadata-urlで運用者がホストする HTTPS ドキュメント URL をclient_idとして提示し、Dynamic Client Registration を省略する。AS メタデータがclient_id_metadata_document_supportedをまだ広告していなくても、設定されていれば尊重する(黙ってフォールバックせず警告を出す)。事前登録済みの client_id(--client-idまたはMCP_OAUTH_CLIENT_ID)が指定された場合はそちらが優先される(#60)- ホストするドキュメントの
redirect_urisには mcp-stdio の loopback コールバックを ポート無し(http://127.0.0.1/callback)で含める必要がある。実際のコールバックは毎回異なる ephemeral port にバインドされるため、AS は loopback リダイレクト URI について任意のポートを許可しなければならない(RFC 8252 §7.3 / §8.4)
- RFC 6749 OAuth 2.0
- §2.3.1
client_secret_basic:percent-encode した認証情報をAuthorization: Basicヘッダーで送信(コード交換・トークンリフレッシュ・Device Authorization Grant ポーリングに適用)
- §2.3.1
- RFC 6750 Bearer Token の利用
- §2.1
Authorization: Bearer <token>リクエストヘッダー
- §2.1
- RFC 9728 Protected Resource Metadata
- 新しい MCP(2026-07-28)のサーバーに対応 —
--protocol-era autoを付けると、mcp-stdio がサーバーにどちらのプロトコルかを尋ねて自動で合わせます。MCP クライアント側の設定変更は不要です。フラグを付けなければ挙動は変わらないので、バージョンを上げるだけなら安全です。mcp-stdio serveは同じアドレスで新旧どちらのクライアントにも自動応答します。--modern-onlyを付ければ新しいクライアント専用にでき、--modern-idle-ttlは遊んでいるバックエンドを回収します。新しいクライアントは接続をひとつ開いたままにして、ツール・プロンプト・リソースの一覧が変わったことや、名指しした個別リソースの更新を受け取れます(あなたのサーバーがresources.subscribeを表明していれば、mcp-stdio が代わりに購読します)。python-sdk v2.0.0 に対して双方向でエンドツーエンド検証済み。→ 新しい MCP のサーバーを使う - バックオフ付きリトライ — 接続エラー時に最大3回リトライ
- HTTP 429 / 503 対応 —
Retry-After(delta-seconds または HTTP-date)を 60 秒上限で尊重する。対象は仕様上Retry-Afterを伴う 429(Too Many Requests)と 503(Service Unavailable)の 2 つ(RFC 9110 §10.2.3)。上限超過時はステータスをクライアントに返して判断を委ねる(cf. modelcontextprotocol/typescript-sdk#1892) - 自動ページネーション(Streamable HTTP トランスポート) —
tools/list/resources/list/resources/templates/list/prompts/listのnextCursorを透過的に追従して 1 つのレスポンスにマージ。先頭以降のページを取りこぼすクライアントでも全件を受け取れる(cf. anthropics/claude-code#39586) - ストリーミング耐性 — SSE レスポンスをリアルタイムで転送、ストリーム切断時に自動再接続
- 行区切り文字の安全化 — 上流レスポンス中の生の
U+2028/U+2029(JSON では合法だが JavaScript の行終端文字)をエスケープし、これらを改行として扱うクライアントによるフレーム崩れを防止。ロスレス(cf. modelcontextprotocol/typescript-sdk#2155) - 引数の正規化 —
tools/callのargumentsがnullの場合は{}に書き換え、null 形式を拒否する厳格なサーバーでも呼び出せるようにする。デフォルト有効、--no-normalize-argumentsで無効化(cf. modelcontextprotocol/typescript-sdk#2012) - キャンセル対応フィルタ — stdin の
notifications/cancelledでキャンセルされた id を追跡し、その id を持つ遅延レスポンスがクライアントに届く前に drop する(MCP キャンセル仕様準拠)。デフォルト有効(TTL 60 秒)、--no-cancel-filterで無効化(cf. anthropics/claude-code#51073) - SSE 切断時のエラー合成 — legacy SSE transport では応答が長寿命の GET ストリームだけに届くため、ストリーム切断時に POST 済みリクエストは永久にハングしてしまう。mcp-stdio は現行ストリームで in-flight の id を追跡し、切断時に各 id へ JSON-RPC
-32000エラーを合成——クライアントはハングせず再試行できる——しつつストリームを自動再接続する。キャンセル済み id はスキップ(cf. anthropics/claude-code#60061) - セッション回復 — 404 でセッション ID をリセットして再試行
- プロトコルバージョンヘッダー —
initialize応答から交渉済みのprotocolVersionを捕捉し、以降の Streamable HTTP リクエストすべてにMCP-Protocol-Versionを付与(MCP 仕様 rev 2025-06-18)。このヘッダーを強制するサーバーは未送信時に初期化後リクエストを400 Bad Requestで拒否する - 401 時の自動トークンリフレッシュ — セッション中に OAuth トークンが失効しても自動更新(OAuth モード時のみ)
- プロアクティブなトークンリフレッシュ — バックグラウンドタイマーが OAuth トークンを失効直前にリフレッシュ(リード時間は
--oauth-refresh-leeway)。トークン失効をトランスポート層の 401 ではなく HTTP 200 のツールエラーとして返すゲートウェイ(例: Atlassian の MCP ゲートウェイ)でも長時間セッションが生き残る。OAuth モードではデフォルト有効、--no-proactive-refreshで無効化(#242) - 403 時のステップアップ認可 —
Bearer error="insufficient_scope"チャレンジを受けると、付与済みスコープと要求スコープの和集合で再認可(RFC 9470 / MCP step-up、cf. anthropics/claude-code#44652) - コールドスタート(
--oauth-eager) —initializeをローカルで即答し、対話 OAuth フローはバックグラウンドスレッドで実行。30〜180 秒かかるブラウザ/SSO/MFA ログインでもクライアントの約 60 秒 initialize タイムアウトを超えない。ログイン完了まで該当メソッドは-32002を返し、完了後にnotifications/*/list_changedでクライアントに再取得を促す。Streamable HTTP 限定、warm(有効/refresh 可能)キャッシュは不変(#296) - Bearer token 認証 —
--bearer-tokenフラグまたはMCP_BEARER_TOKEN環境変数 - カスタムヘッダー —
-H/--headerで任意のヘッダーを送信 - グレースフルシャットダウン — SIGTERM/SIGINT ハンドリング
- プロキシ対応 —
HTTP_PROXY,HTTPS_PROXY,NO_PROXY環境変数を httpx 経由でサポート - 最小依存 — httpx のみ; OAuth は stdlib のみ使用
pip install mcp-stdiouv を使う場合:
uv tool install mcp-stdioインストールせずに直接実行:
uvx mcp-stdio https://your-server.example.com:8080/mcpHomebrew を使う場合:
brew install shigechika/tap/mcp-stdiomcp-stdio https://your-server.example.com:8080/mcpBearer token 認証付き:
# 推奨: 環境変数を使用(トークンが `ps` に表示されない)
MCP_BEARER_TOKEN=YOUR_TOKEN mcp-stdio https://your-server.example.com:8080/mcp
# または直接指定(トークンが `ps` の出力に表示される)
mcp-stdio https://your-server.example.com:8080/mcp --bearer-token YOUR_TOKENカスタムヘッダー付き:
mcp-stdio https://your-server.example.com:8080/mcp --header "X-API-Key: YOUR_KEY"OAuth 2.1 認証付き(OAuth 必須のサーバー向け):
mcp-stdio --oauth https://your-server.example.com:8080/mcp
# 事前登録済みクライアント ID を使用(動的クライアント登録をスキップ)
mcp-stdio --oauth --client-id YOUR_CLIENT_ID https://your-server.example.com:8080/mcpOAuth 2.1 Device Authorization Grant(RFC 8628)— SSH・ヘッドレス環境向け:
mcp-stdio --oauth-device https://your-server.example.com:8080/mcpMCP 2024-11-05 レガシーの SSE トランスポートを使うサーバー向け:
mcp-stdio --transport sse https://your-server.example.com:8080/sse接続確認:
mcp-stdio --check https://your-server.example.com:8080/mcp
# SSE サーバーの場合は --transport sse を渡すと、--check は Streamable HTTP の
# プローブではなくレガシーの GET/endpoint/POST ハンドシェイクで確認する:
mcp-stdio --check --transport sse https://your-server.example.com:8080/sseclaude_desktop_config.json に追加:
{
"mcpServers": {
"my-remote-server": {
"command": "mcp-stdio",
"args": ["https://your-server.example.com:8080/mcp"],
"env": {
"MCP_BEARER_TOKEN": "YOUR_TOKEN"
}
}
}
}設定ファイルの場所:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
claude mcp add my-remote-server \
-e MCP_BEARER_TOKEN=YOUR_TOKEN \
-- mcp-stdio https://your-server.example.com:8080/mcpmcp-stdio [OPTIONS] URL
引数:
URL リモート MCP サーバーの URL
オプション:
--bearer-token TOKEN Bearer token(MCP_BEARER_TOKEN 環境変数でも指定可)
--oauth OAuth 2.1 認証を有効化(ブラウザフロー)
--oauth-device OAuth 2.1 Device Authorization Grant(RFC 8628)— ヘッドレス環境向け
--client-id ID 事前登録済み OAuth クライアント ID(MCP_OAUTH_CLIENT_ID 環境変数でも指定可)
--client-metadata-url URL
自分でホストする Client ID Metadata Document の HTTPS URL
(draft-ietf-oauth-client-id-metadata-document-00)を
client_id として使用し、Dynamic Client Registration を
省略する。--client-id も指定されている場合は無視される(#60)
--oauth-scope SCOPE 要求する OAuth スコープ
--oauth-use-id-token access_token ではなく OIDC id_token を Bearer として送信
(AWS Bedrock AgentCore / Cognito 向け)。id_token が
無ければ access_token にフォールバック(#59)
--oauth-eager コールドスタート: initialize をローカル即答し対話 OAuth を
バックグラウンド実行。長いブラウザ/SSO/MFA ログインでも
クライアントの約 60 秒 initialize タイムアウトを超えない。
Streamable HTTP 限定、--transport sse では無視。warm
キャッシュは不変(#296)
--oauth-refresh-leeway SECONDS
アクセストークンを expire の何秒前に proactive refresh
するか(デフォルト: 60、または MCP_OAUTH_REFRESH_LEEWAY 環境変数)
--no-proactive-refresh
OAuth トークンを失効前にリフレッシュするバックグラウンド
タイマーを無効化する。OAuth モードではデフォルト有効。失効を
401 ではなく HTTP 200 のツールエラーとして返すゲートウェイ
でも長時間セッションを維持する(#242)
--oauth-timeout SECONDS
対話的 OAuth フロー(ブラウザコールバック / デバイス
コード確認)の待機秒数(デフォルト: 120、OAuth 時のみ有効)
--no-resource-indicator
すべての OAuth リクエストから RFC 8707 resource
パラメータを除外する。api:// スコープを使う Microsoft
Entra ID v2 など、resource パラメータを拒否する AS
(AADSTS9010010)で必要。トークンストアに永続化され、
proactive refresh や step-up フローでも一貫して適用される
--oauth-resource URI server-URL 由来の値の代わりに、この RFC 8707 resource
値をすべての OAuth リクエストで送る。特定の resource
識別子を要求する AS(例: Microsoft Entra ID の App ID
URI api://<app-id>)で必要。トークンストアに永続化。
--no-resource-indicator とは排他
-H, --header 'Key: Value' カスタムヘッダー(複数指定可)
--transport {streamable-http,sse}
トランスポート種別(デフォルト: streamable-http)
--timeout-connect SEC 接続タイムアウト(デフォルト: 10秒)
--timeout-read SEC 読み取りタイムアウト(デフォルト: 120秒)
--sse-read-timeout SEC SSE GET ストリームのアイドル読み取りタイムアウト
(デフォルト: 300秒、0 で無効、SSE トランスポートのみ)
--no-tcp-keepalive HTTP ソケットの TCP keepalive を無効化する
--max-message-size BYTES
パース前にバッファする上流レスポンス本文(JSON
または累積 SSE ストリーム)の上限
(デフォルト: 10 MiB、0 で無効化、#416)。
デフォルトで Accept-Encoding: identity を送信
(#417)。-H 'Accept-Encoding: gzip'/deflate
でオプトインすると本物のサイズ上限付き展開器で
復号する(#418)— それ以外は
--max-message-size 0 が必要。本relay自身の
OAuth トラフィックにも適用される(#419)
--no-cancel-filter cancel-aware レスポンスフィルタを無効化する
(notifications/cancelled でキャンセルされた id の
遅延レスポンスを drop する機能)
--no-normalize-arguments
tools/call リクエストの arguments:null を転送前に
{} へ書き換える正規化を無効化する
--check 接続確認して終了
-V, --version バージョン表示
-h, --help ヘルプ表示
各フラグの詳細(プラットフォーム注記や issue 参照を含む)は mcp-stdio --help を実行してください。この表より詳しい説明が表示されます。
通常モードは stdio → HTTP(クライアント側)の橋渡しですが、serve
サブコマンドはその逆向き — HTTP → stdio — で、ローカルの stdio MCP
サーバを Streamable HTTP の MCP エンドポイントとして公開します。ローカルに
インストールしていないクライアントからネットワーク越しに到達できます:
flowchart BT
A["MCP クライアント<br>Claude Code / Desktop<br>(または mcp-stdio --oauth)"]
B("mcp-stdio serve<br><b>HTTP → stdio</b> ゲートウェイ<br>認証: なし / 静的トークン /<br>埋め込み OAuth 2.1 AS")
C["ローカルの stdio<br>MCP サーバ"]
A <== "Streamable HTTP<br>Bearer / OAuth 2.1 (PKCE)" ==> B
B <-- "stdio (子プロセス起動)" --> C
冒頭のクライアント側の図と対になります。あちらは mcp-stdio が stdio → HTTP、 こちらは HTTP → stdio です。
mcp-stdio serve --port 8080 -- python -m my_mcp_server任意の MCP クライアント(mcp-stdio 自身を含む)から接続:
mcp-stdio --check http://127.0.0.1:8080/mcp- 標準ライブラリのみ(
http.server)— ランタイム依存は増えません。 - Streamable HTTP のリクエスト/レスポンス・通知のセマンティクス、セッション 管理に加え、サーバ起点メッセージ用の GET SSE チャネルを実装。
- 認証は任意・段階的:
- トークン無し — エンドポイントは素通し(TLS 終端プロキシ背後で運用)。
- 静的トークン(
--auth-token/MCP_STDIO_SERVE_TOKEN)— OAuth リソース サーバとしてAuthorization: Bearer <token>を要求、401 で RFC 9728 Protected Resource Metadata(/.well-known/oauth-protected-resource)を広告。 - 埋め込み OAuth AS(
--enable-oauth)— 最小 OAuth 2.1 認可サーバ (PKCE 認可コード・RFC 7591 動的 クライアント登録〔§3.2.2 のinvalid_redirect_uriエラー対応〕・refresh・ 不透明インメモリトークン・stdlib のみ)。https issuer のときは認可レスポンスに RFC 9207issパラメータを付与 (mix-up 対策)し metadata でも広告。mcp-stdio クライアントの--oauthが このゲートウェイ相手に通ります。
- 埋め込み AS のトークンセキュリティ: リソースサーバはトークンの audience を検証
(RFC 8707 / MCP — 別リソース宛の
トークンは拒否)、提示された無効トークンには
error="invalid_token"を返し (RFC 6750 §3.1)、認可コードや ローテーション済み refresh token の再利用検知で grant family 全体を失効 (RFC 6749 §4.1.2 / RFC 9700 §4.14.2、正当なリトライを 巻き込まない短い grace window 付き)。 - セッション単位のマルチクライアント分離 — MCP セッションごとに専用の
バックエンド子プロセスを spawn するため、並行クライアントはプロセス境界で
分離される(クライアント間で JSON-RPC の id が衝突しても応答が混線しない)。
MCP Streamable HTTP 仕様どおり、
initializeでMcp-Session-Idを払い出し、 以降のリクエストはそれを携行、未知/終了済みの id には404(クライアントは 再 initialize)、DELETEでそのセッションの子を破棄。並行セッション数には 上限を設け、素通しゲートウェイでの子プロセス無制限 spawn を防ぐ。OAuth 有効時は 各セッションを認証ユーザに束縛し、別ユーザの token で提示された session id は404で拒否(漏れた id がテナントをまたげない)。
静的トークンの例(トークンは env 経由で ps に出さない):
MCP_STDIO_SERVE_TOKEN=your-secret mcp-stdio serve --port 8080 -- python -m my_mcp_server
mcp-stdio --bearer-token your-secret --check http://127.0.0.1:8080/mcp埋め込み OAuth の例。ユーザ認証は前段リバースプロキシに委譲し、ログイン
済みユーザをヘッダで主張させます(--trusted-user-header、クライアント由来の
同名ヘッダを除去するプロキシ背後でのみ信頼)。--dev-user はローカル検証用の
非セキュアな loopback 限定ショートカットです:
mcp-stdio serve --enable-oauth --public-url http://127.0.0.1:8080 \
--dev-user alice --port 8080 -- python -m my_mcp_server
mcp-stdio --oauth http://127.0.0.1:8080/mcpオプション: --host(既定 127.0.0.1)、--port(既定 8080)、--path
(既定 /mcp)、--auth-token TOKEN(または MCP_STDIO_SERVE_TOKEN、推奨);
セッション上限 --max-sessions N(既定 100、cap 超過の initialize は 503)
と --session-idle-ttl SECONDS(無活動がこの秒数続いたセッションと子プロセスを
破棄。DELETE せず切断したクライアントが slot を占有し続けるのを防ぐ。0=既定
で無効); --max-message-size BYTES(宣言された Content-Length がこれを
超えるリクエストを、本文を読む前に 413 で拒否する。既定 10 MiB、0 で
無効化、#416); --user-env VAR(認証済み principal を、spawn する子プロセスの環境変数
にこの名前で注入する。マルチユーザー対応のバックエンドが、自前の OAuth スタックを
持たずに呼び出し元の identity を読めるようになる — HTTP境界ではなくプロセス
spawn境界に trusted-header パターンを適用したもの。--enable-oauth が前提。
open-gateway と共有 static-token principal は、いずれも本物の呼び出し元識別子
ではないため注入対象から除外される。--max-sessions-per-owner と同じ除外規則。
PATH・LD_PRELOAD・PYTHONPATH など、子プロセス自身のランタイムが起動に
必要とする検索パス/動的リンカ系の変数名は指定を拒否する);
埋め込み AS 用: --enable-oauth、--public-url URL(issuer 固定・
プロキシ背後で推奨)、--trusted-user-header HEADER、--dev-user USER
(非セキュア・検証用)、--access-token-ttl SECONDS、--allow-redirect-uri URL
(繰り返し指定可・後述)、--token-store PATH または
--token-store-firestore COLLECTION/DOCUMENT(互いに排他、後述)。
どちらも指定しない場合はインメモリのみで、再起動で発行済みトークンは失効します
(クライアントは --oauth を再実行)。バックエンド
コマンドはオプションの後に置きます(-- 区切りも可)。
- ループバック以外のリモートクライアント — DCR は既定で RFC 8252 準拠の
ループバック
http://のredirect_uriしか受け付けないため、固定 HTTPS コールバックを持つブラウザベースのリモート MCP クライアント(ローカル実行の CLI/ネイティブアプリではない Web アプリ)はこれを満たせません。--allow-redirect-uri URL(繰り返し指定可)は追加で1つのredirect_uriを 完全一致で信頼します(ホスト・プレフィックス・ポートの緩い一致は一切なし)。 実際に信頼できるクライアントに属すると確認できた URL だけを登録してください。 各エントリはハードコードしたリダイレクト先と同等の信頼度を持ちます。 ループバック経路とは独立しており(片方を追加してももう片方は広がりません)、--enable-oauthが前提です。 - 再起動を生き延びるトークン —
--token-store PATHは発行済みトークン・ rotation の消費台帳・クライアント登録を JSON ファイル(0600で作成、 状態変化のたびに atomic 書き込み)に永続化します。再起動前に有効なトークンを 持っていたクライアントは、新たな対話認可なしにツール呼び出しを継続でき、 再起動後に提示された refresh も受理されます。401/invalid_grantで 再認可しないリモートクライアント(失効トークンを無限に再送し、接続済み表示の ままツールだけが静かに消えるタイプ)にとって、デプロイが透過になります。 refresh token 再利用検知と grant family 失効も再起動を越えて機能します (消費台帳ごと永続化されるため)。ファイルは資格情報そのものなので秘密鍵と 同様に保護し、serveプロセスごとに別のパスを与えてください——sidecar の.lockファイルが起動時に 2 つ目のプロセスを拒否します(1 つの store の 共有は発行済みトークンを静かに上書きし合うため)。またパスは起動時に probe 書き込みされるので、書き込めないパスの指定は永続化が静かに無効化 される代わりに起動エラーになります。--enable-oauthが前提です(#277)。 - 再起動を生き延びるトークン(ローカルディスクなし) —
--token-store-firestore COLLECTION/DOCUMENTは--token-storeと 同じ状態(同じJSON形状のスナップショット、同じ起動時probe書き込みによる fail-fast挙動)を、ローカルファイルの代わりに Firestore の1ドキュメントへ永続化 します。Cloud Run 等、永続的なローカルディスクを持たないデプロイ向けです。 GCP プロジェクトは google-cloud の標準的な方法(GOOGLE_CLOUD_PROJECT環境変数、または Cloud Run 上の ADC)で解決され、別途--projectフラグは ありません。google-cloud-firestoreパッケージ(pip install mcp-stdio[firestore]、任意の extra——単純なpip install mcp-stdioでは 不要)と--enable-oauthが前提です。--token-storeと異なり、2つの プロセスが1つのドキュメントを共有することを防ぐロックはありませんが、 各書き込みは blind overwrite ではなく read-merge-write トランザクションを 経由するため(#406)、2つの書き込み元が短時間重なる状況(Cloud Run の リビジョン切り替え等)でも、どちらか一方がその重なりの間に発行・ ローテーションしたトークンが静かに失われることはありません。ただし 1点だけ残存する制約があります:その重なりの間にリプレイ検知による失効や 容量超過による退避が起きたトークンは、tombstone(失効マーカー)を 持たないため、遅れて書き込む側によって復活しうる可能性が残ります (#428)。--token-storeとは互いに排他です。 - パススコープ issuer —
--public-urlがパスを保持するので、1 ホスト配下で 複数の--enable-oauthバックエンドをパスプレフィックスで多重化できる (例:--public-url https://gw.example.org/team-aでhttps://gw.example.org/team-a/mcpを配信)。issuer はhttps://gw.example.org/team-aとなり、AS エンドポイントはプレフィックス配下 (/team-a/authorize・/token・/register)、well-known ドキュメントは RFC 8414 §3.1 / RFC 9728 §3.1 の root-inserted 位置(/.well-known/oauth-authorization-server/team-a、/.well-known/oauth-protected-resource/team-a/mcp)に置かれ、クライアントの パス対応ディスカバリとバイト単位で対称。パスなしの--public-urlは従来通り 動作します(#245)。
serve は複数ユーザの同時利用を前提に設計されています。MCP セッションごとに
専用のバックエンド子プロセスを spawn し、OAuth 有効時は各セッションを認証ユーザに
束縛するので、ユーザはプロセス境界で分離され、漏れた session id がテナントを
またげません。
エンドユーザのログインは前段リバースプロキシに委譲します。プロキシが実際の SSO を
行い、--trusted-user-header でユーザを主張します(クライアント由来の同名ヘッダを
プロキシが除去するからこそ信頼できる)。埋め込み AS がユーザごとのトークンを発行し、
ゲートウェイが各セッションをそのユーザに束縛します。
flowchart TD
UA["User A<br>mcp-stdio --oauth"]
UB["User B<br>mcp-stdio --oauth"]
RP["リバースプロキシ<br>SSO ログイン, X-Forwarded-User 付与<br>クライアント由来コピーは除去"]
GW["mcp-stdio serve --enable-oauth<br>--trusted-user-header X-Forwarded-User"]
CA["stdio 子プロセス<br>A のセッション"]
CB["stdio 子プロセス<br>B のセッション"]
UA == "Streamable HTTP<br>OAuth 2.1 (PKCE)" ==> RP
UB == "Streamable HTTP<br>OAuth 2.1 (PKCE)" ==> RP
RP ==> GW
GW -- "セッションごとに spawn" --> CA
GW -- "セッションごとに spawn" --> CB
ゲートウェイ(プロキシ背後の loopback にバインド):
mcp-stdio serve --enable-oauth \
--public-url https://mcp.example.org \
--trusted-user-header X-Forwarded-User \
--max-sessions 200 --session-idle-ttl 900 \
--host 127.0.0.1 --port 8080 -- python -m my_mcp_server--public-urlは issuer をプロキシが配信する外部 HTTPS URL に固定。--trusted-user-headerはプロキシがログイン後に付与するヘッダ。プロキシが クライアント由来コピーを除去するからこそ信頼する。--max-sessionsはユーザごとの子プロセス数の上限、--session-idle-ttlは ユーザがDELETEせず切断した子プロセスを回収。
各ユーザはクライアントをゲートウェイに向け、一度 OAuth フローを通せば専用の 子プロセスで処理されます:
mcp-stdio --oauth https://mcp.example.org/mcp注意:
- 分離はプロセス境界による — ユーザ A と B は子プロセスを共有しないので、 接続ごとのバックエンド状態(や JSON-RPC id 衝突)が相互に漏れない。
- バックエンドコマンドはテンプレートです。セッションごとに同じコマンドを 新しい子として spawn します。identity はゲートウェイ側で強制(セッション→ユーザ 束縛)され、子プロセスには注入されません。ユーザ固有のコンテキストが必要な バックエンドはリクエストから導出するか、バックエンド構成ごとに 1 ゲートウェイを 立てて(必要ならパスで多重化、上記パススコープ issuer)運用してください。
Claude Code・Claude Desktop・mcp-remote・MCP SDK・Windows の既知の問題については WORKAROUNDS.md を参照してください。
--oauth(ブラウザ)または--oauth-device(ヘッドレス、RFC 8628)指定時、アクセストークンを取得(キャッシュ → リフレッシュ → ブラウザ/デバイス認証)- stdin から JSON-RPC メッセージを読み取り(Claude Desktop/Code が送信)
- HTTPS でリモート MCP サーバーへ転送
- レスポンスをパースして stdout に書き出し
- 401 で(OAuth モードのみ)アクセストークンをリフレッシュしてリトライ。静的な
--bearer-token/-H認証では 401 をそのままクライアントに返す - OAuth モードではバックグラウンドタイマーも失効直前(
--oauth-refresh-leeway)にトークンをリフレッシュする。リクエストの流れとは独立して動作し、トークン失効を 401 ではなく HTTP 200 のツールエラーとして返すゲートウェイでも長時間セッションを維持する(--no-proactive-refreshで無効化)
トランスポート別の挙動:
- Streamable HTTP(デフォルト)— 各メッセージを単一 POST で送信。
Mcp-Session-Idヘッダーでセッション状態を追跡し、404 時は自動で再初期化。交渉済みのMCP-Protocol-Versionヘッダーを初期化後の全リクエストに付与(仕様 rev 2025-06-18)。 - SSE(MCP 2024-11-05 レガシー)— 持続的な
GETストリームで応答と初回のendpointイベント(POST 先 URL)を受信。ストリーム切断時は自動再接続。
OAuth トークンは ~/.config/mcp-stdio/tokens.json に保存されます(パーミッション 0600)。
MIT