Fix MCP handler for JSON-RPC notifications

The handler now properly handles notifications (no 'id' field) by
returning 202 No Content instead of rejecting with method-not-found.
Fixes "sending notifications/initialized: Bad Request" error.

- Added notifications/initialized and notifications/cancelled handlers
- Unknown notifications silently ignored per JSON-RPC spec
- API route returns 202 for notifications

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-free
This commit is contained in:
Amansisa Tadese 2026-06-12 06:18:52 +03:00
parent 5a56e9f519
commit 8683267919
2 changed files with 31 additions and 14 deletions

View file

@ -209,7 +209,12 @@ export async function startServer(port = 9876) {
// MCP JSON-RPC endpoint // MCP JSON-RPC endpoint
if (path === "/mcp" && req.method === "POST") { if (path === "/mcp" && req.method === "POST") {
const body = await parseBody(req); const body = await parseBody(req);
const mcpResp = await handleMCP(body); const { response: mcpResp, isNotification } = await handleMCP(body);
if (isNotification || !mcpResp) {
res.writeHead(202, { "Content-Type": "application/json" });
res.end();
return;
}
const status = mcpResp.error ? 400 : 200; const status = mcpResp.error ? 400 : 200;
res.writeHead(status, { "Content-Type": "application/json" }); res.writeHead(status, { "Content-Type": "application/json" });
res.end(JSON.stringify(mcpResp)); res.end(JSON.stringify(mcpResp));

View file

@ -32,35 +32,47 @@ const TOOLS = [
export function createMCPHandler(runner) { export function createMCPHandler(runner) {
return async function handleMCP(body) { return async function handleMCP(body) {
if (!body || body.jsonrpc !== "2.0") { if (!body || body.jsonrpc !== "2.0") {
return mcpError(null, -32600, "Invalid Request: must be JSON-RPC 2.0"); return { response: mcpError(null, -32600, "Invalid Request: must be JSON-RPC 2.0"), isNotification: false };
} }
const { id, method, params } = body; const { id, method, params } = body;
const isNotification = id === undefined || id === null;
switch (method) { switch (method) {
case "tools/list": case "tools/list":
return { return {
jsonrpc: "2.0", response: { jsonrpc: "2.0", id, result: { tools: TOOLS } },
id, isNotification: false
result: { tools: TOOLS }
}; };
case "tools/call": case "tools/call":
return handleToolCall(id, params, runner); return { response: await handleToolCall(id, params, runner), isNotification: false };
case "initialize": case "initialize":
return { return {
jsonrpc: "2.0", response: {
id, jsonrpc: "2.0",
result: { id,
protocolVersion: "2024-11-05", result: {
capabilities: { tools: {} }, protocolVersion: "2024-11-05",
serverInfo: { name: "polysearch", version: "2.0.0" } capabilities: { tools: {} },
} serverInfo: { name: "polysearch", version: "2.0.0" }
}
},
isNotification: false
}; };
case "notifications/initialized":
case "notifications/cancelled":
log.debug({ method }, "notification received, no response");
return { response: null, isNotification: true };
default: default:
return mcpError(id, -32601, `Method not found: ${method}`); if (isNotification) {
log.debug({ method }, "unknown notification, silently ignored");
return { response: null, isNotification: true };
}
return { response: mcpError(id, -32601, `Method not found: ${method}`), isNotification: false };
} }
}; };
} }