+ 作るログイン
図を読み込んでいます…
1 / 7
MCP入門 ― AIに道具を持たせる共通の作法 / 全6章

つなぎ方 ― stdio と Streamable HTTP

発行
アニメモ公式
版
第1版
改訂
コマ
全7

MCPの運び方は2つだけ。手元で子プロセスとして動かすか、HTTPで遠くに置くか。stdio で最も多い事故(stdout を汚して接続が壊れる)と、どちらを選ぶべきかの目安まで。

運び方は2つ。中身はどちらも同じ

MCPが正式に決めているトランスポートは2つだけです。

- stdio — クライアントがサーバーを子プロセスとして起動し、標準入出力でやりとりする
- Streamable HTTP — 1つのエンドポイントに HTTP POST で送り、返事は JSON か SSE のストリームで受ける

大事なのは、どちらを選んでもデータ層は変わらないことです。tools/call の形も、_meta の中身も、まったく同じ。運び方だけを入れ替えられます。

この2つ以外に、独自のトランスポートを作ることも許されています。ただし JSON-RPC のメッセージ形式と、毎リクエストにメタ情報を載せる決まりは守らなければなりません。トランスポート仕様

  • 04 / MCP GUIDE
  • 1 / 7
  • 運び方は2つ。中身はどちらも同じ
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • データ層は共通。差し替えられるのは運び方だけ。

stdio:子プロセスとして起動して、パイプで話す

手元で動かすサーバーは、ほぼこれです。クライアントがコマンドを実行してサーバーを起動し、そのプロセスの標準入出力を掴みます。

- クライアントは、サーバーの stdin にリクエストと通知を書く
- サーバーは、stdout に応答と通知を書く
- メッセージは改行で区切る。1行1メッセージ
- したがってメッセージの中に改行を含めてはならない

ネットワークを通らないので速く、認証も要りません。資格情報は環境変数など、ローカルの手段で渡します。

終わらせ方も決まっています。クライアントは入力ストリームを閉じ、サーバーが終わるのを待ちます。サーバー側は標準入力が閉じたら速やかに終了するのが行儀です。終わらなければ強制終了されます。

  • 04 / MCP GUIDE
  • 2 / 7
  • stdio:子プロセスとして起動して、パイプで話す
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • stdin
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • stdout
  • 1行1メッセージ。メッセージの中に改行を入れてはいけない。

stdout に、MCPメッセージ以外を書いてはいけない

ここが stdio でいちばん多い事故です。仕様にはこう書かれています。

サーバーは、有効なMCPメッセージ以外のものを `stdout` に書いてはならない(MUST NOT)。

つまり console.log("ここまで来た") や print(result) を1行入れただけで、その行がJSON-RPCとして解釈され、パースに失敗し、接続が壊れます。処理は正しいのに動かないという、いちばん時間を溶かす壊れ方です。

ログを出したいときは stderr を使います。こちらは自由に書いてよく、クライアントは受け取っても無視しても構いません。`stderr` に出力があること自体はエラーの印ではない、とも明記されています。

NG: console.log("debug")      → stdout が汚れて接続が壊れる
OK: console.error("debug")    → stderr なので安全

なお、2026-07-28 版ではプロトコル経由のログ送信(logging)は非推奨になりました。新しく作るなら stderr か OpenTelemetry を使います。

  • 04 / MCP GUIDE
  • 3 / 7
  • stdout に、MCPメッセージ以外を書いてはいけない
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • stdin
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • stdout
  • stdout に書くと壊れる
  • ログは stderr へ
  • console.log を1行入れただけで接続が壊れる。ログは stderr へ。

Streamable HTTP:1つの口に POST して、流れて返る

遠くのサーバーはこちらです。クライアントは単一のエンドポイントに HTTP POST でメッセージを送ります。返事は2通りあります。

- 1つのJSONオブジェクトとして即返る
- そのリクエスト専用の SSE(Server-Sent Events) ストリームとして流れてくる

長い処理の途中経過を送りたいときに、後者が効きます。

HTTPなので、認証は普通のHTTPのやり方が使えます。Authorization: Bearer … を付けるのが基本形で、仕様は OAuth でトークンを取ることを推奨しています。詳しくは第5章で扱います。

このトランスポートでは、_meta の内容が HTTPヘッダにも写されます。間に立つプロキシなどが、本文をパースしなくても振り分けられるようにするためです。ただし本文が正で、食い違ったときにどう拒否するかはトランスポート側が決めます。

