x-mcp-header仕様 — MCPツール引数をHTTPヘッダーに露出する仕組み
MCPのx-mcp-header拡張は、ツール引数の一部をHTTPヘッダーへミラーリングし、ロードバランサやWAFがボディを見ずにルーティングできるようにする仕組み。制約条件と実装時の注意点を整理する。
x-mcp-headerとは何を解決する仕組みか
MCPのツール呼び出しは、通常すべての引数がJSON-RPCのリクエストボディに入ります。ロードバランサやWAF(Web Application Firewall)がリクエストの中身を見てルーティング先を変えたい場合、ボディをパースしないと引数の値が分からず、処理コストが高くつきます。
x-mcp-headerはこの問題を解決する拡張プロパティです。ツール定義のinputSchema内で特定のプロパティに付けておくと、そのプロパティの値がHTTPリクエストのヘッダーにも複製されます。中間装置はヘッダーだけを見て、ボディをパースせずにルーティングや処理の判断ができるようになります。
現行のプロトコル改訂(2026-07-28)で追加された仕様で、直前の版(2025-11-25)には存在しません。同じ改訂ではMCPリクエストへの標準ヘッダー(Mcp-Method、Mcp-Name)の必須化も同時に行われており、x-mcp-headerはその流れの延長にあります。
Streamable HTTPでヘッダーがどう生成されるか
x-mcp-headerはプロパティのJSON Schemaに直接書きます。値には、生成されるヘッダー名の可変部分を指定します。実際のヘッダー名はMcp-Param-{name}という固定の接頭辞に、指定した名前が続く形になります。
{
"name": "execute_sql",
"description": "Execute SQL on Google Cloud Spanner",
"inputSchema": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The region to execute the query in",
"x-mcp-header": "Region"
},
"query": {
"type": "string",
"description": "The SQL query to execute"
}
},
"required": ["region", "query"]
}
}このツール定義でregionにus-west1を渡して呼び出すと、クライアントはHTTPリクエストにMcp-Param-Region: us-west1ヘッダーを追加します。query側にはx-mcp-headerが付いていないので、こちらはボディの中にしか現れません。ヘッダー化するかどうかはプロパティ単位で選べます。
常に付く2つの標準ヘッダーとの関係
x-mcp-headerはオプトインの拡張ですが、Streamable HTTPには常に付く標準ヘッダーが2つあります。Mcp-Method(JSON-RPCのmethodをそのまま複製)はすべてのリクエストに必須、Mcp-Name(呼び出すツール・リソース・プロンプトの名前)はtools/call / resources/read / prompts/getに必須です。
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: execute_sqlこの2つだけでも「どのメソッドの、どの名前への呼び出しか」はボディを見ずに分かります。x-mcp-headerはここに「その呼び出しの、どの引数の値か」を追加する位置づけです。3つを組み合わせると、中間装置はボディを一切パースせずにメソッド・対象・主要な引数値までルーティング判断の材料にできます。
ヘッダーとボディの値が食い違うとどう扱われるか
ヘッダーはボディの値を複製したものなので、両者が食い違う状態は許されません。サーバーはヘッダー値とボディ値の不一致を検証しなければならず、食い違いを見つけたら400 Bad RequestとHeaderMismatch(エラーコード-32020)を返します。
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32020,
"message": "Header mismatch: Mcp-Name header value 'foo' does not match body value 'bar'"
}
}この検証が無いと、中間装置がヘッダーだけを見て「テナントAへルーティング」と判断した一方で、実際にサーバーが処理するボディの値は別のテナントを指している、という状態が起こり得ます。ヘッダーとボディの一致検証は、ルーティング判断と実処理の対象がずれない保証として働きます。ミラーリングされたヘッダーを使ってルーティングやレート制限のポリシーを適用する中間装置は、MCP-Protocol-Versionもあわせて検証することが仕様で求められています。
日本語などの非ASCII値はどう扱われるか
RFC 9110の定めにより、HTTPヘッダーの値に使えるのは基本的に可視ASCII文字・半角スペース・水平タブだけです。地域コードのような英数字はそのままヘッダーに載りますが、値が日本語などの非ASCII文字を含む場合、あるいは制御文字や前後の空白を含む場合は、そのままでは載せられません。
この場合クライアントは、UTF-8表現をBase64エンコードし、=?base64?{エンコード済みの値}?=という形式で包んで送信しなければなりません。
| 元の値 | 理由 | ヘッダーの値 |
|---|---|---|
us-west1 | 理由ASCIIのみ | ヘッダーの値Mcp-Param-Region: us-west1 |
東京 | 理由非ASCII文字を含む | ヘッダーの値Mcp-Param-City: =?base64?5p2x5Lqs?= |
" padded " | 理由前後に空白 | ヘッダーの値Mcp-Param-Text: =?base64?IHBhZGRlZCA=?= |
=?base64?と?=という接頭辞・接尾辞は大文字小文字を区別し、必ずこの形のまま(小文字)でなければなりません。サーバーや中間装置がこの値を検証・比較する際は、必ずデコードしてから比較する必要があります。つまり、地域名やテナント名に日本語表記を使う設計をすると、x-mcp-headerのヘッダー値は素の文字列ではなくBase64化された形で流れることになります。ロードバランサ側のルーティングルールをヘッダー値の文字列一致で組む場合、この点を踏まえてルールを書く必要があります。
型変換の規則も決まっています。integerは10進数の文字列表現(42や-7)に、booleanは小文字の"true" / "false"に変換してから上記のエンコード判定を行います。すでにBase64センチネル形式(=?base64?で始まり?=で終わる形)と偶然一致する素のASCII値も、区別のためにBase64エンコードしなければならないという規定もあります。地域コードのような単純な値を選んでおけば、この一連のエンコード処理を意識せずに済みます。
5つの制約条件
x-mcp-headerの値には仕様上の制約があります。違反した場合の挙動は「そのツール定義ごと拒否」という強いものなので、サーバー実装時に確認しておく価値があります。
| 制約 | 内容 |
|---|---|
| 空文字禁止 | 内容x-mcp-headerの値は空であってはならない |
| トークン構文 | 内容RFC 9110のHTTPフィールド名トークン構文に一致すること |
| 制御文字禁止 | 内容CR(\r)・LF(\n)を含む値は不可 |
| 一意性 | 内容同一inputSchema内で大文字小文字を区別せず重複しないこと |
| 型の制限 | 内容適用できるのはinteger / string / booleanのみ。number型は不可 |
さらに、x-mcp-headerを付けられるのは、スキーマのルートからpropertiesキーだけをたどって到達できるプロパティに限られます。items(配列)、oneOf / anyOf / allOf / not(合成キーワード)、if / then / else、$refを経由する経路にあるプロパティには付けられません。ネストしたオブジェクトのプロパティでも、propertiesキーだけのチェーンであれば許容されます。
クライアントが不正な定義に出会ったときの挙動
Streamable HTTPを使うクライアントは、これらの制約に違反するx-mcp-headerを持つツール定義を拒否しなければなりません。拒否とは、tools/listの結果からそのツールを丸ごと除外することを指します。クライアントは拒否した理由とツール名を警告としてログに残すべきとされています。これにより、1つの不正なツール定義がサーバー全体の他のツールを巻き込んで使えなくすることを防ぎます。
なお、この拒否義務はStreamable HTTPを使うクライアントに限った話です。stdioなど別のトランスポートを使うクライアントはx-mcp-headerの注釈自体を無視してよいとされています。同じサーバー定義でも、接続方式によって挙動が変わる点は見落としやすいところです。
センシティブな値を載せてはいけない理由
仕様は明確に警告しています。パスワード・APIキー・トークン・個人情報のような機密性の高いパラメータにx-mcp-headerを付けないよう、サーバー開発者に求めています。ヘッダーの値はネットワーク上の中間装置(プロキシ・ロードバランサ・WAF)から見える位置に置かれるため、ボディの暗号化とは別のリスクが生まれます。
execute_sqlの例で見る値の流れ
仕様の例(Google Cloud Spannerに対してSQLを実行するツール)は、この機能の典型的な使いどころを示しています。regionをヘッダー化しておけば、複数リージョンにSpannerインスタンスを構えている環境で、ロードバランサはリクエストボディをパースせずにMcp-Param-Regionヘッダーだけを見て適切なリージョンのバックエンドへ振り分けられます。query(SQL文そのもの)はヘッダー化しない設計も理にかなっています。SQL文は長く、HTTPヘッダーの実用的なサイズ制限にも収まりにくいためです。
同様の設計は、マルチテナントのSaaSに対するMCPサーバーでテナントIDをヘッダー化する、処理の優先度を示すフラグをヘッダー化してWAF側のレート制限に使う、といった用途にも応用できます。MCPからデータベースに接続する記事で扱っている読み取り専用アクセスの設計とあわせて検討すると、ルーティングと権限の両面から安全なMCPサーバーを組み立てやすくなります。
Claude Codeでこの機能を使うときに確認すべきこと
Claude CodeでHTTPトランスポートのMCPサーバーに接続するにはclaude mcp add --transport httpを使います。JSON設定ではtypeにstreamable-httpをhttpのエイリアスとして書けるため、サーバー提供元のドキュメントをそのまま流用できます。
claude mcp add --transport http my-server https://api.example.com/mcpClaude CodeはMCP TypeScript SDK 2.0を使う新しいランタイム(v2)で、プロトコル改訂2026-07-28に対応しています。ただし、公式のMCP設定ドキュメントにはx-mcp-header固有の挙動についての記載が見当たりません。この仕様はStreamable HTTPを使うクライアントに対応を必須としているため、対応している可能性はありますが、断定できる一次情報は確認できませんでした。
自分のMCPサーバーでx-mcp-headerを使う設計にする場合は、想定どおりヘッダーが転送されているかを事前に確認してから、WAFやロードバランサ側のルーティングルールを組むのが安全です。確認にはMCP Inspectorでツール呼び出しの生リクエストを見る方法や、サーバー側でリクエストヘッダーをログに残す方法が使えます。MCPサーバー自作ガイドではツール定義の基本からClaude Code接続までを扱っています。
まとめ
x-mcp-headerは、ツール引数の一部をHTTPヘッダーへミラーリングし、中間装置がボディをパースせずにルーティングできるようにする拡張です。値の制約(空文字禁止・トークン構文・一意性・型・スキーマ上の到達可能性)に違反したツール定義はクライアントに拒否され、tools/listから丸ごと除外されます。機密情報をヘッダー化しないことと、実際に接続するクライアント(Claude Codeを含む)でヘッダーが転送される挙動を自分で確認することの2点が、実装前に押さえておきたい要点です。