You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Validate SEP-2243 Mcp-Param-{Name} headers in the modern server #1199
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.
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.HttpServletMcpTransport collects every Mcp-Param-* header into a TransportParams value:
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 TransportParams in the McpTransportContext under a key defined by the SDK.
Validate after the payload is parsed. In ToolsFeature, after find returns the Tool and before the
tool runs, read TransportParams from the transport context and check it against the tools/call
arguments, using the parameters inputSchema marks with x-mcp-header.
Requirements for the transport context key
The transport sets the key after the user's contextExtractor runs, 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-header definitions. The spec's rules (non-empty, RFC 9110 token, unique
ignoring case, only string/integer/boolean, integers in the JS safe range, statically reachable) are a
client MUST: clients drop invalid tools from tools/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 same x-mcp-header parser.
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 properties paths; annotations behind items, allOf or $ref
are ignored
ToolsFeature tests: one per row of the table above, plus 42.0 vs 42, boolean encoding, and stdio
skipping validation
Integration test: 400 / -32020 end to end through HttpServletMcpTransport
A fixture tool with an x-mcp-header parameter in conformance-tests/server-servlet-modern; http-custom-header-server-validation passes and --requirements 2026-07-28 shows no regressions
The Known Limitations entry in conformance-tests/VALIDATION_RESULTS.md is updated
Hi @chemicL, I'd like to work on this one. Plan, following the proposal:
Extract Mcp-Param-* headers into TransportParams in HttpServletMcpTransport (case-insensitive names, =?base64?…?= decoding, malformed values flagged rather than rejected), set after the user's contextExtractor, with an explicit "not mirrored" value for stdio.
Validate in ToolsFeature after find and before input-schema validation, throwing McpException with HEADER_MISMATCH (-32020) per the table.
The unit, integration and conformance tests from the acceptance criteria, plus the VALIDATION_RESULTS.md update.
On the open question about invalid server-side x-mcp-header definitions, I'd ignore them during validation (with a debug log) unless you'd prefer a warning. Could you assign this to me?
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?