MCPサーバーは実際にどのように通信するのか — JSON-RPC、stdio、Streamable HTTPを低レベルで理解する

ChatGPTやClaudeのようなAIアプリケーションにファイルシステム、データベース、社内APIを接続する際、「MCPサーバー」という言葉をよく耳にします。名前に「プロトコル」と入っているので、TCPやHTTPに代わる新しいネットワーク規格のように見えたり、REST APIにツールリストを付け加えるだけのように感じられたりすることもあります。

正確に言えば、MCP(Model Context Protocol)は、AIアプリケーションと外部機能間のアプリケーション層RPC規約です。転送には既存の運用環境で慣れ親しんだプロセス標準入出力やHTTPを使用し、メッセージエンベロープにはJSON-RPC 2.0、ツールの入出力契約にはJSON Schemaを使用します。

この記事では、2026年7月28日に公開された最新の安定仕様を基準に、MCPサーバーの役割、実際のバイトストリーム、HTTPヘッダーと本文、SSE応答、スキーマ標準、認証、エラー処理まで、既存のIT担当者がパケットフローを描けるレベルで整理します。

図:MCPのメッセージ層と転送層を分離した構造

まず結論:MCPとは何か、何ではないのか

MCPを既存技術に当てはめると、次のように整理できます。

質問 正確な答え
— —
MCPはHTTPを置き換えるのか いいえ。リモート転送にはHTTPを使用します。
MCP独自のデータフォーマットがあるのか はい。JSON-RPC 2.0エンベロープ内に、MCPが定義するmethod、params、result、_metaを格納します。
シリアル化形式は何か UTF-8 JSONです。画像・音声はJSON内にMIMEタイプとBase64データとして含めることができます。
入力パラメータの規約は何か JSON Schemaであり、$schemaがなければ2020-12をデフォルトのダイアレクトと見なします。
WebSocketが必要か いいえ。標準転送はstdioとStreamable HTTPです。WebSocketは、別途カスタムトランスポートを作成する際に選択できるだけです。
REST APIなのか いいえ。リソース中心のHTTP APIではなく、tools/list、tools/callのようなメソッドを呼び出すRPCです。
gRPCと同じか 目的は一部似ていますが、ProtobufバイナリとHTTP/2ストリームを強制しません。
MCPサーバーはLLMなのか いいえ。LLMが呼び出すツール・リソース・プロンプトを公開するアダプターまたはサービスです。

重要な区別はデータ層と転送層です。

MCP 데이터 계층
  JSON-RPC 2.0
  MCP 메서드와 메시지 의미
  도구·리소스·프롬프트 스키마
  오류와 메타데이터

MCP 전송 계층
  stdio
  Streamable HTTP
  또는 의미를 보존하는 custom transport

図:MCPのHost・Client・Serverとデータ・転送層の関係 · 公式MCP Architecture文書を基に再構成

したがって、「MCPがHTTPを使うのか?」という質問の答えは、リモートMCPはHTTPを使いますが、MCPの意味自体がHTTPに依存するわけではない、です。

Host、Client、Serverの役割

MCP構造には3つの役割が登場します。

사용자
  ↓
Host 애플리케이션
  ├─ MCP Client A ── MCP Server: 파일 시스템
  ├─ MCP Client B ── MCP Server: GitHub
  └─ MCP Client C ── MCP Server: 사내 데이터베이스

Hostは、対話UI、LLM、権限承認画面を含む全体アプリケーションです。MCP Clientは、特定のMCP Serverと通信するプロトコル構成要素です。MCP Serverは、外部システムをtools、resources、promptsのようなMCP機能として公開します。

例えば、`query_database`というツールを提供するMCPサーバーは、データベース自体ではないかもしれません。認証とSQLポリシーを適用し、既存のDBドライバーを呼び出した後、結果をMCP形式で返すアダプターに近いものです。

共通メッセージエンベロープはJSON-RPC 2.0である

転送方式がstdioであろうとHTTPであろうと、本文メッセージはJSON-RPC 2.0を使用します。リクエストは概ね次の構造です。

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "Seoul"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "example-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

各フィールドの役割は明確です。

フィールド 意味
— —
jsonrpc JSON-RPCバージョン。MCPは”2.0″を使用する。
id リクエストと応答を接続する相関関係ID。進行中のリクエスト間で一意である必要がある。
method 実行するMCPメソッド名
params メソッド引数
_meta プロトコルバージョン、クライアント機能などMCPメタデータ

応答は同じidを返します。

