Repository navigation
Validate SEP-2243 Mcp-Param-{Name} headers in the modern server #1199
Description
Summary
The modern server (io.modelcontextprotocol.modern) doesn't support SEP-2243 custom headers yet. A tool can mark
parameters in its inputSchema with x-mcp-header. Clients then MUST mirror those values into
Mcp-Param-{Name} HTTP headers, and the server MUST check that the headers match the body. Nothing in the
modern server does that check today. The conformance scenario http-custom-header-server-validation reports
"not testable" only because no fixture tool uses x-mcp-header.
Spec: Streamable HTTP, Custom Headers from Tool Parameters
Proposal
The servlet transport doesn't know which parameters a tool mirrors, because that mapping lives in the tool's
inputSchema. So the work is split in two:
-
Extract in the transport.
HttpServletMcpTransportcollects everyMcp-Param-*header into a
TransportParamsvalue:- header names are case-insensitive;
- values in the
=?base64?…?=sentinel format are decoded; - a value with invalid characters or bad Base64 is marked as malformed instead of failing the request,
because only recognized headers may be rejected, and the transport doesn't know yet which headers are
recognized.
The transport stores
TransportParamsin theMcpTransportContextunder a key defined by the SDK. -
Validate after the payload is parsed. In
ToolsFeature, afterfindreturns theTooland before the
tool runs, readTransportParamsfrom the transport context and check it against thetools/call
arguments, using the parametersinputSchemamarks withx-mcp-header.
Requirements for the transport context key
- The transport sets the key after the user's
contextExtractorruns, so the extractor can't overwrite it
or forge it. - A missing key must not mean "skip validation". Otherwise any future HTTP transport that forgets to set the
key would silently turn validation off. stdio sets an explicit "not mirrored" value, and validation is
skipped only for that value.
Validation rules
For each parameter that inputSchema marks with x-mcp-header, reached only through properties keys from
the schema root:
| Case | Behavior |
|---|---|
| A value is present at the parameter's path | The header MUST be present and match: strings as-is, integers compared numerically (42.0 == 42), booleans as true/false |
The value is null, or the path is absent (including a missing intermediate object) |
No header is expected |
| A value is in the body but the header is missing | Reject |
| A recognized header is malformed (invalid characters or bad Base64) | Reject |
An Mcp-Param-* header that no parameter declares |
Ignore |
Every rejection is answered with HTTP 400 and JSON-RPC error -32020 (HeaderMismatch). In code, that means
throwing McpException with ErrorCodes.HEADER_MISMATCH; the transport already maps -32020 to 400. Header
validation runs before arguments are validated against inputSchema, so a mismatch is never reported as an
isError tool result.
Out of scope / open
- The server's own
x-mcp-headerdefinitions. The spec's rules (non-empty, RFC 9110 token, unique
ignoring case, onlystring/integer/boolean, integers in the JS safe range, statically reachable) are a
client MUST: clients drop invalid tools fromtools/list. Decide whether the server warns about invalid
definitions or just ignores them when validating. - The modern client side. Building
Mcp-Param-*headers and filtering out invalid tools is a separate
issue, but it can reuse the samex-mcp-headerparser.
Acceptance criteria
- Unit tests for decoding: valid Base64, invalid Base64, plain values that only look partly like the
sentinel, case-insensitive names - Unit tests for the parser: nested
propertiespaths; annotations behinditems,allOfor$ref
are ignored -
ToolsFeaturetests: one per row of the table above, plus42.0vs42, boolean encoding, and stdio
skipping validation - Integration test:
400/-32020end to end throughHttpServletMcpTransport - A fixture tool with an
x-mcp-headerparameter inconformance-tests/server-servlet-modern;
http-custom-header-server-validationpasses and--requirements 2026-07-28shows no regressions - The Known Limitations entry in
conformance-tests/VALIDATION_RESULTS.mdis updated
Activity
prathamgoyal787 commentedon Oct 20, 2026
Hi @chemicL, I'd like to work on this one. Plan, following the proposal:
Mcp-Param-*headers intoTransportParamsinHttpServletMcpTransport(case-insensitive names,=?base64?…?=decoding, malformed values flagged rather than rejected), set after the user'scontextExtractor, with an explicit "not mirrored" value for stdio.ToolsFeatureafterfindand before input-schema validation, throwingMcpExceptionwithHEADER_MISMATCH(-32020) per the table.VALIDATION_RESULTS.mdupdate.On the open question about invalid server-side
x-mcp-headerdefinitions, I'd ignore them during validation (with a debug log) unless you'd prefer a warning. Could you assign this to me?