From d66d76e3c66227121a891585f3c0beca74452fa4 Mon Sep 17 00:00:00 2001 From: kai-agent-free Date: Fri, 13 Mar 2026 11:30:51 +0000 Subject: [PATCH 1/2] fix: call onerror for nested catch blocks in handlePostRequest In handlePostRequest, two nested try/catch blocks (JSON parsing and JSON-RPC schema validation) were creating error responses via createJsonErrorResponse but not calling this.onerror, silently swallowing the errors. The outer catch correctly called this.onerror. Add this.onerror?.(error as Error) calls to both nested catches, consistent with the pattern used elsewhere in the codebase. Add tests verifying onerror is called for both error cases. Fixes modelcontextprotocol/typescript-sdk#1395 --- packages/server/src/server/streamableHttp.ts | 6 ++-- .../server/test/server/streamableHttp.test.ts | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server/streamableHttp.ts b/packages/server/src/server/streamableHttp.ts index 74e689892..a8d5d11dc 100644 --- a/packages/server/src/server/streamableHttp.ts +++ b/packages/server/src/server/streamableHttp.ts @@ -635,7 +635,8 @@ export class WebStandardStreamableHTTPServerTransport implements Transport { if (options?.parsedBody === undefined) { try { rawMessage = await req.json(); - } catch { + } catch (error) { + this.onerror?.(error as Error); return this.createJsonErrorResponse(400, -32_700, 'Parse error: Invalid JSON'); } } else { @@ -649,7 +650,8 @@ export class WebStandardStreamableHTTPServerTransport implements Transport { messages = Array.isArray(rawMessage) ? rawMessage.map(msg => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(rawMessage)]; - } catch { + } catch (error) { + this.onerror?.(error as Error); return this.createJsonErrorResponse(400, -32_700, 'Parse error: Invalid JSON-RPC message'); } diff --git a/packages/server/test/server/streamableHttp.test.ts b/packages/server/test/server/streamableHttp.test.ts index ab6f22342..5987c819f 100644 --- a/packages/server/test/server/streamableHttp.test.ts +++ b/packages/server/test/server/streamableHttp.test.ts @@ -333,6 +333,38 @@ describe('Zod v4', () => { expectErrorResponse(errorData, -32_700, /Parse error.*Invalid JSON/); }); + it('should call onerror when receiving invalid JSON', async () => { + const errors: Error[] = []; + transport.onerror = (error) => errors.push(error); + + const request = new Request('http://localhost/mcp', { + method: 'POST', + headers: { + Accept: 'application/json, text/event-stream', + 'Content-Type': 'application/json' + }, + body: 'not valid json' + }); + const response = await transport.handleRequest(request); + + expect(response.status).toBe(400); + expect(errors.length).toBe(1); + }); + + it('should call onerror when receiving invalid JSON-RPC message', async () => { + sessionId = await initializeServer(); + const errors: Error[] = []; + transport.onerror = (error) => errors.push(error); + + const request = createRequest('POST', { invalid: 'not a jsonrpc message' } as unknown as JSONRPCMessage, { sessionId }); + const response = await transport.handleRequest(request); + + expect(response.status).toBe(400); + const errorData = await response.json(); + expectErrorResponse(errorData, -32_700, /Parse error.*Invalid JSON-RPC/); + expect(errors.length).toBe(1); + }); + it('should accept notifications without session and return 202', async () => { sessionId = await initializeServer(); From 49d2573b048143362157c367753e98309ff9e6ad Mon Sep 17 00:00:00 2001 From: kai-agent-free Date: Fri, 13 Mar 2026 12:30:13 +0000 Subject: [PATCH 2/2] fix: format streamableHttp test with prettier and add changeset --- .changeset/fix-onerror-nested-catch.md | 5 +++++ packages/server/test/server/streamableHttp.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-onerror-nested-catch.md diff --git a/.changeset/fix-onerror-nested-catch.md b/.changeset/fix-onerror-nested-catch.md new file mode 100644 index 000000000..300730432 --- /dev/null +++ b/.changeset/fix-onerror-nested-catch.md @@ -0,0 +1,5 @@ +--- +'@modelcontextprotocol/server': patch +--- + +Fix nested try-catch in StreamableHTTP transport onerror handler to prevent unhandled exceptions from bubbling up. diff --git a/packages/server/test/server/streamableHttp.test.ts b/packages/server/test/server/streamableHttp.test.ts index 5987c819f..224c5d1c1 100644 --- a/packages/server/test/server/streamableHttp.test.ts +++ b/packages/server/test/server/streamableHttp.test.ts @@ -335,7 +335,7 @@ describe('Zod v4', () => { it('should call onerror when receiving invalid JSON', async () => { const errors: Error[] = []; - transport.onerror = (error) => errors.push(error); + transport.onerror = error => errors.push(error); const request = new Request('http://localhost/mcp', { method: 'POST', @@ -354,7 +354,7 @@ describe('Zod v4', () => { it('should call onerror when receiving invalid JSON-RPC message', async () => { sessionId = await initializeServer(); const errors: Error[] = []; - transport.onerror = (error) => errors.push(error); + transport.onerror = error => errors.push(error); const request = createRequest('POST', { invalid: 'not a jsonrpc message' } as unknown as JSONRPCMessage, { sessionId }); const response = await transport.handleRequest(request);