
# 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                     |

```sh
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`:

```ts
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`.