{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "resultType": "complete",
    "content": [
      {
        "type": "text",
        "text": "서울의 현재 기온은 27°C입니다."
      }
    ],
    "isError": false
  },
  "_meta": {
    "io.modelcontextprotocol/serverInfo": {
      "name": "weather-server",
      "version": "2.1.0"
    }
  }
}

通知にはidがありません。受信者が応答を送信しないメッセージだからです。

{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progress": 70,
    "total": 100
  }
}

図:JSON-RPCリクエスト・応答・通知のフィールドと相関関係 · 公式MCP Architecture文書を基に再構成

つまり、MCPがJSONの上に何の規則もなく自由にデータを載せるわけではありません。JSON-RPC 2.0のリクエスト・応答・通知形式を守り、その中のMCP methodとデータタイプはMCP仕様と公式スキーマに従う必要があります。

2026仕様の核心的変化:接続ではなくリクエストが単位である

過去のMCP資料には、クライアントがinitializeリクエストで接続を初期化し、サーバーがセッションIDを発行し、サーバーがクライアントにJSON-RPCリクエストを再度送信する流れがよく登場します。2025年仕様を説明した記事であれば、当時は正しい内容です。

しかし、2026-07-28仕様はステートレス、リクエストごとのモデルに変わりました。

과거 모델
연결 수립 → initialize → capability 협상 → 세션 상태 유지 → 여러 호출

현재 모델
각 요청 = 프로토콜 버전 + capability + 필요한 인증·메타데이터를 자체 포함

サーバーは、TCP接続やstdioプロセスが同じであるという理由だけで、プロトコルバージョン、クライアントの身元、capabilityを推論してはなりません。長く維持する必要がある作業状態がある場合は、明示的なrequestState、subscription IDのような識別子で表現する必要があります。

現在必須のリクエストメタデータは次の2つです。

"_meta": {
  "io.modelcontextprotocol/protocolVersion": "2026-07-28",
  "io.modelcontextprotocol/clientCapabilities": {}
}

clientInfoやログレベルなどはオプションです。必須メタデータがない場合、サーバーはJSON-RPC -32602 Invalid paramsで拒否し、HTTP転送ではHTTP 400を併用します。

この変化は、ロードバランサーの背後でリクエストを別のインスタンスに送信したり、サーバーレスで処理したりしやすくなるという利点があります。一方、旧型SDKと最新サーバーが混在すると動作方式が大きく異なるため、実装者はサポートする仕様の日付を必ず確認する必要があります。

stdio転送:1行にJSONメッセージ1つ

ローカルMCPサーバーで最も単純な方法はstdioです。クライアントがMCPサーバーを子プロセスとして実行し、次のチャネルを使用します。

Client ── child stdin  ──> MCP Server
Client <── child stdout ── MCP Server
Client <── child stderr ── 로그

ワイヤーフォーマットは、信頼できる双方向バイトストリーム上に1行あたりUTF-8 JSON-RPCメッセージ1つです。

{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{...}}}

