メインコンテンツへスキップ
見出し画像

MCP の仕様 (5) - 基本プロトコル - トランスポート

    以下の記事が面白かったので、簡単にまとめました。

    ・Transports - Model Context Protocol

    前回

    1. トランスポート

    「MCP」はJSON-RPCを使用してメッセージをエンコードします。JSON-RPCメッセージはUTF-8でエンコードする必要があります。
    このプロトコルは現在、クライアント・サーバ間通信用の2つの標準トランスポートメカニズムを定義しています。

    ・stdio
    ・
    Streamable HTTP

    クライアントは可能な限りstdioをサポートする必要があります。
    クライアントとサーバは、プラグ可能な形式でカスタムトランスポートを実装することも可能です。

    2. stdio

    stdio トランスポートでは、次の処理が行われます。

    ・クライアントは MCP サーバをサブプロセスとして起動します。
    ・サーバは標準入力 (stdin) から JSON-RPC メッセージを読み取り、標準出力 (stdout) に送信します。
    ・メッセージは、個々の JSON-RPC リクエスト、通知、またはレスポンスです。
    ・メッセージは改行で区切られ、改行文字を含んではなりません (MUST)。
    ・サーバは、ログ出力のために UTF-8 文字列を標準エラー (stderr) に書き込むことができます (MAY)。クライアントは、このログ出力をキャプチャ、転送、または無視することができます (MAY)。
    ・サーバは、有効な MCP メッセージではないものを stdout に書き込んではなりません (MAY)。
    ・クライアントは、有効な MCP メッセージではないものをサーバの stdin に書き込んではなりません (MAY)。

    画像

    3. Streamable HTTP

    3.1 Streamable HTTP

    これは、プロトコルバージョン2024-11-05のHTTP+SSEトランスポートに代わるものです。

    Streamable HTTPトランスポートでは、サーバは複数のクライアント接続を処理できる独立したプロセスとして動作します。このトランスポートは、HTTP POSTおよびGETリクエストを使用します。サーバはオプションでSSE (Server-Sent Events) を使用して複数のサーバメッセージをストリーミングできます。これにより、基本的なMCPサーバだけでなく、ストリーミングやサーバからクライアントへの通知とリクエストをサポートする機能豊富なサーバも実現できます。
    サーバは、POSTメソッドとGETメソッドの両方をサポートする単一のHTTPエンドポイントパス (以下、MCPエンドポイント) を提供する必要があります。例えば、https://example.com/mcp のようなURLがこれに該当します。

    3.2 セキュリティ警告

    Streamable HTTPトランスポートを実装する場合

    (1) DNSリバインディング攻撃を防ぐため、サーバはすべての受信接続においてOriginヘッダーを検証する必要があります。
    (2) ローカルで実行する場合、サーバはすべてのネットワークインターフェース (0.0.0.0) ではなく、ローカルホスト (127.0.0.1) のみにバインドする必要があります。
    (3) サーバはすべての接続に対して適切な認証を実装する必要があります。

    これらの保護がなければ、攻撃者はDNSリバインディングを利用してリモートウェブサイトからローカルMCPサーバにアクセスできる可能性があります。

    3.3 サーバへのメッセージの送信

    クライアントから送信されるすべての JSON-RPC メッセージは、MCP エンドポイントへの新しい HTTP POST リクエストである必要があります。

    (1) クライアントは、MCPエンドポイントにJSON-RPCメッセージを送信するためにHTTP POSTを使用する必要があります。

    (2) クライアントは、Acceptヘッダーを含め、サポートされるコンテンツタイプとしてapplication/jsonとtext/event-streamの両方を指定する必要があります。

    (3) POSTリクエストのボディは、単一のJSON-RPCリクエスト、通知、またはレスポンスである必要があります。

    (4) 入力がJSON-RPCレスポンスまたは通知の場合
    ・サーバが入力を受け入れる場合、サーバはボディなしのHTTPステータスコード202 Acceptedを返す必要があります。
    ・サーバが入力を受け入れることができない場合、HTTPエラーステータスコード(例:400 Bad Request)を返す必要があります。HTTPレスポンスボディは、IDを持たないJSON-RPCエラーレスポンスで構成されてもよいです。

    (5) 入力がJSON-RPCリクエストの場合、サーバはSSEストリームを開始するContent-Type: text/event-stream、または単一のJSONオブジェクトを返すContent-Type: application/jsonのいずれかを返さなければなりません(MUST)。クライアントはこれら両方のケースをサポートしなければなりません(MUST)。

    (6) サーバがSSEストリームを開始する場合
    ・SSE ストリームには、POST ボディで送信された JSON-RPC リクエストに対する JSON-RPC レスポンスが最終的に含まれるべきです (SHOULD)。
    ・サーバは、JSON-RPC レスポンスを送信する前に、JSON-RPC リクエストと通知を送信してもよい (MAY)。これらのメッセージは、送信元のクライアント リクエストに関連するものであるべきです (SHOULD)。
    ・サーバは、セッションの有効期限が切れない限り、受信した JSON-RPC リクエストに対する JSON-RPC レスポンスを送信する前に、SSE ストリームを閉じてはなりません (SHOULD NOT)。
    ・JSON-RPC レスポンスを送信した後、サーバは SSE ストリームを閉じるべきです (SHOULD)。
    ・切断はいつでも発生する可能性があります (例: ネットワーク状況による)。したがって、以下のようになります。
     ・切断は、クライアントがリクエストをキャンセルしたと解釈されるべきではありません。
     ・キャンセルするには、クライアントは明示的にMCPのCancelledNotificationを送信する必要があります。
     ・切断によるメッセージの損失を避けるため、サーバはストリームを再開可能にすることができます。

    3-4. サーバからのメッセージのリッスン

    (1) クライアントは、MCPエンドポイントに対してHTTP GETを発行できます。これによりSSEストリームが開かれ、クライアントがHTTP POSTでデータを送信することなく、サーバがクライアントと通信できるようになります。

    (2) クライアントは、サポートされるコンテンツタイプとしてtext/event-streamを指定したAcceptヘッダーを含める必要があります。
    サーバは、このHTTP GETへの応答としてContent-Type: text/event-streamを返すか、そうでない場合はHTTP 405 Method Not Allowed (サーバがこのエンドポイントでSSEストリームを提供していないことを示す) を返す必要があります。

    (3) サーバがSSEストリームを開始した場合
    ・サーバは、ストリーム上でJSON-RPCリクエストと通知を送信できます。
    ・これらのメッセージは、クライアントから同時に実行されているJSON-RPCリクエストとは無関係である必要があります。
    ・サーバは、以前のクライアントリクエストに関連付けられたストリームを再開する場合を除き、ストリーム上でJSON-RPCレスポンスを送信してはなりません。
    ・サーバはいつでもSSEストリームを終了できます。
    クライアントはいつでもSSEストリームを終了できます。

    3-5. 複数接続

    (1) クライアントは複数のSSEストリームに同時に接続したままでいる場合があります。

    (2) サーバは、接続されたストリームのうち1つにのみJSON-RPCメッセージを送信しなければなりません。つまり、複数のストリームに同じメッセージをブロードキャストしてはなりません。
    ・ストリームを再開可能にすることで、メッセージ損失のリスクを軽減できます。

    3-6. 再開と再配信

    切断された接続の再開と、失われる可能性のあるメッセージの再配信をサポートするために、以下の点に留意してください。

    (1) サーバは、SSE標準で規定されているように、SSEイベントにIDフィールドを付加できます。
    ・IDフィールドが存在する場合、そのIDは、そのセッション内のすべてのストリーム (セッション管理が使用されていない場合は、その特定のクライアントのすべてのストリーム) にわたってグローバルに一意である必要があります。

    (2) クライアントが切断された接続後に再開する場合、MCPエンドポイントにHTTP GETを発行し、受信した最後のイベントIDを示すLast-Event-IDヘッダーを含める必要があります。
    ・サーバは、このヘッダーを使用して、切断されたストリームで最後のイベントIDの後に送信されるはずだったメッセージを再生し、その時点からストリームを再開できます。
    ・サーバは、別のストリームで配信されるはずだったメッセージを再生してはなりません。

    言い換えれば、これらのイベントIDは、特定のストリーム内でカーソルとして機能するように、サーバによってストリームごとに割り当てられるべきです。

    3-7. セッション管理

    「MCP」 の「セッション」は、初期化フェーズから始まる、クライアントとサーバ間の論理的に関連するやり取りで構成されます。ステートフルセッションを確立するサーバをサポートするには、以下の要件を満たす必要があります。

    (1) Streamable HTTP トランスポートを使用するサーバは、初期化時にセッション ID を割り当てることができます。その際、InitializeResult を含む HTTP レスポンスの Mcp-Session-Id ヘッダーにセッション ID を含めます。
    ・セッション ID は、グローバルに一意で、暗号的に安全である必要があります (例:安全に生成された UUID、JWT、または暗号ハッシュ)。
    ・セッション ID には、表示可能な ASCII 文字(0x21 から 0x7E の範囲)のみを含める必要があります。

    (2) 初期化中にサーバから Mcp-Session-Id が返された場合、Streamable HTTP トランスポートを使用するクライアントは、後続のすべての HTTP リクエストの Mcp-Session-Id ヘッダーにその ID を含める必要があります(MUST)。

    (3) セッション ID を必要とするサーバは、Mcp-Session-Id ヘッダーのないリクエスト(初期化以外)に対して、HTTP 400 Bad Request で応答する必要があります(SHOULD)。
    ・サーバはいつでもセッションを終了できます(MAY)。終了した場合は、そのセッション ID を含むリクエストに対して HTTP 404 Not Found で応答する必要があります(MUST)。

    (4) クライアントが Mcp-Session-Id を含むリクエストへの応答として HTTP 404 を受信した場合、セッション ID を添付せずに新しい InitializeRequest を送信して、新しいセッションを開始する必要があります(MUST)。

    (5) 特定のセッションが不要になったクライアント(ユーザーがクライアントアプリケーションを離れるなど)は、MCP エンドポイントに Mcp-Session-Id ヘッダーを付けて HTTP DELETE を送信し、明示的にセッションを終了する必要があります(SHOULD)。
    ・サーバはこのリクエストに対して HTTP 405 Method Not Allowed で応答して、クライアントによるセッションの終了を許可しないことを示すことができます(MAY)。

    3-8. シーケンス図

    画像

    3-9. プロトコルバージョンヘッダー

    HTTPを使用する場合、クライアントはMCPサーバへのすべてのリクエストにMCP-Protocol-Version: <protocol-version> HTTPヘッダーを含める必要があります。これにより、MCPサーバはMCPプロトコルバージョンに基づいて応答できます。

    ・例:MCP-Protocol-Version: 2025-06-18

    クライアントが送信するプロトコルバージョンは、初期化時にネゴシエートされたバージョンである必要があります。

    後方互換性のため、サーバがMCP-Protocol-Versionヘッダーを受信せず、バージョンを識別する他の方法(例えば、初期化時にネゴシエートされたプロトコルバージョンに依存するなど)がない場合、サーバはプロトコルバージョン2025-03-26であると想定する必要があります。

    サーバが無効またはサポートされていないMCP-Protocol-Versionを含むリクエストを受信した場合、400 Bad Requestで応答する必要があります。

    3-10. 後方互換性

    クライアントとサーバは、以下のように、非推奨の HTTP+SSE トランスポート (プロトコルバージョン 2024-11-05 以降) との後方互換性を維持できます。

    古いクライアントをサポートしたいサーバは、以下の点に留意してください。

    ・古いトランスポートのSSEエンドポイントとPOSTエンドポイントの両方を、ストリーミング可能なHTTPトランスポート用に定義された新しい「MCPエンドポイント」と並行してホストし続けます。
     ・古いPOSTエンドポイントと新しいMCPエンドポイントを組み合わせることも可能ですが、不要な複雑さが生じる可能性があります。

    古いサーバをサポートしたいクライアントは、以下の手順を実行する必要があります。

    (1) ユーザーからMCPサーバURL (古いトランスポートを使用するサーバまたは新しいトランスポートを使用するサーバのいずれかを指す) を受け取ります。
    (2) 上記で定義したAcceptヘッダーを付けて、サーバURLにInitializeRequestをPOST送信します。
    ・成功した場合、クライアントはこれが新しいストリーミング可能な HTTP トランスポートをサポートするサーバであると想定できます。
    ・HTTP 4xx ステータスコード(例:405 メソッドが許可されていません、または 404 見つかりません)で失敗した場合:
     ・サーバ URL に GET リクエストを発行し、SSE ストリームを開いて最初のイベントとしてエンドポイントイベントを返すことを期待します。
     ・エンドポイントイベントが到着すると、クライアントはこれが古い HTTP+SSE トランスポートを実行しているサーバであると想定し、以降のすべての通信でそのトランスポートを使用する必要があります。

    4. カスタムトランスポート

    クライアントとサーバは、特定のニーズに合わせて追加のカスタムトランスポートメカニズムを実装できます。このプロトコルはトランスポートに依存せず、双方向メッセージ交換をサポートする任意の通信チャネルで実装できます。

    カスタムトランスポートをサポートすることを選択した実装者は、「MCP」で定義されたJSON-RPCメッセージ形式とライフサイクル要件に準拠していることを確認する必要があります。カスタムトランスポートは、相互運用性を高めるために、固有の接続確立とメッセージ交換パターンを文書化する必要があります。

    次回



     
     

    npaka

     
     
    プログラマー。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガジェット。年2冊ペースで技術書を執筆。アニソン / カラオケ / ギター / 猫 twitter : @npaka123

    あなたへのおすすめ