Describe the bug
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"
)
func main() {
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.
Describe the bug
validateParamHeaderAnnotations(run fromServer.AddTool,mcp/server.go:354) only inspectsproperties. Its schema structheaderSchemaProperty(mcp/streamable_headers.go:57) parses onlytype,x-mcp-header, andproperties, so anx-mcp-headerannotation placed underitems,oneOf,anyOf,allOf,not,if/then/else, or$refis never seen.AddToolaccepts the tool with no error and the annotation is silently dropped: noMcp-Param-*header is ever generated for it.The SDK already rejects the other invalid
x-mcp-headercases 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-headerMUST only be applied to a property that is statically reachable from the schema root via a chain consisting solely ofpropertieskeys; the chain MUST NOT pass throughitems, 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
AddToolreturns normally. The same holds foroneOf/anyOf/allOf/$refplacements. A valid top-level or nested-propertiesannotation is still handled correctly, so only the non-reachable placements are silently swallowed.Expected behavior
AddToolrejects the tool, as it already does for the other invalidx-mcp-headercases, so the author gets a signal instead of shipping a tool whose annotation every conforming client drops.Additional context
headerSchemaPropertywould need to also parseitems/oneOf/anyOf/allOf/not/if/then/else/$refso 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/listfilter that excludes a tool carrying an invalidx-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.