{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search","arguments":{"q":"MCP"},"_meta":{...}}}

規則は厳格です。

  • メッセージは改行文字で区切り、メッセージ内部に実際の改行を入れてはなりません。JSON文字列の改行は`n`でエスケープします。
  • サーバーのstdoutには有効なMCPメッセージのみを書き込む必要があります。
  • ログ、デバッグ出力、スタックトレースはstderrに送信します。
  • すべての応答は1つのstdoutチャネルに混ざって来るため、idでリクエストと応答をマッチングします。
  • 進行通知も同じチャネルで来て、進行中のリクエストまたはsubscription IDと接続します。
  • キャンセルはnotifications/cancelled通知でリクエストIDを指定します。

stdioだからといって、このフレーミングがオペレーティングシステムの標準ストリームでしかできないわけではありません。同じ「1行にJSON 1つ」の規則をUnix Domain SocketやTCPに適用したカスタムトランスポートも作成できます。ただし、それは標準のstdioトランスポートではなく、同じフレーミングを再利用したユーザー定義の転送です。

図:stdio転送のリクエスト・応答・ログチャネルと改行フレーミング · 公式MCP stdio仕様を基に再構成

Streamable HTTP:リクエストごとにPOSTを1つ

リモートMCPサーバーの標準転送はStreamable HTTPです。最新仕様の核心は次のとおりです。

POST /mcp  ── JSON-RPC 요청 1 ──> JSON 한 개 또는 요청 범위 SSE
POST /mcp  ── JSON-RPC 요청 2 ──> JSON 한 개 또는 요청 범위 SSE
POST /mcp  ── JSON-RPC 알림   ──> 202 Accepted

サーバーは1つのMCPエンドポイントを提供し、すべてのクライアントメッセージはそれぞれ新しいHTTP POSTで送信されます。リクエスト本文にはJSON-RPCオブジェクトが1つだけ含まれます。JSON配列を利用したバッチや、複数のJSON-RPCメッセージを1つのPOSTに入れる方式は考慮してはなりません。

実際のリクエストは次のようになります。

POST /mcp HTTP/1.1
Host: mcp.example.com
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer eyJ...
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {"location": "Seoul"},
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

ここで注目すべき点は、一部の値がHTTPヘッダーとJSON本文に重複していることです。

HTTPヘッダー 本文のソース 目的
— — —
MCP-Protocol-Version _meta.io.modelcontextprotocol/protocolVersion 仕様バージョンの選択
Mcp-Method method プロキシ・WAF・観測ツールが本文解析なしでルーティング
Mcp-Name params.nameまたはparams.uri 呼び出し対象のツール・リソース・プロンプトの識別

本文が意味のsource of truthであり、サーバーはヘッダーと本文が一致するかを検証します。バージョンが異なる場合、HTTP 400とHeaderMismatch JSON-RPCエラーを返します。

ツールスキーマの特定のプリミティブ型フィールドにx-mcp-headerを宣言すると、Mcp-Param-{Name}ヘッダーとして複製することもできます。これはロードバランサーが地域やテナントのような値を基準にルーティングする際に有用ですが、パスワード、トークン、個人情報をヘッダーに含めると中間機器やログに露出しやすいため、使用すべきではありません。

日本語のように安全なASCIIヘッダー値で表現できない値は、UTF-8バイトをBase64に変換し、次のセンチネル形式を使用します。

Mcp-Name: =?base64?{Base64EncodedValue}?=

なぜStreamable HTTPという名前なのか

サーバーはリクエストの性質に応じて、2つの方式のいずれかで応答します。

短い応答:application/json

HTTP/1.1 200 OK
Content-Type: application/json

{"jsonrpc":"2.0","id":42,"result":{"resultType":"complete",...}}

進行状況がある応答:text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream

data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progress":50}}

data: {"jsonrpc":"2.0","id":42,"result":{"resultType":"complete","content":[...]}}

SSEは、当該POSTリクエストに依存する応答ストリームです。進行通知を送信した後、必ず最終的なJSON-RPC応答で終了します。クライアントがこのSSE接続を閉じると、サーバーは当該リクエストがキャンセルされたものとして処理する必要があります。

長期的なツールリストの変更やリソースの変更を受け取りたい場合は、別途GETストリームを開くのではなく、subscriptions/listenリクエストを送信してその応答ストリームを維持します。2026仕様では、過去の独立したGET SSEエンドポイントとプロトコルセッションは削除されました。Last-Event-IDでSSEストリームを再開する機能もサポートしていません。

したがって、運用環境ではリバースプロキシのSSEバッファリング、アイドルタイムアウト、最大リクエスト時間、接続終了伝播を点検する必要があります。HTTP/1.1でも可能ですが、複数の同時ストリームと接続効率を考慮すると、HTTP/2サポートの有無もインフラの観点から確認する価値があります。ただし、MCP仕様がHTTP/2自体を必須と強制するわけではありません。

図:Streamable HTTPのリクエスト別JSON応答とSSEストリーミング分岐 · 公式MCP Streamable HTTP仕様を基に再構成

Toolsはどのように発見し、呼び出すのか

MCPサーバーが提供するツールをクライアントが事前にハードコーディングする必要はありません。tools/listでランタイムに発見します。

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

サーバーはツール名、説明、入力スキーマを返します。

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "tools": [
      {
        "name": "get_weather",
        "description": "지역의 현재 날씨를 조회합니다.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "도시 이름"
            }
          },
          "required": ["location"],
          "additionalProperties": false
        }
      }
    ]
  }
}

Hostはこの説明とスキーマをLLMに伝え、モデルがツール呼び出しを提案すると、ポリシーとユーザー承認を経てtools/callを送信します。MCPはモデルの判断や承認UIまで強制するわけではありません。

JSON Schemaはどこまで標準化されているのか

MCPで「フォーマットがあるのか?」という質問は、2つの層に分けて考える必要があります。

第一に、MCPプロトコルメッセージタイプは公式TypeScriptスキーマがsource of truthであり、ここからJSON Schemaが生成されます。SDK実装者はこのスキーマを基にリクエストと応答タイプを合わせることができます。

第二に、各ツールのinputSchemaとオプションのoutputSchemaもJSON Schemaです。$schemaが省略された場合、JSON Schema 2020-12として解釈されます。実装は2020-12をサポートする必要があり、異なるダイアレクトを使用する場合は$schema URIで明示する必要があります。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": ["limit"],
  "additionalProperties": false
}

