diff --git a/src/mcp.ts b/src/mcp.ts index 8a4acf7..24ad9ff 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -155,6 +155,10 @@ export async function handleMcpRequest(request: Request): Promise { return jsonRpcError(null, -32700, "Parse error"); } + if (Array.isArray(body) && body.length === 0) { + return jsonRpcError(null, -32600, "Invalid Request"); + } + const requests = Array.isArray(body) ? body : [body]; const responses: unknown[] = []; diff --git a/test/mcp.test.ts b/test/mcp.test.ts new file mode 100644 index 0000000..78fa0d9 --- /dev/null +++ b/test/mcp.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from "vitest"; +import { handleMcpRequest } from "../src/mcp"; + +function postJson(body: unknown): Request { + return new Request("https://casebook.test/mcp", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); +} + +describe("handleMcpRequest batches", () => { + it("returns Invalid Request for an empty batch", async () => { + const response = await handleMcpRequest(postJson([])); + + expect(response.status).toBe(400); + expect(await response.json()).toEqual({ + jsonrpc: "2.0", + id: null, + error: { code: -32600, message: "Invalid Request" }, + }); + }); + + it("keeps non-empty notification-only batches response-free", async () => { + const response = await handleMcpRequest( + postJson([ + { jsonrpc: "2.0", method: "notifications/initialized" }, + { jsonrpc: "2.0", method: "ping" }, + ]), + ); + + expect(response.status).toBe(202); + expect(await response.text()).toBe(""); + }); +});