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
Streamable HTTP: an x-mcp-header in a non-reachable schema position (items, oneOf, $ref, ...) is silently accepted instead of rejected #1250
validateParamHeaderAnnotations (run from Server.AddTool, mcp/server.go:354) only inspects properties. Its schema struct headerSchemaProperty (mcp/streamable_headers.go:57) parses only type, x-mcp-header, and properties, so an x-mcp-header annotation placed under items, oneOf, anyOf, allOf, not, if/then/else, or $ref is never seen. AddTool accepts the tool with no error and the annotation is silently dropped: no Mcp-Param-* header is ever generated for it.
The SDK already rejects the other invalid x-mcp-header cases at registration (non-primitive type, invalid token syntax, duplicate token), so the non-reachable placement is the remaining gap.
Per the 2026-07-28 Streamable HTTP spec (Standard Request Headers, Schema Extension), an x-mcp-header MUST only be applied to a property that is statically reachable from the schema root via a chain consisting solely of properties keys; the chain MUST NOT pass through items, composition keywords (oneOf, anyOf, allOf, not), conditional keywords (if/then/else), or $ref. An annotation anywhere else makes the annotation, and thus the tool definition, invalid. So such a tool should be rejected, not served with the annotation silently ignored.
To Reproduce
package main
import (
"context""fmt""github.com/modelcontextprotocol/go-sdk/mcp"
)
funcmain() {
s:=mcp.NewServer(&mcp.Implementation{Name: "s", Version: "0"}, nil)
h:=func(context.Context, *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return&mcp.CallToolResult{}, nil
}
// x-mcp-header under array `items`: not statically reachable via `properties`.s.AddTool(&mcp.Tool{
Name: "tag",
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"tags": map[string]any{
"type": "array",
"items": map[string]any{"type": "string", "x-mcp-header": "Tag"},
},
},
},
}, h)
fmt.Println("AddTool returned without rejecting the invalid annotation")
}
AddTool returns normally. The same holds for oneOf/anyOf/allOf/$ref placements. A valid top-level or nested-properties annotation is still handled correctly, so only the non-reachable placements are silently swallowed.
Expected behavior
AddTool rejects the tool, as it already does for the other invalid x-mcp-header cases, so the author gets a signal instead of shipping a tool whose annotation every conforming client drops.
Additional context
headerSchemaProperty would need to also parse items/oneOf/anyOf/allOf/not/if/then/else/$ref so the validator can detect an annotation in a non-reachable position and reject it.
Related (separate, happy to file if useful): the client has no tools/list filter that excludes a tool carrying an invalid x-mcp-header, which the spec lists as a client MUST.
If the direction is welcome I can put up a PR with the validation extension and a regression test.
Hi, I'd like to work on this issue if the author is not already implementing a fix.
I checked the current validation path in mcp/streamable_headers.go. My proposed scope is to detect x-mcp-header annotations in disallowed schema positions, reject invalid tool definitions at registration, and preserve the behavior of valid top-level and nested properties annotations. I would add focused regression tests for invalid placements and valid annotations, including checks that the new regressions fail before the fix.
Since this issue is not labeled help wanted, I'll wait for maintainer confirmation before implementing or opening a PR, as recommended by the contribution guide. Would you be happy for me to take this on? If the author already has work underway, I'm happy to coordinate to avoid duplication.
I can confirm this on current main. headerSchemaProperty in mcp/streamable_headers.go only decodes type, x-mcp-header and properties, so validateParamHeadersIn never sees an annotation under items, oneOf/anyOf/allOf, not, if/then/else or $ref. Calling validateParamHeaderAnnotations directly on schemas with the annotation under items, oneOf and $ref returns nil for all three.
One small correction to the report: Server.AddTool doesn't return an error on invalid annotations, it panics (AddTool "t": invalid parameter header annotations: ...). So once these placements are rejected, AddTool will panic for them too. On the client side, ClientSession.ListTools runs the same check through filterValidTools, which logs the tool and drops it from the result, so a fix would also make clients drop tools like this.
The fix could be covered by new cases in TestValidateToolParamHeaders (mcp/streamable_headers_test.go), each expecting an error:
annotation under items of an array property
annotation inside a oneOf / anyOf / allOf branch
annotation under not and under if / then / else
annotation in a $defs entry reached through $ref
plus a case confirming that an annotation on a nested properties chain is still accepted.
@jjj-n are you still planning to take this? If not, and a maintainer is OK with it, I have a fix with tests ready to send.
Please go ahead — I never started an implementation and was still waiting on maintainer confirmation since the issue is not labeled help wanted. Your analysis of headerSchemaProperty and the proposed test cases are solid, so it makes sense for this to move forward with your ready fix rather than sit unclaimed. Good luck with the PR!
Activity
jjj-n commentedon Sep 10, 2026
Hi, I'd like to work on this issue if the author is not already implementing a fix.
I checked the current validation path in
mcp/streamable_headers.go. My proposed scope is to detectx-mcp-headerannotations in disallowed schema positions, reject invalid tool definitions at registration, and preserve the behavior of valid top-level and nestedpropertiesannotations. I would add focused regression tests for invalid placements and valid annotations, including checks that the new regressions fail before the fix.Since this issue is not labeled
help wanted, I'll wait for maintainer confirmation before implementing or opening a PR, as recommended by the contribution guide. Would you be happy for me to take this on? If the author already has work underway, I'm happy to coordinate to avoid duplication.swayyaam commentedon Oct 3, 2026
I can confirm this on current main.
headerSchemaPropertyinmcp/streamable_headers.goonly decodestype,x-mcp-headerandproperties, sovalidateParamHeadersInnever sees an annotation underitems,oneOf/anyOf/allOf,not,if/then/elseor$ref. CallingvalidateParamHeaderAnnotationsdirectly on schemas with the annotation underitems,oneOfand$refreturns nil for all three.One small correction to the report:
Server.AddTooldoesn't return an error on invalid annotations, it panics (AddTool "t": invalid parameter header annotations: ...). So once these placements are rejected,AddToolwill panic for them too. On the client side,ClientSession.ListToolsruns the same check throughfilterValidTools, which logs the tool and drops it from the result, so a fix would also make clients drop tools like this.The fix could be covered by new cases in
TestValidateToolParamHeaders(mcp/streamable_headers_test.go), each expecting an error:itemsof an array propertyoneOf/anyOf/allOfbranchnotand underif/then/else$defsentry reached through$refplus a case confirming that an annotation on a nested
propertieschain is still accepted.@jjj-n are you still planning to take this? If not, and a maintainer is OK with it, I have a fix with tests ready to send.
jjj-n commentedon Oct 5, 2026
Please go ahead — I never started an implementation and was still waiting on maintainer confirmation since the issue is not labeled
help wanted. Your analysis ofheaderSchemaPropertyand the proposed test cases are solid, so it makes sense for this to move forward with your ready fix rather than sit unclaimed. Good luck with the PR!swayyaam commentedon Oct 5, 2026
Thanks @jjj-n. PR is up in #1341. Maintainers, happy to adjust or hold if you'd rather approach this differently.