skip to content

What headers must an MCP Streamable HTTP POST carry, and what is HeaderMismatchError?

level: middleimportance: must knowfreq 62%

answer

  1. three headers, one of them duplicated
  2. the edge cannot parse JSON-RPC
  3. two statements that contradict each other
  4. 400 plus a spec-reserved error code
  5. -32020

basics

~10 s

An MCP Streamable HTTP request must carry MCP-Protocol-Version, Mcp-Method, and Mcp-Name for tools/call, resources/read and prompts/get. If MCP-Protocol-Version disagrees with the value in params._meta, the server returns HTTP 400 with JSON-RPC error -32020, HeaderMismatchError.

solid answer

~50 s

In MCP revision 2026-07-28 the HTTP binding requires three headers alongside the JSON-RPC body. `MCP-Protocol-Version` states the revision the request is written against and **MUST equal** the `io.modelcontextprotocol/protocolVersion` value inside `params._meta`; if the two disagree the server answers `400` with JSON-RPC error code `-32020`, `HeaderMismatchError`. `Mcp-Method` mirrors the `method` field of the body. `Mcp-Name` carries the target's name and is required for `tools/call`, `resources/read` and `prompts/get`. The point of the duplication is that infrastructure in front of the server — gateways, load balancers, WAFs, audit logs, rate limiters — can route and apply policy per method and per tool without parsing a JSON-RPC body. That is also exactly why a mismatch is a hard error rather than a warning: if the header and the body could disagree, the policy applied at the edge would not describe the call the server actually executes.

code

http · 8 lines
http
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-11-25
Mcp-Method: tools/call
Mcp-Name: search_docs

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

go deeper

for a junior

Remember the header names — MCP-Protocol-Version, Mcp-Method, Mcp-Name — and that the version also appears inside the request body, where it must match.

for a middle

Explain the mismatch rule precisely: HTTP 400 with JSON-RPC -32020 HeaderMismatchError, and say why the version is duplicated into a header at all.

for a senior

Show what the headers buy in production: per-method routing, per-tool rate limits and authorization at a gateway, and usable access logs for an endpoint that is otherwise a single opaque POST path.

for a principal

Own the tradeoff of duplicating protocol data into the transport layer: it lets ordinary edge infrastructure enforce policy, but it creates a drift surface, which is exactly why the spec makes disagreement a hard error rather than a preference rule.

## The three required headers MCP's Streamable HTTP transport in revision 2026-07-28 puts a small, fixed amount of information into HTTP headers that also appears in the JSON-RPC body. **`MCP-Protocol-Version`** — the revision string, e.g. `2026-07-28`. The same value is required inside the request body at `params._meta` under the key `io.modelcontextprotocol/protocolVersion`. The header and the `_meta` value MUST be identical. **`Mcp-Method`** — mirrors the JSON-RPC `method` of the body: `tools/call`, `resources/read`, `tools/list`, `server/discover`, and so on. **`Mcp-Name`** — the name of the thing being addressed. It is required for the three calls that name a target: `tools/call` (the tool name), `resources/read` (the resource), and `prompts/get` (the prompt). Other methods do not address a named entity and do not carry it. ## The mismatch error If `MCP-Protocol-Version` and the `_meta` protocol version differ, the server returns HTTP `400` and a JSON-RPC error object with code **`-32020`**, named **`HeaderMismatchError`**. This code sits inside the block `-32020` to `-32099`, which the MCP specification reserves for itself on top of the JSON-RPC standard codes. Note what this error is *not*. It is not "I do not speak that version" — that is `-32022`, `UnsupportedProtocolVersionError`, which carries `data.supported` and `data.requested`. `-32020` says something narrower and more damning: the request contradicts itself. The client sent two statements about which protocol revision this is, and they disagree, so the server refuses to guess which one to believe. ## Why duplicate information into headers at all The headers exist for everything sitting between the client and the MCP server. - **Routing.** A gateway can send `tools/call` to one pool and cheap metadata calls like `tools/list` to another, or route by `Mcp-Name` so that one expensive tool runs on dedicated capacity. - **Policy.** A WAF or authorization proxy can allow `resources/read` but require step-up authentication for a named destructive tool, without implementing a JSON-RPC parser. - **Observability.** Access logs, traces and metrics get a meaningful dimension for free. Without the headers, every request in the log is an indistinguishable `POST /mcp`, which makes latency and error-rate analysis per tool impossible. - **Rate limiting and quotas.** Per-method and per-tool limits become an edge concern rather than an application concern. - **Cost.** Body inspection at the edge means buffering and parsing every request, which is both slow and a denial-of-service surface of its own. ## Why the mismatch has to be fatal Duplicated data invites drift, and drift between an edge decision and a server action is a security problem, not a hygiene problem. If the header said one revision and the body another, a version-aware policy at the edge would be evaluating a different request from the one the server executes. The specification removes the ambiguity by making disagreement an error, and by making it an error the *server* raises — the party that can see both values. The same reasoning explains why `Mcp-Method` and `Mcp-Name` are worth taking seriously in a server implementation even though the body is authoritative: a server that ignores them entirely will happily execute requests whose headers lied to the gateway. A defensive server validates that they agree with the body and rejects the request when they do not. ## Implementation notes - Generate the headers from the same structure that produced the body. Hand-writing them twice in client code is how mismatches happen. - Do not try to be helpful by "correcting" a mismatch on the server. Returning `400` with `-32020` gives the client an unambiguous, debuggable signal; silently preferring one value hides a client bug that will resurface as inconsistent behaviour under a gateway. - Header names are case-insensitive in HTTP, but the values are not: the protocol version is an exact `YYYY-MM-DD` string, and tool names are case-sensitive. - These headers are per request, like everything else in this revision. There is no handshake that establishes them once for a connection, and a server MUST NOT infer them from an earlier request that arrived on the same connection. ## What an interviewer is checking The surface answer is "three headers". The answer that lands explains the *why*: MCP is stateless, one endpoint and one verb, so without these headers every request is opaque to infrastructure — and once you duplicate data, you must define what happens when the copies disagree.

  • How does -32020 differ from -32022?
    `-32020` `HeaderMismatchError` means the request contradicts itself: the `MCP-Protocol-Version` header and the `_meta` protocol version are not the same string. `-32022` `UnsupportedProtocolVersionError` means the request was internally consistent but named a revision this server does not implement; it carries `data.supported` and `data.requested` so the client can retry with a version both sides share.
  • Which requests must carry Mcp-Name, and why not all of them?
    `tools/call`, `resources/read` and `prompts/get` — the three methods that address a named target. Methods like `tools/list` or `server/discover` address the server as a whole, so there is no name to carry. The header exists so an intermediary can apply per-tool or per-resource policy without reading the body; for methods with no target there is nothing to express.
  • Should a server verify that Mcp-Method matches the body's method?
    Yes. The body is authoritative for execution, but if the header can differ from it, a gateway that routed or authorized on the header made its decision about a different call. Validating the pair and rejecting a disagreement keeps the edge's view and the server's action aligned, which is the whole reason the headers exist.

saying these in an interview costs you the question

  • Thinks MCP-Protocol-Version replaces the version inside params._meta
  • Treats a header/body version mismatch as something to auto-correct
  • Confuses -32020 with an unsupported-version error
  • Claims the headers are negotiated once per connection
  • Says Mcp-Name is required on every request

context