Zenn
🦩

MCPの通信方法がステートフルからステートレスに?:HTTP+SSEからStreamable HTTPへ

2025/03/19に公開
9

こんにちは、しば田です!

記事を書くに至ったきっかけの投稿がこちら。v0やTurborepoで有名なJared Palmer氏です。

https://x.com/jaredpalmer/status/1901633502078226565

和訳すると、

”Anthropicによる正解ムーブ。MCPは完全にステートレスで、再開可能、そして単なるプレーンなHTTPで実装できるようになる。”

上記の発言は、MCPのGithubのレポジトリに出ているこのPRの変更について言及しています。
https://github.com/modelcontextprotocol/specification/pull/206

内容がパッとは分からなかったので整理しました。

MCPとは?

この記事では解説はしません。
他人様の記事ですが、こちらの記事がわかりやすかったです。

https://zenn.dev/yamada_quantum/articles/465c4993465053#mcpクライアントとしてcursorを利用する

現在の通信方式(ステートフル):HTTP+SSEトランスポート

MCPの現在の通信方式であるHTTP+SSEトランスポートは、クライアントとサーバー間の双方向通信を実現するために2つの異なる通信を使用している。

クライアント→サーバ:HTTP
サーバ→クライアント:SSE

SSE(サーバ送信イベント)は、簡単に言うと、サーバーからクライアントへ一方向の Push通信を行うための仕組みです。

下記の記事で知ったのですが、ChatGPTなどで1文字ずつ表示される挙動の裏側にもこの技術が使われているようです。
https://zenn.dev/cybozu_frontend/articles/try-server-sent-events

HTTP+SSEトランスポートの通信イメージ

この通信方式では、サーバーからクライアントへのメッセージ送信にSSE(Server-Sent Events)を活用している。SSEは一方向の長期接続を確立し、サーバーからクライアントへのリアルタイム通知に適している。

  1. 接続開始フェーズ:

    • クライアントは/sseエンドポイントにGETリクエストを送信
      • →SSE接続が確立される(以降、サーバーは接続を維持)
    • サーバは、てメッセージ送信用エンドポイントのURLをクライアントに通知
  2. メッセージ交換フェーズ:

    • クライアント→サーバー: 通常のHTTP POSTリクエスト
      • 1で教えてもらったメッセージ送信用エンドポイントのURLを使う
    • サーバー→クライアント: 確立されたSSE接続を使い続けてJSONメッセージを送信する

なぜステートフル(状態を保持する)方式なのか?

  1. 会話の文脈維持

    • AIモデルとの対話では前後の文脈を覚えておく必要がある。
    • 各リクエストを独立したものとして扱うと、会話の流れが失われる。
  2. リアルタイム通知の必要性

    • AIが処理中に途中経過を通知する必要がある場合など、サーバーからクライアントへ常に開かれた通信路(SSE)が必要だった。
  3. 技術的な単純さ

    • ステートフルな接続を維持する方が、実装が単純でわかりやすい
    • 各リクエストごとに状態を復元するには複雑な仕組みが必要

出てきた課題

以下のような課題が出てきている:

  1. 高負荷でスケーリングに弱い

    • サーバーは各クライアントとのSSE接続を常に維持する必要がある
      • 1,000人(エージェント)が接続したら、1,000SSE接続を常時確立しないといけない
        • 各接続はメモリとシステムリソースを消費し、長時間開いたままになる。
        • 接続を分散させるのは大変
      • 接続数が増えていけばいくほどパフォーマンスが低下する
  2. ネットワークの中断に弱い:

    • ネットワーク中断があるとSSE接続が切れる
    • 接続が切れた場合、再接続しても以前の状態(これまでの会話)は失われる
      • さらに再起動時にすべてのクライアントが一斉に再接続する。

このような課題に対応するため、新しい 「Streamable HTTP」トランスポート が提案されている。

今後の通信方式(ステートレス):Streamable HTTP

Streamable HTTPでは、
現行のHTTP+SSEトランスポートの課題を解消できる。

Streamable HTTPとは要は、
基本HTTP通信 + 必要な時だけSSE(Streamable) という通信方式のこと。

主な特徴としては:

  • 基本はHTTP通信ベース
  • セッションIDによる状態管理
  • 必要な時にSSEへ動的アップグレード

基本はHTTP通信ベース

Streamable HTTPでは、クライアントとサーバー間の通信が基本的に/message(または類似名)の単一エンドポイントを通じて行われる。これにより:

  1. SSEを使わないので通信構造がシンプルになる
    • 実装や管理が容易になる
  2. 「ただの HTTP」なので既存のミドルウェアやインフラとの連携も容易になる。

セッションIDによる状態管理

Streamable HTTPの重要な特徴は、物理的な接続維持ではなく、論理的なセッションIDを用いて対話を継続する点にある。

これにより:

  1. サーバーは常時接続を維持する必要がなくなる
  2. 仮にネットワークが中断されたとしてもセッションIDを使って対話内容を復元できる
  3. 水平スケーリング環境で適切なサーバーへリクエストを振り分けられる
    • 同じセッションIDのリクエストを同じサーバーに送ることで処理効率が向上

SSEへの動的アップグレード

Streamableという名前の通り、必要に応じてストリーミング通信(SSE)にシームレスに切り替えることが可能

Streamable HTTPでは、サーバーが必要に応じて通常のHTTPレスポンスをSSEストリームに動的にアップグレードできる。

これにより、単純な応答はHTTP、複雑な応答はSSEと動的に切り替えることができる。

例えば、以下のようなケースで活用できる:

WebSocketが採用されなかった理由

  1. 呼び出しごとに WebSocket 接続が必要になると、不要な運用コストやネットワーク負荷が大きくなる
  2. ブラウザ環境では Authorization などのヘッダーを WebSocket に付与できず、SSE と違ってサードパーティライブラリで WebSocket を再実装する手段がない
  3. WebSocket へのアップグレードは GET リクエストのみ自動対応で、POST など他のHTTP メソッドは対応外。そのため POSTエンドポイントで二段階アップグレードが必要となり、実装が複雑化し遅延が増える
  4. サポートするトランスポート数を増やすとクライアント⇄サーバー間の互換性問題が複雑化するのでなるべく増やしたくない。

しかし、将来的に SSE の運用で課題が出た場合は、WebSocketの再検討は排除しているわけではないらしい。

最後に

AI x 開発について発信してます、気が向いたらフォローお願いします!
https://x.com/KeisukeShibata_

9

Discussion

ログインするとコメントできます