Request Headers
Body fields mirrored into headers, validated for divergence.
Modern clients mirror key body fields into HTTP headers so a gateway can route, authorize, or rate-limit without parsing the JSON-RPC body. h3-mcp validates header-vs-body equality on every modern POST and rejects any divergence with 400 + -32020 (HeaderMismatch).
| Header | Source | Required for |
|---|---|---|
MCP-Protocol-Version | _meta protocol version | all requests |
Mcp-Method | method | all requests |
Mcp-Name | params.name or params.uri | tools/call, resources/read, prompts/get |
Mcp-Param-{Name} | x-mcp-header annotation | annotated tool parameters |
curl -X POST http://localhost:3000/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2026-07-28' \
-H 'mcp-method: tools/call' \
-H 'mcp-name: hello' \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "hello", "arguments": {},
"_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {} } } }'#Non-ASCII Values
Values that cannot be represented as plain ASCII arrive wrapped in a sentinel — =?base64?{value}?= — and are decoded before comparison. Integer parameters must use the decimal representation (42 or 42.0); hexadecimal, exponent, and zero-padded forms are rejected rather than normalized.
#Notifications Are Checked Too
The headers are validated on every modern POST, requests and notification-shaped bodies alike. A body whose method is a request method but which carries no id is rejected with -32600 and never executed.
This closes an obvious bypass: if header checks were gated on the presence of an id, a client could drop the id and slip past a gateway that routes on Mcp-Method.
#Mirroring Tool Parameters
Annotate a tool parameter with x-mcp-header and clients will mirror it, letting an intermediary route on a value from inside arguments:
const queryTool = defineMcpTool({
name: "execute_sql",
inputSchema: {
type: "object",
properties: {
region: { type: "string", "x-mcp-header": "Region" },
query: { type: "string" },
},
required: ["region", "query"],
},
handler: async (args) => ({ content: [{ type: "text", text: args.region as string }] }),
});A client then sends Mcp-Param-Region: eu-west-1 alongside the body, and a mismatch is a -32020.
x-mcp-header is only honored on string / integer / boolean properties reachable from the schema root through a chain of properties keys. Annotations that violate those constraints are ignored rather than enforced.
Important
Never annotate a sensitive value. Mirrored parameters are visible to every intermediary on the path and routinely end up in access logs. Annotate a region or a tenant id, not a token, a query, or personal data.
Note
Header/body divergence is a security control, not a formality. An intermediary may act on the header while your handler acts on the body; if the two are allowed to disagree, every gateway decision becomes advisory. That is why -32020 is never relaxed.
#Accept
When a modern POST sends Accept, it must list both application/json and text/event-stream — the server picks between them per request. Anything else is a 406.