Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save shuntagami/7591ae346f00faa994711e3532107ba6 to your computer and use it in GitHub Desktop.

Select an option

Save shuntagami/7591ae346f00faa994711e3532107ba6 to your computer and use it in GitHub Desktop.
Model Context Protocol (MCP)の実装と通信仕組み

Model Context Protocol (MCP)の実装と通信仕組み

1. JSON-RPCとは

JSON-RPC は、Remote Procedure Call (RPC) の一種で、JSON形式でメッセージをやりとりする通信プロトコルです。

簡単に言うと:

  • クライアントが「この関数呼んで!」とリクエストを出し、サーバーがその処理をして結果を返す仕組み
  • JSONを使ってやり取りするため、可読性が高く、多くの言語で扱いやすい

通信イメージ:

// クライアントからのリクエスト
{
  "jsonrpc": "2.0",
  "method": "add",
  "params": [3, 5],
  "id": 1
}

// サーバーからのレスポンス
{
  "jsonrpc": "2.0",
  "result": 8,
  "id": 1
}

特徴:

  • 軽量でシンプルなRPCプロトコル
  • JSON形式(読みやすく、言語非依存)
  • リクエスト/レスポンスパターンと通知(一方向)メッセージの両方をサポート
  • HTTPに限らず、WebSocketや任意の双方向通信でも使える

2. JSON-RPCとgRPCの違い

項目 JSON-RPC gRPC
プロトコル JSON over HTTP or any transport HTTP/2
データ形式 JSON(テキスト) Protocol Buffers(バイナリ)
スキーマ定義 なし(コードベースで定義) .proto ファイルで明示的に定義
通信の双方向性 一方向(通知は可能) 双方向ストリーミング対応
性能 遅め(テキスト形式+HTTP/1) 高速(バイナリ+HTTP/2)
言語対応 どの言語でも簡単に実装できる 多くの言語に公式サポートあり
可読性 JSONなので人間にも読みやすい Protocol Buffersは人間には読みにくい
実装の手軽さ 非常に簡単 .proto の学習+コード生成が必要

3. MCPがJSON-RPCを採用している理由

MCPがgRPCではなくJSON-RPCを使用している主な理由:

  1. 人間が扱いやすい(デバッグしやすい)

    • JSONはそのまま読めるし、curlやPostmanでも簡単に試せる
    • AIツールのデバッグや実験に適している
  2. 柔軟性と相互運用性

    • MCPは標準化を目指しており、「どの言語・環境でも動く」ことが重要
    • ブラウザ・Node.js・Python・Go・Rustなど幅広い環境で扱える
  3. 初期の導入コストが低い

    • gRPCは.protoの学習・コード生成など初期コストが高い
    • MCPは誰でも試せる・組み込めることを重視
  4. AIエージェント同士のやりとりに適している

    • 構造化されつつも柔軟なやりとりが必要
    • スキーマが強すぎるgRPCは逆に制約になるケースもある

4. RPCの通信の仕組み

RPCは「ネットワーク越しに関数を呼び出したように見せる」仕組みです。実際には裏で通信処理を行っています。

通信ステップ:

  1. クライアントが関数を呼び出す

    const result = add(3, 5);
    
  2. この呼び出しは、裏でこう変換される

    {
      "jsonrpc": "2.0",
      "method": "add",
      "params": [3, 5],
      "id": 1
    }
    

    これをHTTPやWebSocketなどの通信でサーバーへ送信

  3. サーバーが受信して、実際の関数を実行

    func add(a int, b int) int {
        return a + b
    }
    

    3 + 5 = 8 を実行

  4. 結果をクライアントに返す

    {
      "jsonrpc": "2.0",
      "result": 8,
      "id": 1
    }
    

    クライアント側ではこの結果を受け取り、add関数の返り値として扱う

内部処理:

  • スタブ: クライアント側の"偽の関数"。実際は通信するコードにすぎない
  • スケルトン: サーバー側で、受け取ったデータを"本物の関数"に橋渡しする

5. 低レイヤーの通信: ソケット

RPCの裏側で動いている低レイヤーの通信の仕組みは、ソケット(Socket)を使っています。

ソケットとは: アプリケーションがネットワーク越しにデータを送受信するための「出入口」です。

TCP通信の流れ:

  1. クライアントがサーバーに接続要求

    conn, _ := net.Dial("tcp", "example.com:8080")
    

    TCPの3-wayハンドシェイクで接続が確立

  2. 接続確立後、データ送信

    conn.Write([]byte("add 3 5"))
    

    OSのカーネルがIPパケットに分割し、TCPヘッダーを付けてネットに送出

  3. サーバー側はソケットで待機

    ln, _ := net.Listen("tcp", ":8080")
    conn, _ := ln.Accept()
    buf := make([]byte, 1024)
    conn.Read(buf)
    
  4. サーバーで処理して結果を返す

    conn.Write([]byte("8\n"))
    