JSON Schemaはタイプだけでなく、必須フィールド、範囲、文字列パターン、列挙値、ネストされた構造を検証できます。しかし、スキーマがあるからといって自動的に安全になるわけではありません。外部URLの$refを自動的に取得するとSSRFやサプライチェーン問題が発生する可能性があり、過度なoneOf、allOf、再帰参照は検証器のリソースを消費する可能性があります。公式仕様も外部ネットワーク$refの自動逆参照を禁止し、深さ・時間・サブスキーマ数の制限を推奨しています。

結果データはテキストのみ可能なのか

ツール結果のcontentは複数のタイプを混在させることができます。

{
  "resultType": "complete",
  "content": [
    {"type": "text", "text": "분석 결과입니다."},
    {
      "type": "image",
      "data": "iVBORw0KGgoAAA...",
      "mimeType": "image/png"
    },
    {
      "type": "resource_link",
      "uri": "file:///reports/result.json",
      "name": "result.json",
      "mimeType": "application/json"
    }
  ],
  "structuredContent": {
    "riskScore": 82,
    "severity": "high"
  },
  "isError": false
}

テキストはJSON文字列、画像と音声はBase64データとMIMEタイプ、ファイルやウェブ資源はURIリンクまたは埋め込みリソースで表現します。機械が処理する結果はstructuredContentにJSON値として返し、outputSchemaで検証できます。

ここでstructuredContentは、LLMがスキーマ制約付きデコーディングで作成した「構造化出力」とは異なる概念です。MCPサーバーが返すツール結果データに過ぎません。

大容量バイナリを毎回Base64で本文に入れると、サイズが約33%増加し、メモリ使用量も大きくなります。大きなファイルは、アクセス制御されたリソースURIや別途オブジェクトストレージURLを返し、クライアントが必要なときに読み込む設計の方が適切かもしれません。

HTTPステータスコードとJSON-RPCエラーは異なる層である

Streamable HTTPでは、エラーが2つの層に存在します。

HTTP 계층
  인증 실패, 잘못된 Origin, 지원하지 않는 endpoint,
  헤더 불일치, 전송 자체의 실패

JSON-RPC/MCP 계층
  파싱 실패, 잘못된 요청, 존재하지 않는 method,
  잘못된 params, tool 실행 오류

代表的なJSON-RPC標準エラーは次のとおりです。

コード 意味
— —
-32700 Parse error
-32600 Invalid Request
-32601 Method not found
-32602 Invalid params
-32603 Internal error

MCPはヘッダー不一致 -32020、必要なクライアント機能の欠落 -32021、サポートされていないプロトコルバージョン -32022のようなエラーも定義します。

ツール自体が正常に実行されたが業務エラーが発生した場合には、HTTP 200と正常なJSON-RPC応答内でisError: trueを使用できます。例えば、「決済カード拒否」はネットワーク転送失敗とは異なります。モニタリングでもHTTP成功率だけを見ると、MCPツールの業務失敗を見逃す可能性があります。

認証はHTTPでOAuth 2.1系列を使用する

MCP認証はオプションですが、HTTPベースのトランスポートが認証をサポートする場合、最新のMCP Authorization仕様に従うことが推奨されます。核心は、MCPサーバーがOAuthリソースサーバーとして動作することです。

MCP Client
  ├─ Protected Resource Metadata 조회
  ├─ Authorization Server Metadata 조회
  ├─ OAuth 2.1 + PKCE로 사용자 승인
  └─ Authorization: Bearer <access-token>
                         ↓
                    MCP Server

仕様はOAuth 2.1ドラフト、RFC 6750 Bearer Token、RFC 8414 Authorization Server Metadata、RFC 9728 Protected Resource Metadata、RFC 8707 Resource Indicatorsなどを組み合わせます。クライアントはトークンをクエリ文字列に入れてはならず、すべての保護されたHTTPリクエストにAuthorization: Bearerヘッダーを含める必要があります。サーバーはトークンのaudienceが自分自身であることを検証する必要があります。

stdioはこのHTTP OAuthフローに従わず、プロセス環境で資格情報を受け取ることを推奨します。だからといって、APIキーを設定ファイルやログに平文で残してもよいという意味ではありません。OSキーチェーン、シークレットマネージャー、最小権限環境変数注入のような別途運用統制が必要です。

REST、OpenAPI、gRPC、WebSocketと比較すると

