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や任意の双方向通信でも使える
| 項目 | JSON-RPC | gRPC |
|---|---|---|
| プロトコル | JSON over HTTP or any transport | HTTP/2 |
| データ形式 | JSON(テキスト) | Protocol Buffers(バイナリ) |
| スキーマ定義 | なし(コードベースで定義) | .proto ファイルで明示的に定義 |
| 通信の双方向性 | 一方向(通知は可能) | 双方向ストリーミング対応 |
| 性能 | 遅め(テキスト形式+HTTP/1) | 高速(バイナリ+HTTP/2) |
| 言語対応 | どの言語でも簡単に実装できる | 多くの言語に公式サポートあり |
| 可読性 | JSONなので人間にも読みやすい | Protocol Buffersは人間には読みにくい |
| 実装の手軽さ | 非常に簡単 | .proto の学習+コード生成が必要 |
MCPがgRPCではなくJSON-RPCを使用している主な理由:
-
人間が扱いやすい(デバッグしやすい)
- JSONはそのまま読めるし、curlやPostmanでも簡単に試せる
- AIツールのデバッグや実験に適している
-
柔軟性と相互運用性
- MCPは標準化を目指しており、「どの言語・環境でも動く」ことが重要
- ブラウザ・Node.js・Python・Go・Rustなど幅広い環境で扱える
-
初期の導入コストが低い
- gRPCは.protoの学習・コード生成など初期コストが高い
- MCPは誰でも試せる・組み込めることを重視
-
AIエージェント同士のやりとりに適している
- 構造化されつつも柔軟なやりとりが必要
- スキーマが強すぎるgRPCは逆に制約になるケースもある
RPCは「ネットワーク越しに関数を呼び出したように見せる」仕組みです。実際には裏で通信処理を行っています。
通信ステップ:
-
クライアントが関数を呼び出す
const result = add(3, 5); -
この呼び出しは、裏でこう変換される
{ "jsonrpc": "2.0", "method": "add", "params": [3, 5], "id": 1 }これをHTTPやWebSocketなどの通信でサーバーへ送信
-
サーバーが受信して、実際の関数を実行
func add(a int, b int) int { return a + b }3 + 5 = 8 を実行
-
結果をクライアントに返す
{ "jsonrpc": "2.0", "result": 8, "id": 1 }クライアント側ではこの結果を受け取り、add関数の返り値として扱う
内部処理:
- スタブ: クライアント側の"偽の関数"。実際は通信するコードにすぎない
- スケルトン: サーバー側で、受け取ったデータを"本物の関数"に橋渡しする
RPCの裏側で動いている低レイヤーの通信の仕組みは、ソケット(Socket)を使っています。
ソケットとは: アプリケーションがネットワーク越しにデータを送受信するための「出入口」です。
TCP通信の流れ:
-
クライアントがサーバーに接続要求
conn, _ := net.Dial("tcp", "example.com:8080")TCPの3-wayハンドシェイクで接続が確立
-
接続確立後、データ送信
conn.Write([]byte("add 3 5"))OSのカーネルがIPパケットに分割し、TCPヘッダーを付けてネットに送出
-
サーバー側はソケットで待機
ln, _ := net.Listen("tcp", ":8080") conn, _ := ln.Accept() buf := make([]byte, 1024) conn.Read(buf) -
サーバーで処理して結果を返す
conn.Write([]byte("8\n"))
RPC通信の層構造:
- RPC: 関数名+引数で通信
- JSON-RPC/gRPC: プロトコルに従ったパッケージ化
- TCPソケット: アプリ間で信頼性ある通信を提供
- IP層: パケットをルーティング
- 物理層など: 実際のネットワーク(Wi-Fi、有線など)
MCPでは、複数の通信方式(トランスポート)が実装されており、様々な環境で利用できるようになっています。
MCPコードベースには、以下の主なトランスポート実装が含まれています:
-
StreamableHTTPServerTransport / StreamableHTTPClientTransport
- HTTP + Server-Sent Events (SSE) ベースの通信
- セッション管理やリクエスト/レスポンスの追跡機能を持つ
- 通常のHTTPリクエスト/レスポンスとSSEを組み合わせた設計
-
WebSocketClientTransport / WebSocketServerTransport
- WebSocketベースの双方向通信
- 持続的な接続を維持し、メッセージ送受信が可能
-
StdioClientTransport / StdioServerTransport
- 標準入出力ベースの通信
- プロセス間通信に適しており、コマンドライン環境で使用可能
-
SSEServerTransport / SSEClientTransport
- Server-Sent Events専用のトランスポート
- サーバーからクライアントへの一方向通信に特化
-
InMemoryTransport
- 同一プロセス内での通信に使用(主にテスト用)
特に重要なStreamableHTTPServerTransportの実装では:
-
セッション管理
- 初期化時にセッションIDが生成される
- 以降のリクエストは全て
mcp-session-idヘッダーでセッションを識別 - サーバー側は複数のセッションを同時に管理できる
-
メッセージの流れ
- 初期化:クライアントが
initializeメソッドを呼び出し、サーバーがセッションIDを発行 - 通常リクエスト:POSTリクエストでJSON-RPCメッセージを送信
- イベント購読:GETリクエストでSSEストリームを確立
- 初期化:クライアントが
-
SSEストリーム活用
- リクエスト/レスポンスパターンでもSSEを使用可能
- JSON応答モード(
enableJsonResponse)も用意されている - リクエストIDを使って適切なレスポンスマッピングを行う
MCPのJSON-RPC実装では、次の4つの主要メッセージタイプを使用:
-
リクエスト(Request)
{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "greet", "arguments": { "name": "World" } }, "id": 1 } -
レスポンス(Response)
{ "jsonrpc": "2.0", "result": { "content": [{ "type": "text", "text": "Hello, World!" }] }, "id": 1 } -
通知(Notification)
{ "jsonrpc": "2.0", "method": "notifications/message", "params": { "level": "info", "data": "Processing request" } } -
エラー(Error)
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Invalid params" } }
| 特徴 | リクエスト/レスポンス | 通知(イベント) |
|---|---|---|
| JSON-RPC構造 | idフィールドあり |
idフィールドなし |
| 送信方向 | 双方向(リクエスト→レスポンス) | 一方向(サーバー→クライアントなど) |
| 使用例 | ツール呼び出し、リソース取得 | ログメッセージ、進捗状況 |
| 応答期待 | 応答が必要 | 応答は期待しない |
-
初期化
- クライアントがPOSTで
initializeメソッドを呼び出す - サーバーが応答とともにセッションIDを返す(
mcp-session-idヘッダー)
- クライアントがPOSTで
-
通常リクエスト
- クライアントがセッションIDを含めてPOSTリクエスト
- サーバーは応答としてJSON(または必要に応じてSSEストリーム)を返す
-
イベント購読
- クライアントがセッションIDを含めてGETリクエスト(SSEストリーム確立)
- サーバーはSSEフォーマットでイベントを継続的に送信
-
セッション終了
- クライアントがDELETEメソッドでセッション終了をリクエスト
- またはタイムアウトや接続切断でセッション終了
この設計により、HTTP上でも双方向通信と持続的なイベントストリームを実現しています。
MCPはJSON-RPCをベースにしながら、多様な通信方式をサポートする柔軟な設計を採用しています。特に:
- 様々なトランスポート(HTTP+SSE、WebSocket、標準入出力)に対応
- セッション管理と状態追跡の仕組みを提供
- リクエスト/レスポンスとイベント通知を統一的に扱う
- 複数の同時接続や複雑なメッセージフローをサポート
これらの特徴により、AIエージェント間の豊かなコミュニケーションが可能になり、モデルの文脈や機能を効果的に共有・管理できるようになっています。