RPC通信の層構造:

  • RPC: 関数名+引数で通信
  • JSON-RPC/gRPC: プロトコルに従ったパッケージ化
  • TCPソケット: アプリ間で信頼性ある通信を提供
  • IP層: パケットをルーティング
  • 物理層など: 実際のネットワーク(Wi-Fi、有線など)

6. MCPにおける通信方式の実装

MCPでは、複数の通信方式(トランスポート)が実装されており、様々な環境で利用できるようになっています。

MCPのトランスポート実装

MCPコードベースには、以下の主なトランスポート実装が含まれています:

  1. StreamableHTTPServerTransport / StreamableHTTPClientTransport

    • HTTP + Server-Sent Events (SSE) ベースの通信
    • セッション管理やリクエスト/レスポンスの追跡機能を持つ
    • 通常のHTTPリクエスト/レスポンスとSSEを組み合わせた設計
  2. WebSocketClientTransport / WebSocketServerTransport

    • WebSocketベースの双方向通信
    • 持続的な接続を維持し、メッセージ送受信が可能
  3. StdioClientTransport / StdioServerTransport

    • 標準入出力ベースの通信
    • プロセス間通信に適しており、コマンドライン環境で使用可能
  4. SSEServerTransport / SSEClientTransport

    • Server-Sent Events専用のトランスポート
    • サーバーからクライアントへの一方向通信に特化
  5. InMemoryTransport

    • 同一プロセス内での通信に使用(主にテスト用)

Streamable HTTP実装の詳細

特に重要なStreamableHTTPServerTransportの実装では:

  1. セッション管理

    • 初期化時にセッションIDが生成される
    • 以降のリクエストは全てmcp-session-idヘッダーでセッションを識別
    • サーバー側は複数のセッションを同時に管理できる
  2. メッセージの流れ

    • 初期化:クライアントがinitializeメソッドを呼び出し、サーバーがセッションIDを発行
    • 通常リクエスト:POSTリクエストでJSON-RPCメッセージを送信
    • イベント購読:GETリクエストでSSEストリームを確立
  3. SSEストリーム活用

    • リクエスト/レスポンスパターンでもSSEを使用可能
    • JSON応答モード(enableJsonResponse)も用意されている
    • リクエストIDを使って適切なレスポンスマッピングを行う

JSON-RPCメッセージの構造

MCPのJSON-RPC実装では、次の4つの主要メッセージタイプを使用:

  1. リクエスト(Request)

    {
      "jsonrpc": "2.0",
      "method": "tools/call",
      "params": { "name": "greet", "arguments": { "name": "World" } },
      "id": 1
    }
    
  2. レスポンス(Response)

    {
      "jsonrpc": "2.0",
      "result": { "content": [{ "type": "text", "text": "Hello, World!" }] },
      "id": 1
    }
    
  3. 通知(Notification)

    {
      "jsonrpc": "2.0",
      "method": "notifications/message",
      "params": { "level": "info", "data": "Processing request" }
    }
    
  4. エラー(Error)

    {
      "jsonrpc": "2.0",
      "id": 1,
      "error": {
        "code": -32602,
        "message": "Invalid params"
      }
    }
    

リクエスト/レスポンスとイベント通知の違い

特徴 リクエスト/レスポンス 通知(イベント)
JSON-RPC構造 idフィールドあり idフィールドなし
送信方向 双方向(リクエスト→レスポンス) 一方向(サーバー→クライアントなど)
使用例 ツール呼び出し、リソース取得 ログメッセージ、進捗状況
応答期待 応答が必要 応答は期待しない

補足:StreamableHTTP通信の流れ

  1. 初期化

    • クライアントがPOSTでinitializeメソッドを呼び出す
    • サーバーが応答とともにセッションIDを返す(mcp-session-idヘッダー)
  2. 通常リクエスト

    • クライアントがセッションIDを含めてPOSTリクエスト
    • サーバーは応答としてJSON(または必要に応じてSSEストリーム)を返す
  3. イベント購読

    • クライアントがセッションIDを含めてGETリクエスト(SSEストリーム確立)
    • サーバーはSSEフォーマットでイベントを継続的に送信
  4. セッション終了

    • クライアントがDELETEメソッドでセッション終了をリクエスト
    • またはタイムアウトや接続切断でセッション終了

この設計により、HTTP上でも双方向通信と持続的なイベントストリームを実現しています。

まとめ

MCPはJSON-RPCをベースにしながら、多様な通信方式をサポートする柔軟な設計を採用しています。特に:

  • 様々なトランスポート(HTTP+SSE、WebSocket、標準入出力)に対応
  • セッション管理と状態追跡の仕組みを提供
  • リクエスト/レスポンスとイベント通知を統一的に扱う
  • 複数の同時接続や複雑なメッセージフローをサポート

これらの特徴により、AIエージェント間の豊かなコミュニケーションが可能になり、モデルの文脈や機能を効果的に共有・管理できるようになっています。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment