diff --git a/src/routes/api/mcp/+server.ts b/src/routes/api/mcp/+server.ts index 5c4cb83e..cabe1a42 100644 --- a/src/routes/api/mcp/+server.ts +++ b/src/routes/api/mcp/+server.ts @@ -39,7 +39,12 @@ export const POST: RequestHandler = async (event) => { try { const { request } = event; - const body = await request.json(); + let body: any; + try { + body = await request.json(); + } catch { + return json({ error: 'Invalid or missing JSON body' }, { status: 400 }); + } const { instanceId, mcpId, config, npmPackage, defaultArgs, envVars, command, args } = body as { instanceId: string; mcpId?: string; @@ -150,7 +155,12 @@ export const DELETE: RequestHandler = async (event) => { try { const { request } = event; - const body = await request.json(); + let body: any; + try { + body = await request.json(); + } catch { + return json({ error: 'Invalid or missing JSON body' }, { status: 400 }); + } const { instanceId } = body as { instanceId: string }; if (!instanceId) { diff --git a/src/routes/api/mcp/call/+server.ts b/src/routes/api/mcp/call/+server.ts index 818d9833..b332601e 100644 --- a/src/routes/api/mcp/call/+server.ts +++ b/src/routes/api/mcp/call/+server.ts @@ -19,7 +19,12 @@ export const POST: RequestHandler = async (event) => { try { const { request } = event; - const body = await request.json(); + let body: any; + try { + body = await request.json(); + } catch { + return json({ error: 'Invalid or missing JSON body' }, { status: 400 }); + } const { instanceId, toolName, args, requestId } = body as { instanceId: string; toolName: string; diff --git a/src/routes/api/mcp/mcp.integration.test.ts b/src/routes/api/mcp/mcp.integration.test.ts index 7e6a585c..6a7c5fc5 100644 --- a/src/routes/api/mcp/mcp.integration.test.ts +++ b/src/routes/api/mcp/mcp.integration.test.ts @@ -149,4 +149,36 @@ describe('/api/mcp', () => { expect(body.authority.reasonCodes).toContain('native_governor_required'); expect(connectMCP).not.toHaveBeenCalled(); }); + + it('returns 400 for empty request body instead of 500', async () => { + const response = await POST({ + request: new Request('http://127.0.0.1/api/mcp', { + method: 'POST', + headers: { 'content-type': 'application/json', 'x-api-key': 'mcp-test-secret' }, + body: '' + }), + url: new URL('http://127.0.0.1/api/mcp'), + getClientAddress: () => '127.0.0.1' + } as never); + + expect(response.status).toBe(400); + expect(await response.json()).toMatchObject({ error: expect.stringContaining('JSON body') }); + expect(connectMCP).not.toHaveBeenCalled(); + }); + + it('returns 400 for malformed JSON body instead of 500', async () => { + const response = await POST({ + request: new Request('http://127.0.0.1/api/mcp', { + method: 'POST', + headers: { 'content-type': 'application/json', 'x-api-key': 'mcp-test-secret' }, + body: '{not valid json' + }), + url: new URL('http://127.0.0.1/api/mcp'), + getClientAddress: () => '127.0.0.1' + } as never); + + expect(response.status).toBe(400); + expect(await response.json()).toMatchObject({ error: expect.stringContaining('JSON body') }); + expect(connectMCP).not.toHaveBeenCalled(); + }); });