mcp: fix ListTools panic and null normalization on malformed tools - #1120
Merged
guglielmo-san merged 2 commits intoJul 30, 2026
Merged
Conversation
filterValidTools panicked with a nil-pointer dereference when a server returned a malformed "tools":[null] response, and normalized a "tools":null response into a non-nil empty slice. Guard nil tool elements (log and exclude them) so untrusted server input can no longer crash the client, and preserve a nil tools slice as nil instead of laundering it into a non-nil empty slice. Fixes modelcontextprotocol#1119
Comment on lines
+262
to
+266
| // Preserve a nil slice as nil rather than normalizing a malformed | ||
| // "tools":null response into a non-nil empty slice. | ||
| if tools == nil { | ||
| return nil | ||
| } |
Contributor
There was a problem hiding this comment.
I don't think this code is necessary. Returning an empty list or nil does not change anything from the user point of view
Contributor
Author
There was a problem hiding this comment.
Good point — nil vs an empty slice isn't observable to callers, so I've dropped that guard. The nil-tool skip that actually fixes the tools:[null] panic stays. Done in b73df06.
Comment on lines
+974
to
+976
| func TestFilterValidToolsNilHandling(t *testing.T) { | ||
| // A malformed "tools":[null] response must not panic the client, and a | ||
| // "tools":null response must not be normalized into a non-nil empty slice. |
Contributor
There was a problem hiding this comment.
can you merge this test with the existing FilterValidTools ?
Contributor
Author
There was a problem hiding this comment.
Done — merged the nil-handling checks into TestFilterValidTools and removed the separate test (b73df06).
- Drop the nil-slice guard: returning nil vs an empty slice is not observable to callers, so the special case is unnecessary. The nil-*tool* guard that fixes the panic on "tools":[null] stays. - Merge the nil-handling checks into the existing TestFilterValidTools instead of a separate test function.
guglielmo-san
approved these changes
Jul 30, 2026
Contributor
|
Thank you for the contribution! |
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.
Fixes #1119.
ClientSession.ListToolspost-processes the server response throughfilterValidTools, which has two problems on malformed input:"tools":[null]. Anil*Toolelement reachesvalidateParamHeaderAnnotations(tool), which dereferencestool.InputSchema, crashing the client on untrusted server input."tools":nullis normalized into a non-nil empty slice.make([]*Tool, 0, len(tools))is always non-nil, so a nilToolsslice from a malformed response is silently laundered into valid-looking data.Reproduction:
Fix (
mcp/streamable_headers.go):"tools":[null]can no longer crash the client. This is consistent with the function’s existing contract of excluding invalid tools.nilfor a nil input slice so a"tools":nullresponse is preserved as nil rather than normalized into an empty slice.Added
TestFilterValidToolsNilHandlingcovering both defects plus the mixed valid/nil case.go test ./mcp/passes;gofmtandgo vetare clean.Note on scope: this keeps the change minimal and within
filterValidTools’s existing signature. If maintainers would rather surface a hard protocol error for a malformed"tools":null/"tools":[null]response at theListToolslevel (rather than tolerating and filtering it), I’m happy to follow up with that larger change.