{{ message }}
Repository navigation
feat: validate SEP-2243 Mcp-Param headers in modern server - #1202
Open
prathamgoyal787 wants to merge 2 commits into
Open
prathamgoyal787 wants to merge 2 commits into
prathamgoyal787 wants to merge 2 commits into
Conversation
HttpServletMcpTransport collects every Mcp-Param-* header into TransportParams (case-insensitive names, =?base64?...?= decoding, malformed values flagged rather than failing) and attaches it to the McpTransportContext after the user's contextExtractor, so the extractor can neither drop nor forge it. StdioMcpTransport declares NOT_MIRRORED; a context without params counts as a request with no headers. ToolsFeature checks, after find and before inputSchema validation, every x-mcp-header parameter reachable through properties: a present argument needs a matching header (integers compared numerically, booleans as true/false), a null or absent argument expects none, malformed headers of declared parameters are rejected and undeclared headers are ignored. Rejections fail with -32020 (HEADER_MISMATCH), answered with 400 over HTTP. Invalid x-mcp-header definitions are ignored with a debug log. Adds the test_custom_header conformance fixture; the http-custom-header-server-validation scenario now passes (10/10). Closes modelcontextprotocol#1199
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Validates SEP-2243
Mcp-Param-{Name}headers in the modern server: for every tool parameter marked withx-mcp-header,tools/callnow checks the header the client sent against the argument in the body.Closes #1199
Motivation and Context
SEP-2243 (part of the 2026-07-28 revision) lets a tool mark parameters with
x-mcp-headerso clients copy their values intoMcp-Param-{Name}HTTP headers. Load balancers and gateways can then route on them without parsing the JSON body. The spec makes it a MUST for servers to reject a request whose headers disagree with the body, are missing while the body has a value, or are malformed. Otherwise an intermediary could route on a header the server never checked.The modern server already validates
Mcp-Method,Mcp-NameandMCP-Protocol-Version, but notMcp-Param-*. This was listed as a Known Limitation inconformance-tests/VALIDATION_RESULTS.md, andhttp-custom-header-server-validationcould not pass. The check needs the called tool'sinputSchema, which the transport doesn't have. So, as proposed in #1199:Transport:
HttpServletMcpTransportcollects the headers into a newTransportParamsvalue.=?base64?…?=form are decoded strictly: padding is required and the bytes must be valid UTF-8.Context: the value is attached to the
McpTransportContextunderTransportParams.KEYafter the user'scontextExtractorruns, so the extractor can neither drop nor forge it.stdio:
StdioMcpTransportattachesTransportParams.NOT_MIRRORED, the only value that skips validation. A context without the key counts as "no headers sent", not "skip".Validation:
ToolsFeaturevalidates afterfindand beforeinputSchemavalidation. Onlyx-mcp-headerannotations reached throughpropertiescount;items,allOfand$refare not followed.42.0=42), booleans astrue/falsenullor absent (incl. missing intermediate object)Mcp-Param-*header no parameter declaresRejections throw
McpExceptionwithErrorCodes.HEADER_MISMATCH(-32020), which the servlet answers with400.Invalid definitions: invalid server-side
x-mcp-headerdefinitions are ignored with a debug log, per the open question in Validate SEP-2243Mcp-Param-{Name}headers in the modern server #1199. Invalid means a name that is empty or not an RFC 9110 token, a type other thanstring/integer/boolean, or a case-insensitive duplicate.How Has This Been Tested?
New and extended tests:
TransportParamsTestscovers:attachoverriding a value forged by the extractorMcpParamHeadersTests: nestedpropertiespaths; annotations behinditems/allOf/$ref/$defsignored; invalid names, types and duplicates.ToolsFeatureParamHeaderTests: one test per table row, each asserting the tool andinputSchemavalidation never ran on rejection. Also42.0vs42, booleans, Base64 and literal values,NOT_MIRROREDskipping validation, and a missing key not skipping it.StdioMcpTransportTests: stdio passesNOT_MIRRORED.HttpServletMcpTransportIntegrationTests, end to end over Tomcat: matching header →200; mismatched, missing or malformed header →400/-32020.Results:
mvn test -pl mcp-core: 635 tests, 0 failures, 0 errors.mvn test -pl mcp-test: 1036 tests, 0 failures, 1 skipped. All modern-server tests pass. There were 12 errors, all Testcontainers "Could not find a valid Docker environment" in legacy client tests, because Docker wasn't available locally.@modelcontextprotocol/conformance@0.2.0-alpha.12againstconformance-tests/server-servlet-modern, with the newtest_custom_headerfixture:--scenario http-custom-header-server-validation: 10/10 passed.--requirements 2026-07-28: all 37 required scenarios pass, so no regressions. The only failures are the unscoredtasks-*extension.Breaking Changes
No public API is removed or changed. One behaviour change to be aware of: a custom transport or in-memory caller that passes an
McpTransportContextwithoutTransportParams(for exampleMcpTransportContext.EMPTY) now gets-32020when it calls a tool with anx-mcp-headerargument value. This is deliberate: #1199 requires that a missing key must not skip validation. Such transports should attachTransportParams.NOT_MIRRORED, or the parsed headers, withTransportParams.attach(...). Tools withoutx-mcp-headerare unaffected.Types of changes
Checklist
Additional context
Mcp-Param-Xheader counts as malformed.BigDecimal, so42,42.0and4.2e1all match.Mcp-Namedecoder still accepts unpadded Base64.VALIDATION_RESULTS.mdmoves the scenario to Passing and narrows Known Limitation Update the License #2 to "definitions aren't enforced at registration/listing". Theserver-servlet-modernREADME lists the new fixture.