mcp: a property with an array "type" (any pointer field from jsonschema.For) disables x-mcp-header handling for the whole tool
Maintainer thường phản hồi trong vòng 1 ngày
@jay23606 đang làm issue này rồi.
Từ ngày 16/10/2026.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 50/100
Hướng nghiên cứu
Start in mcp/streamable_headers.go: headerSchemaProperty decodes type as a string, so an array or boolean subschema makes unmarshalSchemaProperties return nil for the whole tool. Read validateParamHeaderAnnotations and the two tests named in the issue, TestExtractToolParamHeaders and TestValidateToolParamHeaders. Done when a property with an array or boolean schema no longer disables annotations on other properties, and an annotated nullable property is rejected with the existing primitive-types error, as the issue proposes. The maintainers still need to decide the nullable case.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Describe the bug
headerSchemaProperty (mcp/streamable_headers.go) decodes type into a string. JSON Schema also allows an array there, and jsonschema.For emits one for every pointer field ("type": ["null", "string"]). When any property in a tool's inputSchema has an array type, the remarshal in unmarshalSchemaProperties fails and returns nil. The client and server then treat the tool as having no x-mcp-header annotations at all, even on other properties:
- The client sends no
Mcp-Param-*headers for the tool. A server that validates them, as the 2026-07-28 spec requires, answers withHeaderMismatch. - The server skips
validateParamHeaders, so a request whoseMcp-Param-*header contradicts the body is accepted. validateParamHeaderAnnotationsreturns nil without checking anything, soAddTooland the client'stools/listfilter also accept an invalid annotation, such as one on anumberproperty.
A boolean subschema ("properties": {"extra": true}) fails the same way.
To Reproduce
A typed tool with an annotated region and an optional *string field, served over Streamable HTTP and called by the go-sdk client on 2026-07-28 (go run . with a replace to the checkout):
type withOptionalIn struct {
Region string `json:"region"`
Query string `json:"query"`
Note *string `json:"note,omitempty"` // inferred as "type": ["null", "string"]
}
s, _ := jsonschema.For[withOptionalIn](nil)
s.Properties["region"].Extra = map[string]any{"x-mcp-header": "Region"}
mcp.AddTool(server, &mcp.Tool{Name: "with_optional", InputSchema: s}, handler)
The full program registers this tool next to an identical plain tool without Note. The program serves both tools with NewStreamableHTTPHandler (stateless, TTLMs set so the client caches tools/list). It calls each tool with {"region": "eu", "query": "q"} and records the outgoing headers in a RoundTripper. Then it calls each tool again with Mcp-Param-Region overwritten to forged:
plain client sent Mcp-Param-Region="eu", call err=<nil>
with_optional client sent Mcp-Param-Region="", call err=<nil>
plain header forged to "forged" (body region=eu): call err=... header mismatch: Mcp-Param-Region header value 'forged' does not match body value: rejected by transport: Bad Request
with_optional header forged to "forged" (body region=eu): call err=<nil>
The only difference between the two tools is the extra optional field.
Expected behavior
with_optional behaves like plain: the client sends Mcp-Param-Region: eu, and the server rejects the forged header.
Proposed fix
In headerSchemaProperty, keep type as a json.RawMessage. Give the struct an UnmarshalJSON that turns true/false into an empty property, so each property decodes independently. An annotated property must still have a single primitive type. A nullable annotated property (["null", "string"]) is rejected with the existing "primitive types" error, like the TypeScript SDK's check (typeof schema.type === 'string'). The change is about 20 lines, plus table cases in TestExtractToolParamHeaders and TestValidateToolParamHeaders. If you'd rather accept ["null", <primitive>] on annotated properties (both header paths already skip null arguments), that is a small change.
The new cases fail on main and pass with the patch. With the patch, the repro output matches for both tools. go test ./..., go vet ./..., gofmt -l . and staticcheck 2026.2.2 are clean. I couldn't run v0.6.1, the CI version, because it can't read Go 1.27 export data. This is separate from #1250 / #1341, which covers annotations in positions that are not reachable through properties.
If you're happy with this direction, I can open the PR.
Additional context
- Go MCP SDK:
mainat 9012a21 go version go1.27.0 windows/amd64
This report and the patch were written with Claude Code (AI-assisted). I ran the repro and the tests above on my machine.
- Ngôn ngữ chính
- Go
- Star
- 5.3k
- Fork
- 606
- Merge trung bình
- 1 ngày 16 giờ
- Pull request đã merge (30 ngày)
- 72
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của modelcontextprotocol/go-sdk
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
modelcontextprotocol/go-sdk#1444 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Session-lifecycle bookkeeping logs at info, spamming multiple log lines per request on stateless streamable HTTPCó thể đã có người làm @anneheartrecord đã nhận 60 ngày trước. Đang mởP3
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
modelcontextprotocol/go-sdk#1204 · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Expose generic `SendNotification` on `ServerSession` for custom protocol extensionsCó thể đã có người làm @ajuijas đã nhận 226 ngày trước. Đang mởneeds investigation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
modelcontextprotocol/go-sdk#745 · 13 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
internal/json: Unmarshal allocates a 32 KiB stream buffer on every callCó thể đã có người làm @raveesh-me đã nhận 2 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 25/100
modelcontextprotocol/go-sdk#1453 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
auth/extauth: ClientCredentialsHandler does not send the resource parameterCó thể đã có người làm @shlee1223 đã nhận 2 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 25/100
modelcontextprotocol/go-sdk#1450 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của modelcontextprotocol/go-sdk
Issue tương tự
-
[doc-healer] DDUw improvement: retry integrity-filtered issue reads with direct single-item lookupsĐang mởautomation documentation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 80/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
containerd/containerd#14476 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 82/100
Infisical/infisical#8987 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
scan framework and scan control accept empty and whitespace-only policy names in Args validationCó thể đã có người làm @WKr1shna đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày