From cce793927a150ca71fb211cac48361233978b56b Mon Sep 17 00:00:00 2001 From: KooshaPari Date: Sat, 12 Sep 2026 21:35:34 -0700 Subject: [PATCH] fix: prevent double-prefixed McpError message when reconstructing from wire (fixes #2786) McpError's constructor prefixes messages with 'MCP error NNNN: ', and the server serializes this prefixed message as error.message. When the client reconstructs the error via fromError, the already-prefixed message gets the prefix added again, resulting in messages like: 'MCP error -32601: MCP error -32601: Unknown tool: nope' Fixed by stripping the prefix in fromError before passing to the constructor, so deserialized errors get exactly one prefix. Also changed the queued-response path in protocol.ts to use fromError consistently instead of new McpError. --- src/shared/protocol.ts | 2 +- src/types.ts | 17 +++++++++++++++-- test/shared/protocol.test.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/shared/protocol.ts b/src/shared/protocol.ts index 2637be65bc..2b5f9bd7dd 100644 --- a/src/shared/protocol.ts +++ b/src/shared/protocol.ts @@ -892,7 +892,7 @@ export abstract class Protocol { }); }); + describe('McpError.fromError should not double-prefix', () => { + it('should not double-prefix when message already has MCP error prefix', () => { + // Simulate the message as it arrives from the wire (already prefixed by server's McpError constructor) + const prefixedMessage = 'MCP error -32601: Unknown tool: nope'; + const error = McpError.fromError(ErrorCode.MethodNotFound, prefixedMessage); + + // Should have exactly one prefix, not two + expect(error.message).toBe('MCP error -32601: Unknown tool: nope'); + expect(error.message).not.toContain('MCP error -32601: MCP error'); + }); + + it('should add prefix when message does not have it', () => { + // Direct usage without prefix (e.g., internal code creating an error) + const error = McpError.fromError(ErrorCode.InternalError, 'Something went wrong'); + + // Should have the prefix added + expect(error.message).toBe('MCP error -32603: Something went wrong'); + }); + + it('should handle negative error codes correctly', () => { + const prefixedMessage = 'MCP error -32600: Invalid request'; + const error = McpError.fromError(ErrorCode.InvalidRequest, prefixedMessage); + expect(error.message).toBe('MCP error -32600: Invalid request'); + }); + }); + describe('Response and error message routing integration', () => { it('should handle mixed response and error messages in queue', async () => { await protocol.connect(transport);