diff --git a/src/index.ts b/src/index.ts index 7b4cca9..48f21b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,6 +57,9 @@ export default { } catch { return json(rpcError(null, -32700, "parse error")); } + if (typeof body !== "object" || body === null || Array.isArray(body)) { + return json(rpcError(null, -32600, "Invalid Request")); + } const result = await handle(body, env, caller); // Notifications (no id) get a 202 with no body per JSON-RPC. diff --git a/test/index.test.mjs b/test/index.test.mjs index ef49ed2..eea909f 100644 --- a/test/index.test.mjs +++ b/test/index.test.mjs @@ -104,6 +104,26 @@ test("tools/call rejects non-object arguments", async (t) => { assert.equal(upstreamCalls, 0); }); +test("non-object JSON-RPC bodies get -32600 Invalid Request", async (t) => { + const env = { + BRIDGEKIT_CLIENTS: JSON.stringify({ + "client-key": { name: "test client", tools: [], allowWrite: false }, + }), + }; + + for (const badBody of [null, [], "foo", 42, true]) { + await t.test(`body ${JSON.stringify(badBody)}`, async () => { + const response = await mcpRequest(env, badBody); + + assert.deepEqual(await response.json(), { + jsonrpc: "2.0", + id: null, + error: { code: -32600, message: "Invalid Request" }, + }); + }); + } +}); + test("/ai rejects requests without a client key before calling the model", async () => { let upstreamCalls = 0; globalThis.fetch = async () => {