この回のゴール
- MCP が どう通信 しているかの具体的な仕組みを理解する
- JSON-RPC 2.0 の書式を読めるようになる
- stdio と HTTP (SSE) の 2 つの transport の違いを把握する
- Tools / Resources / Prompts の各プリミティブの API シェイプ を知る
1. MCP の通信方式の全体像
┌─────────┐ ┌──────────┐
│ Client │ ◀── JSON-RPC 2.0──▶│ Server │
└─────────┘ (transport) └──────────┘
│
│ transport は選べる:
├── stdio (標準入出力)
├── HTTP + SSE
└── WebSocket (草案中)
JSON-RPC 2.0 とは?
- リモート関数呼び出し の軽量プロトコル
- JSON で request / response を書く
- 通信路は 何でもよい (TCP, HTTP, stdio...)
- 2010 年策定、シンプルで枯れている
2. JSON-RPC の 3 種類のメッセージ
(a) Request — 関数を呼び出す
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_handbook",
"arguments": {"query": "有給は何日"}
}
}
id: 応答との紐付けmethod: 呼ぶ関数(プロトコルで決まっている)params: 引数
(b) Response — 結果を返す
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{"type": "text", "text": "勤続 5 年で 25 日..."}]
}
}
または、エラー:
{
"jsonrpc": "2.0",
"id": 1,
"error": {"code": -32602, "message": "Invalid params"}
}
(c) Notification — 応答不要の通知
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": {"progress": 0.5}
}
id がないのが特徴。進捗通知や状態変化に使う。
3. MCP の主要メソッド
初期化
client → server: initialize
params: {protocolVersion, capabilities, clientInfo}
server → client: result
result: {protocolVersion, capabilities, serverInfo}
client → server: notifications/initialized (完了通知)
Tools
client → server: tools/list (利用可能なツール一覧を要求)
server → client: result: {tools: [...]}
client → server: tools/call
params: {name: "...", arguments: {...}}
server → client: result: {content: [...]}
Resources
client → server: resources/list
server → client: result: {resources: [{uri, name, ...}]}
client → server: resources/read
params: {uri: "..."}
server → client: result: {contents: [{uri, mimeType, text}]}
Prompts
client → server: prompts/list
server → client: result: {prompts: [...]}
client → server: prompts/get
params: {name: "...", arguments: {...}}
server → client: result: {messages: [...]}
4. Transport: stdio vs HTTP+SSE
stdio (標準入出力)
[Client Process] ──stdin──▶ [Server Process]
stdout ◀──
- 用途: ローカルの信頼できるサーバー
- Claude Desktop が デフォルト で使うトランスポート
- サーバーは単なる Python/Node スクリプト
- シンプル・低遅延
- OS レベルで プロセス分離
HTTP + SSE (Server-Sent Events)
[Client] ──HTTP POST──▶ [Server]
◀──SSE stream─
- 用途: リモートサーバー、ネットワーク越し
- 認証 (OAuth)、マルチクライアント対応
- Anthropic 公式の多くの商用サーバーが使う形
どう使い分ける?
| 場面 | 推奨トランスポート |
|---|---|
| 自作ツール、ローカル開発 | stdio |
| Claude Desktop と連携 | stdio |
| リモート/クラウドサーバー | HTTP+SSE |
| マルチユーザー SaaS | HTTP+SSE |
5. 完全な 1 往復の例
ユーザー: 「有給何日?」
Step 1: ツール一覧取得(初回のみ)
→ {"jsonrpc":"2.0","id":1,"method":"tools/list"}
← {"jsonrpc":"2.0","id":1,"result":{"tools":[
{"name":"search_handbook","description":"...",
"inputSchema":{"type":"object","properties":{...}}}
]}}
Step 2: LLM がツール選択(ここは Claude API 経由)
Claude API に tools を渡して呼び出し → Claude が search_handbook を選択。
Step 3: ツール実行
→ {"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search_handbook","arguments":{"query":"有給"}}}
← {"jsonrpc":"2.0","id":2,"result":{"content":[
{"type":"text","text":"勤続 5 年で 25 日..."}
]}}
Step 4: 結果を LLM に戻す
tool_result として Claude API に返す → Claude が最終回答を生成。
6. 実は第 6 章と構造は同じ
第 6 章で作った search_handbook ツールと、MCP の search_handbook は:
- 定義の書式: JSON Schema(同じ)
- 呼び出し: 関数呼び出し(同じ)
- 結果: text/json 系(同じ)
違うのは「誰が呼ぶか」と「どう通信するか」だけ:
| 項目 | 第 6 章 (直接) | MCP |
|---|---|---|
| 誰が定義 | あなたの app.py | MCP サーバー作者 |
| 誰が呼ぶ | あなたの app.py | Claude Desktop 等のクライアント |
| 通信 | Python 関数呼び出し | JSON-RPC over stdio/HTTP |
| 再利用 | そのアプリ内だけ | 全ての MCP クライアント |
まとめ
- MCP の通信は JSON-RPC 2.0 (Request / Response / Notification の 3 種)
- 主要メソッド:
initialize→tools/list→tools/call(Resources / Prompts も同様) - Transport は stdio (ローカル・低遅延) と HTTP+SSE (リモート・OAuth) の 2 系
- 第 6 章の手書きツールと 構造は同じ。違いは「誰が呼ぶか・どう運ぶか」だけ
この回の限界(次への動機)
プロトコルはわかった。でも、実際にサーバーを書く にはどうすれば? Python の公式 SDK はどんな API? 👉 次回「最小 MCP サーバー」で、FastMCP デコレータでサーバーを 30 行で書きます。