diff --git a/src/routes/mcpDemux.ts b/src/routes/mcpDemux.ts index b851c3e..f742e71 100644 --- a/src/routes/mcpDemux.ts +++ b/src/routes/mcpDemux.ts @@ -25,11 +25,39 @@ export async function mcpDemuxRoutes(app: FastifyInstance) { const { customer_id } = req.params; + const body = req.body as { + method: string; + params?: unknown; + id?: string | number; + }; + + // MCP Streamable-HTTP: notifications must return 202 empty, never forward. + // notifications have no id; forwardMcpCall would hang waiting for a response. + if (body.method?.startsWith("notifications/")) { + return reply.code(202).send(); + } + + // Answer initialize locally — avoids a WS round-trip per LiteLLM tool call + // and makes the handshake robust to transient extension disconnects. + if (body.method === "initialize") { + reply.header("Mcp-Session-Id", randomUUID()); + reply.header("Content-Type", "application/json"); + return reply.send({ + jsonrpc: "2.0", + id: body.id ?? null, + result: { + protocolVersion: "2024-11-05", + capabilities: { tools: {} }, + serverInfo: { name: "sitebridge-demux", version: "1.0.0" }, + }, + }); + } + if (!isCustomerOnline(customer_id)) { // Return a JSON-RPC error in MCP format return reply.code(200).send({ jsonrpc: "2.0", - id: (req.body as { id?: unknown })?.id ?? null, + id: body.id ?? null, error: { code: -32001, message: "tool_unavailable: customer not online", @@ -37,12 +65,6 @@ export async function mcpDemuxRoutes(app: FastifyInstance) { }); } - const body = req.body as { - method: string; - params?: unknown; - id?: string | number; - }; - const callId = body.id ?? randomUUID(); try { @@ -52,6 +74,7 @@ export async function mcpDemuxRoutes(app: FastifyInstance) { config.mcpBridgeTimeoutMs ); + reply.header("Content-Type", "application/json"); return reply.send({ jsonrpc: "2.0", id: callId,