Skip to content

fix(server): send protocol errors on the wire with a single "MCP error <code>:" prefix (#659) - #662

Merged
debugmcpdev merged 5 commits into
mainfrom
fix/659-doubled-mcp-error-prefix
Sep 2, 2026
Merged

fix(server): send protocol errors on the wire with a single "MCP error <code>:" prefix (#659)#662
debugmcpdev merged 5 commits into
mainfrom
fix/659-doubled-mcp-error-prefix

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Summary

Closes #659.

Every McpError thrown from a request handler reached SDK clients as MCP error -32602: MCP error -32602: <reason>. The SDK's Protocol copies the thrown error's .message verbatim into the JSON-RPC error, and the client-side McpError constructor prefixes MCP error <code>: again. The dev proxy has been hiding this with dedupeMcpErrorPrefix, which is why it was easy to miss.

Fix

Convert at the JSON-RPC boundary instead of at every throw site:

  • WireMcpError (src/errors/debug-errors.ts) is an McpError whose .message is the bare detail; class, .code, .data and stack are preserved. mcpErrorDetail() recovers the detail (a DebugError's recorded detail, otherwise .message with exactly one own-code prefix stripped) and toWireError() converts.
  • tools/call (tool-dispatch.ts) rethrows through toWireError; the prompt and resource handlers throw WireMcpError directly.
  • Upstream code keeps throwing McpError / DebugError, whose prefixed .message is right in logs, and every existing instanceof / .code / message-contains test holds unchanged.

Tests

  • New tests/core/unit/server/mcp-wire-errors.test.ts drives the real handlers through an SDK Server/Client pair over InMemoryTransport and pins the exact client-side message (one prefix, then the reason) for: tools/call argument validation, an unknown tool, a non-McpError handler rejection wrapped as InternalError, an unknown prompt, and an unknown resource (read + subscribe).
  • Unit coverage for mcpErrorDetail / WireMcpError / toWireError in debug-errors.test.ts.

npm run typecheck:all, npm run lint, and the full server unit directory pass; the test ratchet is unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DP1W22KJbkHZi4ueAtMFPF

CI Bot and others added 2 commits September 2, 2026 16:38
…r <code>:" prefix (#659)

The SDK's Protocol copies a thrown error's .message verbatim into the
JSON-RPC error and the client-side McpError constructor prefixes
"MCP error <code>: " again, so every McpError thrown from a request
handler reached SDK clients doubled ("MCP error -32602: MCP error
-32602: …"). The dev proxy hid it with dedupeMcpErrorPrefix.

Convert at the JSON-RPC boundary instead of at every throw site:
WireMcpError is an McpError whose .message is the bare detail (class,
code, data and stack preserved), and tools/call rethrows through
toWireError; the prompt and resource handlers throw it directly. Upstream
code keeps throwing McpError/DebugError, whose prefixed .message is right
in logs, and the existing instanceof/code/message-contains tests hold.

Adds an in-memory Server/Client test pinning the exact client-side
message for tools/call argument validation, an unknown tool, a wrapped
internal error, an unknown prompt and an unknown resource.

Closes #659

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP1W22KJbkHZi4ueAtMFPF
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP1W22KJbkHZi4ueAtMFPF
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit e6bfeba into main Sep 2, 2026
8 checks passed
@debugmcpdev
debugmcpdev deleted the fix/659-doubled-mcp-error-prefix branch September 2, 2026 17:23
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.

Thrown McpErrors reach SDK clients with a doubled "MCP error -32602: MCP error -32602: …" prefix

1 participant