fix(server): return JSON-RPC errors for missing tools#1674
fix(server): return JSON-RPC errors for missing tools#1674fahe1em1 wants to merge 2 commits intomodelcontextprotocol:mainfrom
Conversation
|
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
| } catch (error) { | ||
| if (error instanceof ProtocolError && error.code === ProtocolErrorCode.UrlElicitationRequired) { | ||
| throw error; // Return the error to the caller without wrapping in CallToolResult | ||
| if (error instanceof ProtocolError) { |
There was a problem hiding this comment.
This broadens the catch from only UrlElicitationRequired to all ProtocolError instances. Before this change, a ProtocolError with any other code (e.g. MethodNotFound thrown by user code inside a tool handler) would have been caught by the outer branch and returned as a CallToolResult with isError: true. Now it becomes a JSON-RPC error response instead.
Is this intentional for all protocol error codes? For example, if a tool implementation internally calls another protocol method that throws a ProtocolError, that would now surface as a JSON-RPC error rather than a tool-level error — which changes what the client sees. The test only covers the tool not found case (-32602), but this catch clause now affects every ProtocolError subtype.
Worth documenting which ProtocolError codes are expected to reach this path, or alternatively using an allowlist (e.g. UrlElicitationRequired, InvalidParams) rather than a blanket catch.
Summary
ProtocolErrors as JSON-RPC errors instead of wrapping them intoCallToolResult.isErrorCallToolResult.isErrorwrapping for ordinary tool execution failuresRoot Cause
McpServeralready throws aProtocolErrorwhen a tool does not exist, but the catch block in thetools/callhandler was catching that protocol error and converting it into a normal tool result withisError: true.Why This Is Small And Safe
This only changes how existing
ProtocolErrors are surfaced. Tool execution errors still follow the existingCallToolResult.isErrorpath, while protocol validation and missing-tool failures now stay aligned with JSON-RPC semantics.Testing
pnpm --filter @modelcontextprotocol/server test -- streamableHttp.test.tspnpm --filter @modelcontextprotocol/server lintCloses #1510.