技術 主な役割 転送・エンコーディング MCPとの違い
— — — —
REST API HTTPリソース操作 HTTP + 主にJSON MCPは定義されたRPCメソッドとAIツール発見規約を提供
OpenAPI HTTP APIの静的仕様 YAMLまたはJSON文書 MCPはランタイムのtools/listと呼び出し・結果メッセージまで定義
JSON-RPC 2.0 RPCメッセージエンベロープ 転送独立JSON MCPはこのエンベロープの上にメソッドと意味を標準化
gRPC 強型リモート呼び出し HTTP/2 + Protobuf MCPは人間が読めるJSONとJSON Schema中心
WebSocket 全二重接続転送 フレームベース MCPの基本転送ではなく、カスタムトランスポート候補
SSE サーバーからクライアントへのストリーミング HTTP text/event-stream Streamable HTTPでリクエストごとの進行通知・最終応答に使用

既存のREST APIをMCPに接続する際に、APIを捨てる必要はありません。MCPサーバーを薄いアダプターとして配置し、内部では既存のRESTやgRPCを呼び出す構成が一般的です。

LLM Host
  → MCP Client
  → MCP Server
  → 기존 REST/gRPC/DB/Queue

MCPはバックエンドシステムを新たに構築する技術というよりも、AIアプリケーションがそのシステムの機能を発見し、呼び出すための共通インターフェースに近いものです。

実際に導入する前に確認すべき候補を絞ると

既存のIT組織がMCPサーバーを検討する際には、次の項目から確認することをお勧めします。

1. 転送選択

  • 1台のコンピューターでHostがプロセスを直接実行する場合は、stdioが単純です。
  • 複数のユーザーとリモートサーバーを接続する場合は、Streamable HTTPが適しています。
  • カスタムトランスポートは、標準クライアント互換性と観測ツールを失うコストをまず計算する必要があります。

2. 仕様バージョンとSDK互換性

  • サーバーとクライアントが2026-07-28ステートレスモデルを両方サポートしているか確認します。
  • initialize、MCP-Session-Id、GET SSEに依存する旧型実装かどうかを区別します。
  • ヘッダーと本文のプロトコルバージョン不一致をテストします。

3. スキーマ品質

  • すべてのツールに具体的なinputSchemaを定義します。
  • 可能であればadditionalProperties: false、長さ・範囲・enumを使用します。
  • 重要な機械処理結果にはoutputSchemaとstructuredContentを提供します。
  • 外部$ref、再帰、複雑な組み合わせスキーマにリソース制限を設けます。

4. 権限と安全装置

  • ツールリストもトークンスコープに合わせて最小化します。
  • 読み取りと書き込みツールを分離し、削除・決済・権限変更はユーザー承認を受けます。
  • LLMが作成した引数を信頼せず、サーバーで再度検証します。
  • stdioサーバーが継承する環境変数とファイル権限を点検します。

5. HTTP運用項目

  • Origin検証とlocalhostバインドでDNSリバインディングを防止します。
  • OAuth audience、scope、トークン有効期限を検証します。
  • SSEバッファリング、アイドルタイムアウト、接続終了とキャンセル伝播をテストします。
  • Mcp-Method、Mcp-Nameはロギングしますが、機密性の高いMcp-Param-*は残しません。

6. 信頼性と観測性

  • JSON-RPC idでエンドツーエンドトレースを接続します。
  • HTTPエラー、JSON-RPCエラー、isError: trueを別途指標として集計します。
  • 再試行可能な読み取り操作と、重複実行が危険な書き込み操作を区別します。
  • タイムアウト、キャンセル、部分進行通知後の失敗をテストします。

まとめ

MCPサーバーを低レベルで見ると、神秘的なAI専用ネットワークではありません。

의미와 메시지
  MCP method + JSON-RPC 2.0

도구 계약
  JSON Schema 2020-12 기본

로컬 전송
  UTF-8 newline-delimited JSON over stdio

원격 전송
  HTTP POST + application/json 또는 request-scoped SSE

인증
  HTTP는 OAuth 2.1 계열, stdio는 실행 환경의 자격증명

MCPの価値は、新しいソケットやシリアル化フォーマットを発明したことにはありません。既存の標準を組み合わせて、AI Hostがツールを発見し、タイプを理解し、呼び出し、結果を受け取る共通の契約を作成したことにあります。

オペレーターが覚えておくべき核心は3つです。MCPデータ層と転送層を分離して見ること、最新仕様のステートレスリクエストモデルと旧型セッションモデルを混同しないこと、スキーマがあっても権限・検証・承認・観測をサーバー外部と内部で共に強制することです。

参考資料


Comments

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です