Skip to content

feat(tools): check unknown-tool protocol errors and id correlation - #508

Open
astrogilda wants to merge 1 commit into
modelcontextprotocol:mainfrom
astrogilda:tools-call-protocol-error
Open

astrogilda wants to merge 1 commit into
modelcontextprotocol:mainfrom
astrogilda:tools-call-protocol-error

Conversation

@astrogilda

Copy link
Copy Markdown

tools-call-protocol-error calls a tool the server does not have and reads the raw response frame. It catches an unknown tool answered with a CallToolResult carrying isError: true and no JSON-RPC error response, and a request id echoed with the wrong type.

The Tools page lists unknown tools under protocol errors, and the base protocol page makes the id echo a MUST for error and result responses.

SDK commit 2025-11-25 2026-07-28
typescript-sdk 60321700 9/9 pass 9/9 pass
python-sdk 6affe5c0 2/8 pass, 6 fail, 1 warning 2/8 pass, 6 fail, 1 warning

The ToolError raised at tool_manager.py:85 is folded into an isError result at server.py:437, which typescript-sdk changed in 2.0.0-alpha.1 (#1389), and the five dependent checks report "Not testable" per #248.

The vendored everything-server pins SDK 1.29, which folds the same way, so this change guards its tools/call handler and throws the McpError first. npm test passes 624/624. The negative fixture tools-call-unknown-tool-as-result.ts carries a vitest case for the FAILURE.

The probes are the wire half of two reject vectors in agent-evidence-vectors (a8-error-response-has-no-preimage, a4-duplicate-member); the corpus runs against any verifier from one command:

uvx agent-evidence-vectors --verifier './verifier --json'
{"jsonrpc": "2.0", "id": 4242, "result": {"content": [{"type": "text", "text": "Unknown tool: conformance_tool_that_does_not_exist"}], "isError": true}}

…lation

Add the tools-call-protocol-error server scenario. It calls a tool the
server does not have, once with a numeric and once with a string request
id, and then tools/list with a string id, and checks eight things on the
raw frames: the unknown tool comes back as a JSON-RPC error response and
not as a CallToolResult with isError, the error code is the -32602 the
specification example uses (WARNING), error.code is an integer,
error.message is a string, an error response carries no result member,
and both error and result responses echo the request id with its type
intact.

The everything-server pins SDK 1.x, whose McpServer folds its own
InvalidParams error for an unknown tool into an isError result. Guard
the tools/call handler so the protocol error reaches the wire, the way
the 2.x SDK already does. A negative fixture answers unknown tools with
isError and stringifies ids; its vitest case asserts the FAILURE, the
WARNING, and the not-testable reporting of the dependent checks.
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.

1 participant