Skip to content

Validate SEP-2243 Mcp-Param-{Name} headers in the modern server #1199

Description

@chemicL

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:

  1. 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.

  2. 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

Activity

  1. added this to the 2.2.0-M2 milestone on Oct 20, 2026
  2. added
    enhancementNew feature or request
    P1Significant bug affecting many users, highly requested feature
    on Oct 20, 2026
  3. prathamgoyal787 commented on Oct 20, 2026

    @prathamgoyal787

    Hi @chemicL, I'd like to work on this one. Plan, following the proposal:

    1. 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.
    2. Validate in ToolsFeature after find and before input-schema validation, throwing McpException with HEADER_MISMATCH (-32020) per the table.
    3. 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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Significant bug affecting many users, highly requested featurearea/serverenhancementNew feature or request

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions