diff --git a/src/server/mcp/server.ts b/src/server/mcp/server.ts index bc5f858..806d1d5 100644 --- a/src/server/mcp/server.ts +++ b/src/server/mcp/server.ts @@ -143,6 +143,11 @@ export function createOpenSeoMcpServer(authProps: McpProps) { ], }, { + // The tool list is fixed per request and no list_changed notification + // is ever published, so don't advertise the capability — modern clients + // use it to decide whether to open a subscriptions/listen stream. + // Without the pre-declaration, registerTool defaults it to true. + capabilities: { tools: { listChanged: false } }, instructions: "OpenSEO research tools use credits. Proceed with normal focused research, but ask the user for confirmation before planned batches over 2,000 credits.", }, diff --git a/src/server/mcp/transport.ts b/src/server/mcp/transport.ts index de1cd02..42faef3 100644 --- a/src/server/mcp/transport.ts +++ b/src/server/mcp/transport.ts @@ -128,6 +128,12 @@ function createRequestHandler( route: MCP_ROUTE, allowedOriginHostnames, legacy: "reject", + // MCP serving is strictly stateless: no notification is ever published, + // so refuse subscriptions/listen outright (in-band -32603 before the + // ack). The SSE streams it would otherwise hold open pin isolates for + // hours and turn every isolate death into a burst of exceededMemory + // request outcomes (EVE-95). + maxSubscriptions: 0, }); return async (request: Request, env: unknown, ctx: ExecutionContext) => {