途中でやめたいときは、stdio では notifications/cancelled を送り、HTTPではそのリクエストのストリームを閉じます。

  • 04 / MCP GUIDE
  • 4 / 7
  • Streamable HTTP:1つの口に POST して、流れて返る
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • stdin
  • POST
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • stdout
  • SSE / JSON
  • stdout に書くと壊れる
  • Authorization: Bearer …
  • ログは stderr へ
  • 返事は JSON かSSE。認証はヘッダに載せる。

どちらを選ぶか

stdioStreamable HTTP
どこで動く利用者のパソコンサーバー・クラウド
相手にする数ふつう1クライアント多数のクライアント
資格情報環境変数など手元の手段OAuth のトークン
配り方インストールしてもらうURLを教えるだけ
得意なこと手元のファイル・開発環境社内共有・SaaS連携

迷ったときの目安です。

触る対象が利用者の手元にあるなら stdio。 ファイル、Git リポジトリ、ローカルのデータベース。ネットワーク越しにやる意味がありません。

皆で同じものを見るなら Streamable HTTP。 社内検索、課題管理、顧客データ。1か所で権限を管理できます。

両方を出しているサービスもあります。同じデータ層なので、実装を共有して口だけ2つ用意できるからです。

  • 04 / MCP GUIDE
  • 5 / 7
  • どちらを選ぶか
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • stdin
  • POST
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • stdout
  • SSE / JSON
  • 手元のファイル・Git・開発環境
  • 社内検索・課題管理・SaaS連携
  • stdout に書くと壊れる
  • Authorization: Bearer …
  • ログは stderr へ
  • 触る対象が手元なら stdio。皆で見るなら HTTP。

版は毎回のリクエストが運ぶ

状態を持たないので、「この接続は何版か」という覚え方はしません。リクエストごとに `_meta` が版を運びます。

サーバーが対応していない版で来たら、対応している版を並べたエラーを返し、クライアントが選び直します。

古いサーバーと繋ぐときは、まず server/discover を投げるのが推奨です。

- DiscoverResult が返る → 新しい世代
- 版が合わないエラーが返る → 新しい世代。示された版で話し直す
- それ以外のエラー、または無応答 → 旧世代。initialize の握手へ切り替える

このフォールバックを特定のエラーコードに結びつけてはいけません。 旧世代のサーバーが何を返すかは実装任せです。

プロセスが落ちたときも同じ考え方です。状態を持たないので、クライアントは起動し直して、失われたリクエストを投げ直すだけで済みます。ただし subscriptions/listen で開いていた通知の購読は張り直す必要があります。

  • 04 / MCP GUIDE
  • 6 / 7
  • 版は、毎回のリクエストが運ぶ
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • stdin
  • POST
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • stdout
  • SSE / JSON
  • 手元のファイル・Git・開発環境
  • 社内検索・課題管理・SaaS連携
  • stdout に書くと壊れる
  • Authorization: Bearer …
  • ログは stderr へ
  • どちらの運び方でも、版と能力は _meta が毎回運ぶ
  • 接続が版を覚えない。だから落ちても投げ直すだけ。

確認:print デバッグを入れたら、繋がらなくなった

原因は `stdout` を汚したことです。stdio では標準出力がそのまま通信路なので、MCPメッセージ以外の1行が混ざった時点で壊れます。

直し方は単純です。console.log を console.error に、print() を print(file=sys.stderr) に変えます。これだけで直ります。

この章のまとめです。

- 運び方は stdio と Streamable HTTP の2つ
- 中身(データ層)はどちらでも同じ
- stdio では stdout に余計なものを書かない。ログは stderr
- HTTP では認証をヘッダに載せる。取り方は OAuth が推奨
- 版は接続ではなく毎回のリクエストが運ぶ

次の章では、その HTTP の側で必要になる認可を見ます。

  • 04 / MCP GUIDE
  • 7 / 7
  • 確認:print デバッグを入れたら、繋がらなくなった
  • stdio(手元・子プロセス)
  • Streamable HTTP(遠く)
  • stdin
  • POST
  • クライアント
  • サーバー
  • クライアント
  • サーバー
  • stdout
  • SSE / JSON
  • 手元のファイル・Git・開発環境
  • 社内検索・課題管理・SaaS連携
  • stdout に書くと壊れる
  • Authorization: Bearer …
  • ログは stderr へ
  • 直し方は console.log → console.error に変えるだけ
  • 標準出力がそのまま通信路。1行の混入で壊れる。

0 件のコメント

読み込み中…

権利侵害・不適切な内容の連絡: [email protected]