Skip to content

mcp: reject x-mcp-header annotations not reachable through properties - #1341

Open
swayyaam wants to merge 1 commit into
modelcontextprotocol:mainfrom
swayyaam:fix/header-annotations-nested
Open

swayyaam wants to merge 1 commit into
modelcontextprotocol:mainfrom
swayyaam:fix/header-annotations-nested

Conversation

@swayyaam

@swayyaam swayyaam commented Oct 5, 2026

Copy link
Copy Markdown

The spec only allows x-mcp-header on a property you can reach from the schema root through properties keys. validateParamHeaderAnnotations only ever looked at properties, because headerSchemaProperty doesn't decode anything else. So an annotation under items, oneOf/anyOf/allOf, not, if/then/else or a $ref target was silently accepted and then ignored.

The fix walks the decoded schema and rejects any x-mcp-header found under a subschema keyword (items, prefixItems, additionalProperties, the composition and conditional keywords, $defs, and so on). I went with walking the decoded JSON instead of adding typed fields to headerSchemaProperty, because a boolean like "additionalProperties": false would make the typed decode fail, which would quietly turn off validation and header extraction for that tool.

Behavior change: Server.AddTool now panics for these schemas, same as it already does for other invalid annotations. On the client side, ClientSession.ListTools drops them through filterValidTools.

Tests: 7 new rejection cases in TestValidateToolParamHeaders (items, oneOf, anyOf, allOf, not, if/then/else, $defs via $ref) plus one case where a valid nested properties annotation sits next to unannotated subschemas and is still accepted. The 7 fail without the fix and pass with it. I also reverted the fix after writing it to confirm they go red again. go test ./... and go vet ./... are clean.

Fixes #1250

I used Claude Code to help find and fix this; I ran the tests and reviewed every line myself.

The 2026-07-28 Streamable HTTP spec only allows x-mcp-header on a
property reached from the schema root through "properties" keys. An
annotation under items, a composition or conditional keyword, or a $ref
target makes the tool definition invalid.

validateParamHeaderAnnotations only looked at "properties", because
headerSchemaProperty decodes nothing else. An annotation anywhere else
was never seen, so Server.AddTool accepted the tool and the annotation
was silently dropped.

Walk the subschema keywords as well and report any x-mcp-header found
under them. The walk uses the decoded JSON rather than new fields on
headerSchemaProperty, since keywords like additionalProperties can be
booleans and a typed field would make the whole decode fail. With this
change Server.AddTool panics for such a tool, as it already does for
the other invalid annotations, and ClientSession.ListTools drops it.

New TestValidateToolParamHeaders cases cover items, oneOf, anyOf,
allOf, not, if/then/else and a $defs entry reached through $ref; all
seven fail without the change. Another case checks that a nested
properties annotation next to unannotated subschemas is still accepted.

Fixes modelcontextprotocol#1250

Signed-off-by: Swayam Mishra <swayyaam@gmail.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Streamable HTTP: an x-mcp-header in a non-reachable schema position (items, oneOf, $ref, ...) is silently accepted instead of rejected

1 participant