agents 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/{agent-tool-types-VPsjVYL0.d.ts → agent-tool-types-NofdbL9X.d.ts} +57 -4
  2. package/dist/agent-tool-types.d.ts +1 -1
  3. package/dist/{agent-tools-BGpgfpJT.d.ts → agent-tools-DLquv-dp.d.ts} +2 -2
  4. package/dist/agent-tools.d.ts +1 -1
  5. package/dist/browser/ai.d.ts +126 -7
  6. package/dist/browser/ai.js +73 -29
  7. package/dist/browser/ai.js.map +1 -1
  8. package/dist/browser/index.d.ts +81 -69
  9. package/dist/browser/index.js +3 -2
  10. package/dist/browser/tanstack-ai.d.ts +13 -7
  11. package/dist/browser/tanstack-ai.js +18 -19
  12. package/dist/browser/tanstack-ai.js.map +1 -1
  13. package/dist/chat/index.d.ts +111 -5
  14. package/dist/chat/index.js +207 -35
  15. package/dist/chat/index.js.map +1 -1
  16. package/dist/chat-sdk/index.d.ts +1 -1
  17. package/dist/{classPrivateFieldGet2-Beqsfu2Z.js → classPrivateFieldGet2-CZ7QjTXN.js} +5 -5
  18. package/dist/{classPrivateMethodInitSpec-B5ko1s2R.js → classPrivateMethodInitSpec-D-0__zd9.js} +2 -2
  19. package/dist/client.d.ts +19 -2
  20. package/dist/client.js +31 -11
  21. package/dist/client.js.map +1 -1
  22. package/dist/{compaction-helpers-BEUILPss.d.ts → compaction-helpers-DVcu5lPN.d.ts} +91 -12
  23. package/dist/connector-D6yYzYHg.js +1080 -0
  24. package/dist/connector-D6yYzYHg.js.map +1 -0
  25. package/dist/connector-DXursxV5.d.ts +340 -0
  26. package/dist/experimental/memory/session/index.d.ts +75 -12
  27. package/dist/experimental/memory/session/index.js +226 -21
  28. package/dist/experimental/memory/session/index.js.map +1 -1
  29. package/dist/experimental/memory/utils/index.d.ts +2 -2
  30. package/dist/{index-CPe1OtI0.d.ts → index-B7IbEeze.d.ts} +32 -1
  31. package/dist/index.d.ts +8 -2
  32. package/dist/index.js +116 -45
  33. package/dist/index.js.map +1 -1
  34. package/dist/mcp/client.d.ts +1 -1
  35. package/dist/mcp/index.d.ts +1 -1
  36. package/dist/mcp/index.js +1 -1
  37. package/dist/mcp/index.js.map +1 -1
  38. package/dist/observability/index.d.ts +1 -1
  39. package/dist/react.d.ts +12 -1
  40. package/dist/react.js +101 -30
  41. package/dist/react.js.map +1 -1
  42. package/dist/{retries-CF_HKSlJ.d.ts → retries-CwlpAGet.d.ts} +35 -5
  43. package/dist/retries.d.ts +9 -5
  44. package/dist/retries.js +87 -1
  45. package/dist/retries.js.map +1 -1
  46. package/dist/serializable.d.ts +1 -1
  47. package/dist/skills/index.js +2 -2
  48. package/dist/sub-routing.d.ts +1 -1
  49. package/dist/workflows.d.ts +1 -1
  50. package/package.json +10 -10
  51. package/dist/shared-4CAYLCTO.d.ts +0 -34
  52. package/dist/shared-wyII629d.js +0 -432
  53. package/dist/shared-wyII629d.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/mcp/sse-keepalive.ts","../../src/mcp/utils.ts","../../src/mcp/transport.ts","../../src/mcp/event-store.ts","../../src/mcp/client-transports.ts","../../src/mcp/worker-transport.ts","../../src/mcp/auth-context.ts","../../src/mcp/handler.ts","../../src/mcp/index.ts"],"sourcesContent":["/**\n * Shared SSE keepalive utility for MCP transports.\n *\n * Cloudflare's edge closes idle SSE responses after ~5 minutes. Writers\n * that may sit silent for that long (long-running tool calls, idle\n * standalone GET streams) arm a keepalive to keep the response under the\n * watchdog.\n *\n * See cloudflare/agents#1583.\n */\n\n/** Interval between SSE keepalive comment frames, in ms.\n *\n * The WHATWG SSE spec recommends a comment line every \"15 seconds or so\"\n * (html.spec.whatwg.org §9.2.7). 25s gives comfortable headroom below\n * both the ~30s post-handler background-work cancellation window on\n * Workers and the ~5min Cloudflare edge idle-stream watchdog.\n */\nexport const KEEPALIVE_INTERVAL_MS = 25_000;\n\n/** SSE comment frame the parser drops before any event dispatch. */\nexport const KEEPALIVE_FRAME = \": keepalive\\n\\n\";\n\n/**\n * Start an SSE keepalive on `writer`. Returns a `clearInterval` handle\n * that the stream cleanup must invoke when the stream closes.\n */\nexport function startKeepalive(\n writer: WritableStreamDefaultWriter<Uint8Array>,\n encoder: TextEncoder\n): ReturnType<typeof setInterval> {\n const handle = setInterval(() => {\n writer\n .write(encoder.encode(KEEPALIVE_FRAME))\n .catch(() => clearInterval(handle));\n }, KEEPALIVE_INTERVAL_MS);\n return handle;\n}\n","import {\n JSONRPCMessageSchema,\n type JSONRPCMessage,\n type MessageExtraInfo,\n InitializeRequestSchema,\n isJSONRPCResultResponse,\n isJSONRPCNotification\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpAgent } from \".\";\nimport { getAgentByName } from \"..\";\nimport type { CORSOptions } from \"./types\";\nimport { MessageType } from \"../types\";\nimport { startKeepalive } from \"./sse-keepalive\";\n\n/**\n * Since we use WebSockets to bridge the client to the\n * MCP transport in the Agent, we use this header to signal\n * the method of the original request the user made, while\n * leaving the WS Upgrade request as GET.\n */\nexport const MCP_HTTP_METHOD_HEADER = \"cf-mcp-method\";\n\n/**\n * Since we use WebSockets to bridge the client to the\n * MCP transport in the Agent, we use this header to include\n * the original request body.\n */\nexport const MCP_MESSAGE_HEADER = \"cf-mcp-message\";\n\nconst MAXIMUM_MESSAGE_SIZE_BYTES = 4 * 1024 * 1024; // 4MB\n\nexport const createStreamingHttpHandler = (\n basePath: string,\n namespace: DurableObjectNamespace<McpAgent>,\n options: {\n corsOptions?: CORSOptions;\n jurisdiction?: DurableObjectJurisdiction;\n } = {}\n) => {\n let pathname = basePath;\n if (basePath === \"/\") pathname = \"/*\";\n\n const basePattern = new URLPattern({ pathname });\n return async (request: Request, ctx: ExecutionContext) => {\n const url = new URL(request.url);\n if (basePattern.test(url)) {\n if (request.method === \"POST\") {\n // Validate the Accept header\n const acceptHeader = request.headers.get(\"accept\");\n // The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.\n if (\n !acceptHeader?.includes(\"application/json\") ||\n !acceptHeader.includes(\"text/event-stream\")\n ) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message:\n \"Not Acceptable: Client must accept both application/json and text/event-stream\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 406 });\n }\n\n const ct = request.headers.get(\"content-type\");\n if (!ct || !ct.includes(\"application/json\")) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message:\n \"Unsupported Media Type: Content-Type must be application/json\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 415 });\n }\n\n // Check content length against maximum allowed size\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") ?? \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message: `Request body too large. Maximum size is ${MAXIMUM_MESSAGE_SIZE_BYTES} bytes`\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 413 });\n }\n\n let sessionId = request.headers.get(\"mcp-session-id\");\n let rawMessage: unknown;\n\n try {\n rawMessage = await request.json();\n } catch (_error) {\n const body = JSON.stringify({\n error: {\n code: -32700,\n message: \"Parse error: Invalid JSON\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // Make sure the message is an array to simplify logic\n let arrayMessage: unknown[];\n if (Array.isArray(rawMessage)) {\n arrayMessage = rawMessage;\n } else {\n arrayMessage = [rawMessage];\n }\n\n let messages: JSONRPCMessage[] = [];\n\n // Try to parse each message as JSON RPC. Fail if any message is invalid\n for (const msg of arrayMessage) {\n if (!JSONRPCMessageSchema.safeParse(msg).success) {\n const body = JSON.stringify({\n error: {\n code: -32700,\n message: \"Parse error: Invalid JSON-RPC message\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n }\n\n messages = arrayMessage.map((msg) => JSONRPCMessageSchema.parse(msg));\n\n // Before we pass the messages to the agent, there's another error condition we need to enforce\n // Check if this is an initialization request\n // https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle/\n const maybeInitializeRequest = messages.find(\n (msg) => InitializeRequestSchema.safeParse(msg).success\n );\n\n if (!!maybeInitializeRequest && sessionId) {\n const body = JSON.stringify({\n error: {\n code: -32600,\n message:\n \"Invalid Request: Initialization requests must not include a sessionId\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // The initialization request must be the only request in the batch\n if (!!maybeInitializeRequest && messages.length > 1) {\n const body = JSON.stringify({\n error: {\n code: -32600,\n message:\n \"Invalid Request: Only one initialization request is allowed\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // If an Mcp-Session-Id is returned by the server during initialization,\n // clients using the Streamable HTTP transport MUST include it\n // in the Mcp-Session-Id header on all of their subsequent HTTP requests.\n if (!maybeInitializeRequest && !sessionId) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // If we don't have a sessionId, we are serving an initialization request\n // and need to generate a new sessionId\n sessionId = sessionId ?? namespace.newUniqueId().toString();\n\n // Get the agent and set props\n const agent = await getAgentByName(\n namespace,\n `streamable-http:${sessionId}`,\n {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n }\n );\n const isInitialized = await agent.getInitializeRequest();\n\n if (maybeInitializeRequest) {\n await agent.setInitializeRequest(maybeInitializeRequest);\n } else if (!isInitialized) {\n // if we have gotten here, then a session id that was never initialized\n // was provided\n const body = JSON.stringify({\n error: {\n code: -32001,\n message: \"Session not found\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 404 });\n }\n\n // We've evaluated all the error conditions! Now it's time to establish\n // all the streams\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Connect to the Durable Object via WebSocket\n const existingHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n existingHeaders[key] = value;\n });\n\n const req = new Request(request.url, {\n headers: {\n ...existingHeaders,\n [MCP_HTTP_METHOD_HEADER]: \"POST\",\n [MCP_MESSAGE_HEADER]: Buffer.from(\n JSON.stringify(messages)\n ).toString(\"base64\"),\n Upgrade: \"websocket\"\n }\n });\n const response = await agent.fetch(req);\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n\n await writer.close();\n const body = JSON.stringify({\n error: {\n code: -32001,\n message: \"Failed to establish WebSocket connection\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 500 });\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // If there are no requests, we send the messages to the agent and\n // acknowledge the request with a 202 since we don't expect any\n // responses back through this connection. Decide this *before*\n // arming a keepalive on the SSE writer so we don't leak a timer.\n const hasOnlyNotificationsOrResponses = messages.every(\n (msg) => isJSONRPCNotification(msg) || isJSONRPCResultResponse(msg)\n );\n if (hasOnlyNotificationsOrResponses) {\n // closing the websocket will also close the SSE connection\n ws.close();\n\n return new Response(null, {\n headers: corsHeaders(request, options.corsOptions),\n status: 202\n });\n }\n\n // Long-running tool calls can sit silent for many seconds while\n // the DO runs the handler. Arm a keepalive on the response stream\n // so the Cloudflare edge ~5min idle watchdog doesn't close us\n // before the tool result arrives. POST streams are scoped to a\n // specific request id and can't be resumed via Last-Event-ID,\n // so there's no alternative recovery path here.\n const keepAlive = startKeepalive(writer, encoder);\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", (event) => {\n async function onMessage(event: MessageEvent) {\n try {\n const data =\n typeof event.data === \"string\"\n ? event.data\n : new TextDecoder().decode(event.data);\n const message = JSON.parse(data);\n\n // We only forward events from the MCP server\n if (message.type !== MessageType.CF_MCP_AGENT_EVENT) {\n return;\n }\n\n // Send the message as an SSE event\n await writer.write(encoder.encode(message.event));\n\n // If we have received all the responses, close the connection\n if (message.close) {\n clearInterval(keepAlive);\n ws?.close();\n await writer.close().catch(() => {});\n }\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n }\n onMessage(event).catch(console.error);\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", (error) => {\n async function onError(_error: Event) {\n clearInterval(keepAlive);\n await writer.close().catch(() => {});\n }\n onError(error).catch(console.error);\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", () => {\n async function onClose() {\n clearInterval(keepAlive);\n await writer.close().catch(() => {});\n }\n onClose().catch(console.error);\n });\n\n // Return the SSE response. We handle closing the stream in the ws \"message\"\n // handler\n return new Response(readable, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n \"mcp-session-id\": sessionId,\n ...corsHeaders(request, options.corsOptions)\n },\n status: 200\n });\n } else if (request.method === \"GET\") {\n // Validate the Accept header\n const acceptHeader = request.headers.get(\"accept\");\n // The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.\n if (!acceptHeader?.includes(\"text/event-stream\")) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Not Acceptable: Client must accept text/event-stream\"\n },\n id: null\n });\n return new Response(body, { status: 406 });\n }\n\n // Require sessionId\n const sessionId = request.headers.get(\"mcp-session-id\");\n if (!sessionId)\n return new Response(\n JSON.stringify({\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\"\n },\n id: null,\n jsonrpc: \"2.0\"\n }),\n { status: 400 }\n );\n\n // Create SSE stream\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n const agent = await getAgentByName(\n namespace,\n `streamable-http:${sessionId}`,\n {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n }\n );\n const isInitialized = await agent.getInitializeRequest();\n if (!isInitialized) {\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32001, message: \"Session not found\" },\n id: null\n }),\n { status: 404 }\n );\n }\n\n const existingHeaders: Record<string, string> = {};\n request.headers.forEach((v, k) => {\n existingHeaders[k] = v;\n });\n\n const response = await agent.fetch(\n new Request(request.url, {\n headers: {\n ...existingHeaders,\n [MCP_HTTP_METHOD_HEADER]: \"GET\",\n Upgrade: \"websocket\"\n }\n })\n );\n\n const ws = response.webSocket;\n if (!ws) {\n await writer.close();\n return new Response(\"Failed to establish WS to DO\", {\n status: 500\n });\n }\n ws.accept();\n\n // Forward DO messages as SSE\n ws.addEventListener(\"message\", (event) => {\n try {\n async function onMessage(ev: MessageEvent) {\n const data =\n typeof ev.data === \"string\"\n ? ev.data\n : new TextDecoder().decode(ev.data);\n const message = JSON.parse(data);\n\n // We only forward events from the MCP server\n if (message.type !== MessageType.CF_MCP_AGENT_EVENT) {\n return;\n }\n await writer.write(encoder.encode(message.event));\n }\n onMessage(event).catch(console.error);\n } catch (e) {\n console.error(\"Error forwarding message to SSE:\", e);\n }\n });\n\n ws.addEventListener(\"error\", () => {\n writer.close().catch(() => {});\n });\n ws.addEventListener(\"close\", () => {\n writer.close().catch(() => {});\n });\n\n return new Response(readable, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n \"mcp-session-id\": sessionId,\n ...corsHeaders(request, options.corsOptions)\n },\n status: 200\n });\n } else if (request.method === \"DELETE\") {\n const sessionId = request.headers.get(\"mcp-session-id\");\n if (!sessionId) {\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\"\n },\n id: null\n }),\n { status: 400, headers: corsHeaders(request, options.corsOptions) }\n );\n }\n const agent = await getAgentByName(\n namespace,\n `streamable-http:${sessionId}`,\n { jurisdiction: options.jurisdiction }\n );\n const isInitialized = await agent.getInitializeRequest();\n if (!isInitialized) {\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32001, message: \"Session not found\" },\n id: null\n }),\n { status: 404, headers: corsHeaders(request, options.corsOptions) }\n );\n }\n // .destroy() passes an uncatchable Error, so we make sure we first return\n // the response to the client.\n ctx.waitUntil(\n agent.destroy().catch(() => {\n /* This will always throw. We silently catch here */\n })\n );\n return new Response(null, {\n status: 204,\n headers: corsHeaders(request, options.corsOptions)\n });\n }\n }\n\n // Route not found\n const body = JSON.stringify({\n error: {\n code: -32000,\n message: \"Not found\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 404 });\n };\n};\n\nexport const createLegacySseHandler = (\n basePath: string,\n namespace: DurableObjectNamespace<McpAgent>,\n options: {\n corsOptions?: CORSOptions;\n jurisdiction?: DurableObjectJurisdiction;\n } = {}\n) => {\n let pathname = basePath;\n if (basePath === \"/\") pathname = \"/*\";\n\n const basePattern = new URLPattern({ pathname });\n const messagePattern = new URLPattern({ pathname: `${basePath}/message` }); // SSE only\n return async (request: Request, ctx: ExecutionContext) => {\n const url = new URL(request.url);\n // Handle initial SSE connection\n if (request.method === \"GET\" && basePattern.test(url)) {\n // Use a session ID if one is passed in, or create a unique\n // session ID for this connection\n const sessionId =\n url.searchParams.get(\"sessionId\") || namespace.newUniqueId().toString();\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Send the endpoint event\n const endpointUrl = new URL(request.url);\n endpointUrl.pathname = encodeURI(`${basePath}/message`);\n endpointUrl.searchParams.set(\"sessionId\", sessionId);\n const relativeUrlWithSession =\n endpointUrl.pathname + endpointUrl.search + endpointUrl.hash;\n const endpointMessage = `event: endpoint\\ndata: ${relativeUrlWithSession}\\n\\n`;\n writer.write(encoder.encode(endpointMessage));\n\n // Get the Durable Object\n const agent = await getAgentByName(namespace, `sse:${sessionId}`, {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n });\n\n // Connect to the Durable Object via WebSocket\n const existingHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n existingHeaders[key] = value;\n });\n const response = await agent.fetch(\n new Request(request.url, {\n headers: {\n ...existingHeaders,\n [MCP_HTTP_METHOD_HEADER]: \"SSE\",\n Upgrade: \"websocket\"\n }\n })\n );\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n await writer.close();\n return new Response(\"Failed to establish WebSocket connection\", {\n status: 500\n });\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", (event) => {\n async function onMessage(event: MessageEvent) {\n try {\n const message = JSON.parse(event.data);\n\n // validate that the message is a valid JSONRPC message\n const result = JSONRPCMessageSchema.safeParse(message);\n if (!result.success) {\n // The message was not a valid JSONRPC message, so we will drop it\n // PartyKit will broadcast state change messages to all connected clients\n // and we need to filter those out so they are not passed to MCP clients\n return;\n }\n\n // Send the message as an SSE event\n const messageText = `event: message\\ndata: ${JSON.stringify(result.data)}\\n\\n`;\n await writer.write(encoder.encode(messageText));\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n }\n onMessage(event).catch(console.error);\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", (error) => {\n async function onError(_error: Event) {\n try {\n await writer.close();\n } catch (_e) {\n // Ignore errors when closing\n }\n }\n onError(error).catch(console.error);\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", () => {\n async function onClose() {\n try {\n await writer.close();\n } catch (error) {\n console.error(\"Error closing SSE connection:\", error);\n }\n }\n onClose().catch(console.error);\n });\n\n // Return the SSE response\n return new Response(readable, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n ...corsHeaders(request, options.corsOptions)\n }\n });\n }\n\n // Handle incoming MCP messages. These will be passed to McpAgent\n // but the response will be sent back via the open SSE connection\n // so we only need to return a 202 Accepted response for success\n if (request.method === \"POST\" && messagePattern.test(url)) {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\n `Missing sessionId. Expected POST to ${basePath} to initiate new one`,\n { status: 400 }\n );\n }\n\n const contentType = request.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"application/json\")) {\n return new Response(`Unsupported content-type: ${contentType}`, {\n status: 400\n });\n }\n\n // check if the request body is too large\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") || \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {\n return new Response(`Request body too large: ${contentLength} bytes`, {\n status: 400\n });\n }\n\n // Get the Durable Object\n const agent = await getAgentByName(namespace, `sse:${sessionId}`, {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n });\n\n const messageBody = await request.json();\n\n // Build MessageExtraInfo with filtered headers\n const headers = Object.fromEntries(request.headers.entries());\n\n const extraInfo: MessageExtraInfo = {\n requestInfo: { headers }\n };\n\n const error = await agent.onSSEMcpMessage(\n sessionId,\n messageBody,\n extraInfo\n );\n\n if (error) {\n return new Response(error.message, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n ...corsHeaders(request, options.corsOptions)\n },\n status: 400\n });\n }\n\n return new Response(\"Accepted\", {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n ...corsHeaders(request, options.corsOptions)\n },\n status: 202\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n };\n};\n\n/**\n * Auto-negotiating handler that serves both streamable HTTP and legacy SSE\n * on the same path. Streamable-HTTP-capable clients are preferred; legacy SSE\n * clients fall back transparently.\n *\n * Discrimination rules:\n * - POST to `{basePath}/message` → legacy SSE (the sub-path is SSE-only)\n * - POST to `{basePath}` → streamable HTTP\n * - GET with `mcp-session-id` header → streamable HTTP (standalone SSE reconnect)\n * - GET without `mcp-session-id` → legacy SSE (new SSE connection)\n * - DELETE → streamable HTTP (SSE has no session teardown)\n */\nexport const createAutoHandler = (\n basePath: string,\n namespace: DurableObjectNamespace<McpAgent>,\n options: {\n corsOptions?: CORSOptions;\n jurisdiction?: DurableObjectJurisdiction;\n } = {}\n) => {\n const handleStreamableHttp = createStreamingHttpHandler(\n basePath,\n namespace,\n options\n );\n const handleLegacySse = createLegacySseHandler(basePath, namespace, options);\n\n const messagePattern = new URLPattern({\n pathname: `${basePath}/message`\n });\n\n return async (request: Request, ctx: ExecutionContext) => {\n const url = new URL(request.url);\n\n if (request.method === \"DELETE\") {\n return handleStreamableHttp(request, ctx);\n }\n\n if (request.method === \"POST\" && messagePattern.test(url)) {\n return handleLegacySse(request, ctx);\n }\n\n if (request.method === \"POST\") {\n return handleStreamableHttp(request, ctx);\n }\n\n if (request.method === \"GET\" && request.headers.has(\"mcp-session-id\")) {\n return handleStreamableHttp(request, ctx);\n }\n\n if (request.method === \"GET\") {\n return handleLegacySse(request, ctx);\n }\n\n return new Response(\"Method Not Allowed\", {\n status: 405,\n headers: {\n Allow: \"GET, POST, DELETE\",\n ...corsHeaders(request, options.corsOptions)\n }\n });\n };\n};\n\n// CORS helper functions\nexport function corsHeaders(_request: Request, corsOptions: CORSOptions = {}) {\n const origin = corsOptions.origin || \"*\";\n const headers =\n corsOptions.headers ||\n \"Content-Type, Accept, Authorization, mcp-session-id, mcp-protocol-version\";\n\n return {\n \"Access-Control-Allow-Headers\": headers,\n \"Access-Control-Allow-Methods\":\n corsOptions.methods || \"GET, POST, DELETE, OPTIONS\",\n \"Access-Control-Allow-Origin\": origin,\n \"Access-Control-Expose-Headers\":\n corsOptions.exposeHeaders || \"mcp-session-id\",\n \"Access-Control-Max-Age\": (corsOptions.maxAge || 86400).toString()\n };\n}\n\nexport function handleCORS(\n request: Request,\n corsOptions?: CORSOptions\n): Response | null {\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: corsHeaders(request, corsOptions) });\n }\n\n return null;\n}\n\nexport function isDurableObjectNamespace(\n namespace: unknown\n): namespace is DurableObjectNamespace<McpAgent> {\n return (\n typeof namespace === \"object\" &&\n namespace !== null &&\n \"newUniqueId\" in namespace &&\n typeof namespace.newUniqueId === \"function\" &&\n \"idFromName\" in namespace &&\n typeof namespace.idFromName === \"function\"\n );\n}\n","import type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n type MessageExtraInfo,\n type RequestInfo,\n isJSONRPCErrorResponse,\n isJSONRPCRequest,\n isJSONRPCResultResponse,\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type RequestId\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport type {\n EventStore,\n StreamId,\n EventId\n} from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport { getCurrentAgent, type Connection } from \"..\";\nimport type { McpAgent } from \".\";\nimport { MessageType } from \"../types\";\nimport { MCP_HTTP_METHOD_HEADER, MCP_MESSAGE_HEADER } from \"./utils\";\n\nexport type { EventStore, StreamId, EventId };\n\nexport class McpSSETransport implements Transport {\n sessionId: string;\n // Set by the server in `server.connect(transport)`\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n private _getWebSocket: () => WebSocket | null;\n private _started = false;\n constructor() {\n const { agent } = getCurrentAgent<McpAgent>();\n if (!agent)\n throw new Error(\"McpAgent was not found in Transport constructor\");\n\n this.sessionId = agent.getSessionId();\n this._getWebSocket = () => agent.getWebSocket();\n }\n\n async start() {\n // The transport does not manage the WebSocket connection since it's terminated\n // by the Durable Object in order to allow hibernation. There's nothing to initialize.\n if (this._started) {\n throw new Error(\"Transport already started\");\n }\n this._started = true;\n }\n\n async send(message: JSONRPCMessage) {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n const websocket = this._getWebSocket();\n if (!websocket) {\n throw new Error(\"WebSocket not connected\");\n }\n try {\n websocket.send(JSON.stringify(message));\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n\n async close() {\n // Similar to start, the only thing to do is to pass the event on to the server\n this.onclose?.();\n }\n}\n\n/**\n * Configuration options for StreamableHTTPServerTransport\n */\nexport interface StreamableHTTPServerTransportOptions {\n /**\n * Event store for resumability support.\n * If provided, resumability will be enabled, allowing clients to\n * reconnect and resume messages.\n *\n * If the store also implements {@link ClearableEventStore.clearStream}\n * the transport will call it after the final response of a POST\n * stream is written, so storage stays bounded without any background\n * sweep. {@link DurableObjectEventStore} is the canonical example.\n */\n eventStore?: EventStore | ClearableEventStore;\n}\n\n/**\n * An {@link EventStore} that supports dropping all events for a single\n * stream id. Implemented by {@link DurableObjectEventStore}.\n */\nexport interface ClearableEventStore extends EventStore {\n clearStream(streamId: StreamId): Promise<void>;\n}\n\nfunction isClearableEventStore(\n store: EventStore | ClearableEventStore\n): store is ClearableEventStore {\n return typeof (store as ClearableEventStore).clearStream === \"function\";\n}\n\n/**\n * Adapted from: https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts\n * - Validation and initialization are removed as they're handled in `McpAgent.serve()` handler.\n * - Replaces the Node-style `req`/`res` with Worker's `Request`.\n * - Writes events as WS messages that the Worker forwards to the client as SSE events.\n * - Replaces the in-memory maps that track requestID/stream by using `connection.setState()` and `agent.getConnections()`.\n *\n * Besides these points, the implementation is the same and should be updated to match the original as new features are added.\n */\n/** Fixed streamId for the standalone GET listen stream. */\nconst STANDALONE_STREAM_ID = \"_GET_stream\";\n\n/** State persisted on each WebSocket connection by the transport. */\ntype TransportConnState = {\n /** Stable identifier for the SSE stream this connection serves.\n * Used as the event-store key. Survives WS reconnects via Last-Event-ID. */\n streamId?: string;\n /** True iff this connection is the standalone GET listen stream. */\n _standaloneSse?: boolean;\n /** Request ids whose responses must flow through this connection. */\n requestIds?: RequestId[];\n};\n\nexport class StreamableHTTPServerTransport implements Transport {\n private _started = false;\n private _eventStore?: EventStore | ClearableEventStore;\n\n // This tracks which messages on each POST stream have been answered.\n // It is fine that we do not persist this since it only supports backwards\n // compatibility for clients batching requests, which the spec discourages.\n // Keying by stream avoids colliding ids on independent POST streams sharing\n // completion state with one another.\n private _streamResponseIds: Map<string, Set<RequestId>> = new Map();\n\n sessionId: string;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n /**\n * Optional message interceptor that can intercept messages before they are passed to onmessage.\n * If the interceptor returns true, the message is considered handled and won't be forwarded.\n * This is used by McpAgent to intercept elicitation responses.\n */\n messageInterceptor?: (\n message: JSONRPCMessage,\n extra?: MessageExtraInfo\n ) => Promise<boolean>;\n\n constructor(options: StreamableHTTPServerTransportOptions) {\n const { agent } = getCurrentAgent<McpAgent>();\n if (!agent)\n throw new Error(\"McpAgent was not found in Transport constructor\");\n\n // Initialization is handled in `McpAgent.serve()` and agents are addressed by sessionId,\n // so we'll always have this available.\n this.sessionId = agent.getSessionId();\n this._eventStore = options.eventStore;\n }\n\n /**\n * Starts the transport. This is required by the Transport interface but is a no-op\n * for the Streamable HTTP transport as connections are managed per-request.\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\"Transport already started\");\n }\n this._started = true;\n }\n\n /**\n * Handles GET requests for SSE stream.\n *\n * Two roles a GET can play:\n * 1. Fresh standalone listen stream — carries server-initiated\n * requests/notifications unrelated to any in-progress POST.\n * 2. Resumption of a previously-disconnected stream via\n * `Last-Event-ID`. The disconnected stream may have been the\n * standalone stream OR a POST tool-call response stream; per the\n * MCP 2025-03-26 spec the server replays missed messages \"on the\n * stream that was disconnected\" and continues delivering\n * subsequent messages on that same stream.\n *\n * To resume a POST stream we recover the original streamId from the\n * event-store and the original `requestIds` from durable storage,\n * then write them onto the new WS connection so `send()` keeps\n * routing in-flight tool responses to it.\n */\n async handleGetRequest(req: Request): Promise<void> {\n const { connection, agent } = getCurrentAgent<McpAgent>();\n if (!connection)\n throw new Error(\"Connection was not found in handleGetRequest\");\n if (!agent) throw new Error(\"Agent was not found in handleGetRequest\");\n\n const lastEventId = req.headers.get(\"last-event-id\");\n\n // Resume path: the client identifies which stream it lost via\n // Last-Event-ID. Recover the original streamId from the event\n // store and register this connection under it. Matches the SDK\n // reference implementation (typescript-sdk's `replayEvents`):\n // the resumed connection is mapped to the *original* streamId,\n // no dual-role tagging.\n //\n // Forward routing then depends on what kind of stream it was:\n // - active POST: persisted requestIds are restored so further\n // tool responses route to this new WS via state.requestIds.\n // - standalone listen stream: tag with _standaloneSse so\n // server-initiated notifications continue to land here.\n // - completed POST / unknown: this connection is a one-shot\n // replay channel. No future messages will be routed to it.\n //\n // In every resumable case we supersede any prior connection bound\n // to the same streamId by closing it, so there is at most one live\n // connection per stream. This keeps `send()` routing deterministic\n // and keeps us within the MCP rule that each message goes out on\n // exactly one stream.\n if (this._eventStore && lastEventId) {\n const resumedStreamId =\n await this._eventStore.getStreamIdForEventId?.(lastEventId);\n if (resumedStreamId) {\n const resumeState: TransportConnState = {\n streamId: resumedStreamId\n };\n if (resumedStreamId === STANDALONE_STREAM_ID) {\n resumeState._standaloneSse = true;\n } else {\n const persistedReqs =\n await agent.getStreamRequestIds(resumedStreamId);\n if (persistedReqs && persistedReqs.length > 0) {\n resumeState.requestIds = persistedReqs;\n }\n }\n this.supersedePriorStreamConnections(\n agent,\n connection.id,\n resumedStreamId\n );\n connection.setState(resumeState);\n await this.replayEvents(lastEventId);\n return;\n }\n }\n\n // Fresh standalone listen stream. The MCP spec allows only one\n // standalone GET per session, so supersede any existing one.\n this.supersedePriorStreamConnections(\n agent,\n connection.id,\n STANDALONE_STREAM_ID\n );\n const standaloneState: TransportConnState = {\n streamId: STANDALONE_STREAM_ID,\n _standaloneSse: true\n };\n connection.setState(standaloneState);\n }\n\n /**\n * Close any connection (other than `selfId`) currently bound to\n * `streamId`, so at most one live connection serves a given stream.\n * Closing rather than mutating sibling state mirrors how the SDK's\n * single `_streamMapping` entry gives last-writer-wins for free, and\n * keeps `send()` from routing to a stale bridge.\n */\n private supersedePriorStreamConnections(\n agent: McpAgent,\n selfId: string,\n streamId: string\n ): void {\n for (const other of agent.getConnections<TransportConnState>()) {\n if (other.id === selfId) continue;\n if (other.state?.streamId !== streamId) continue;\n other.close(1000, \"Superseded by resumed stream\");\n }\n }\n\n /**\n * Replays events that would have been sent after the specified event ID\n * Only used when resumability is enabled\n */\n private async replayEvents(lastEventId: string): Promise<void> {\n if (!this._eventStore) {\n return;\n }\n\n const { connection } = getCurrentAgent();\n if (!connection)\n throw new Error(\"Connection was not available in replayEvents\");\n\n try {\n await this._eventStore?.replayEventsAfter(lastEventId, {\n send: async (eventId: string, message: JSONRPCMessage) => {\n try {\n this.writeSSEEvent(connection, message, eventId);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n });\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n\n /**\n * Writes an event to the SSE stream with proper formatting\n */\n private writeSSEEvent(\n connection: Connection,\n message: JSONRPCMessage,\n eventId?: string,\n close?: boolean\n ) {\n let eventData = \"event: message\\n\";\n // Include event ID if provided - this is important for resumability\n if (eventId) {\n eventData += `id: ${eventId}\\n`;\n }\n eventData += `data: ${JSON.stringify(message)}\\n\\n`;\n\n return connection.send(\n JSON.stringify({\n type: MessageType.CF_MCP_AGENT_EVENT,\n event: eventData,\n close\n })\n );\n }\n\n /**\n * Handles POST requests containing JSON-RPC messages\n */\n async handlePostRequest(\n req: Request & { auth?: AuthInfo },\n parsedBody: unknown\n ): Promise<void> {\n const authInfo: AuthInfo | undefined = req.auth;\n const requestInfo: RequestInfo = {\n headers: Object.fromEntries(req.headers.entries()),\n url: new URL(req.url)\n };\n // Remove headers that are not part of the original request\n delete requestInfo.headers[MCP_HTTP_METHOD_HEADER];\n delete requestInfo.headers[MCP_MESSAGE_HEADER];\n delete requestInfo.headers.upgrade;\n\n const rawMessage = parsedBody;\n let messages: JSONRPCMessage[];\n\n // handle batch and single messages\n if (Array.isArray(rawMessage)) {\n messages = rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg));\n } else {\n messages = [JSONRPCMessageSchema.parse(rawMessage)];\n }\n\n // check if it contains requests\n const hasRequests = messages.some(isJSONRPCRequest);\n\n if (!hasRequests) {\n // We process without sending anything\n for (const message of messages) {\n // check if message should be intercepted (i.e. elicitation responses)\n if (this.messageInterceptor) {\n const handled = await this.messageInterceptor(message, {\n authInfo,\n requestInfo\n });\n if (handled) {\n continue; // msg was handled by interceptor, skip onmessage\n }\n }\n this.onmessage?.(message, { authInfo, requestInfo });\n }\n } else if (hasRequests) {\n const { connection, agent } = getCurrentAgent<McpAgent>();\n if (!connection)\n throw new Error(\"Connection was not found in handlePostRequest\");\n if (!agent) throw new Error(\"Agent was not found in handlePostRequest\");\n\n // We need to track by request ID to maintain the connection\n const requestIds = messages\n .filter(isJSONRPCRequest)\n .map((message) => message.id);\n\n // The streamId is stable for the lifetime of this POST's stream.\n // We seed it with the WS connection id (unique per POST), and a\n // resumed GET later inherits the *same* streamId via Last-Event-ID.\n const streamId = connection.id;\n const postState: TransportConnState = { streamId, requestIds };\n connection.setState(postState);\n\n // Persist the mapping so a future GET-with-Last-Event-ID can\n // restore `requestIds` onto a fresh WS connection. Only relevant\n // when an event store is configured — without one the client has\n // no `id:` to resume from anyway. Cleaned up in `send()` on the\n // final response.\n if (this._eventStore) {\n await agent.setStreamRequestIds(streamId, requestIds);\n }\n\n // handle each message\n for (const message of messages) {\n if (this.messageInterceptor) {\n const handled = await this.messageInterceptor(message, {\n authInfo,\n requestInfo\n });\n if (handled) {\n continue; // Message was handled by interceptor, skip onmessage\n }\n }\n this.onmessage?.(message, { authInfo, requestInfo });\n }\n // The server SHOULD NOT close the SSE stream before sending all JSON-RPC responses\n // This will be handled by the send() method when responses are ready\n }\n }\n\n async close(): Promise<void> {\n // Close all SSE connections\n const { agent } = getCurrentAgent();\n if (!agent) throw new Error(\"Agent was not found in close\");\n\n for (const conn of agent.getConnections()) {\n conn.close(1000, \"Session closed\");\n }\n this.onclose?.();\n }\n\n /**\n * Store the event, decide whether this is the final response, write\n * the SSE frame iff a live connection is attached, then run cleanup.\n * Caller resolves `streamId` and `relatedIds` (from connection state\n * or persisted reverse lookup) and passes `liveConnection` as null\n * when the originating WS has dropped.\n */\n private async sendOnStream(\n agent: McpAgent,\n streamId: string,\n relatedIds: readonly RequestId[],\n liveConnection: Connection<TransportConnState> | null,\n message: JSONRPCMessage,\n requestId: RequestId\n ): Promise<void> {\n const eventId = await this._eventStore?.storeEvent(streamId, message);\n\n let shouldClose = false;\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n let responseIds = this._streamResponseIds.get(streamId);\n if (!responseIds) {\n responseIds = new Set<RequestId>();\n this._streamResponseIds.set(streamId, responseIds);\n }\n responseIds.add(requestId);\n shouldClose = relatedIds.every((id) => responseIds.has(id));\n if (shouldClose) this._streamResponseIds.delete(streamId);\n }\n\n // Write FIRST, clean up SECOND. Clearing before the write would\n // leave a mid-flight client with a wiped stream on reconnect.\n // `writeSSEEvent` is sync (enqueues, doesn't await), so the bytes\n // are committed before any cleanup await can interleave. Wrap in\n // try/catch so a dead WS can't skip cleanup and orphan the\n // stream-reqs + stored events.\n if (liveConnection) {\n try {\n this.writeSSEEvent(liveConnection, message, eventId, shouldClose);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n\n if (shouldClose) {\n // A concurrent GET resume between these awaits would replay\n // events about to be deleted — benign.\n await agent.deleteStreamRequestIds(streamId);\n if (this._eventStore && isClearableEventStore(this._eventStore)) {\n await this._eventStore.clearStream(streamId);\n }\n }\n }\n\n async send(\n message: JSONRPCMessage,\n options?: { relatedRequestId?: RequestId }\n ): Promise<void> {\n // Request-scoped (response / `relatedRequestId` notification) vs\n // server-initiated on the standalone GET stream. Two helpers.\n const isResponse =\n isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);\n const requestId = isResponse ? message.id : options?.relatedRequestId;\n\n if (requestId === undefined) {\n if (isResponse) {\n throw new Error(\n \"Cannot send a response on a standalone SSE stream unless resuming a previous client request\"\n );\n }\n return this.sendStandalone(message);\n }\n\n return this.sendForRequest(message, requestId);\n }\n\n /**\n * Server-initiated message on the standalone GET stream. Stored under\n * a fixed streamId so it's replayable even when no live connection is\n * currently attached.\n *\n * Sent on exactly one stream, per MCP: \"the server MUST send each of\n * its JSON-RPC messages on only one of the connected streams; it MUST\n * NOT broadcast the same message across multiple streams.\"\n * `handleGetRequest` supersedes prior standalone connections, so\n * there is at most one to send on.\n */\n private async sendStandalone(message: JSONRPCMessage): Promise<void> {\n const { agent } = getCurrentAgent<McpAgent>();\n if (!agent) throw new Error(\"Agent was not found in send\");\n\n const eventId = await this._eventStore?.storeEvent(\n STANDALONE_STREAM_ID,\n message\n );\n\n const standalone = Array.from(\n agent.getConnections<TransportConnState>()\n ).find((conn) => conn.state?._standaloneSse);\n // No live standalone stream: the event is stored above and replays\n // when a client reconnects with Last-Event-ID. Per spec the server\n // MAY send on the stream, so dropping the live write is fine.\n if (standalone) {\n this.writeSSEEvent(standalone, message, eventId);\n }\n }\n\n /**\n * Message scoped to a specific in-flight client request: a tool\n * response, error, or progress notification. Resolves which stream\n * owns the request id (live POST connection, resumed GET, or\n * persisted reverse lookup for a dropped WS) and delegates to\n * {@link sendOnStream} for the actual store / write / cleanup.\n */\n private async sendForRequest(\n message: JSONRPCMessage,\n requestId: RequestId\n ): Promise<void> {\n const { agent, connection: originatingConnection } =\n getCurrentAgent<McpAgent>();\n if (!agent) throw new Error(\"Agent was not found in send\");\n\n // Pick the live connection that should receive this message. Normally\n // request ids uniquely identify a POST connection. If a client violates\n // that constraint, prefer the connection whose handler is currently\n // producing this message rather than leaking a plausible response to\n // the first matching POST stream. Only prefer an originating connection\n // while it is still live: after a POST stream disconnects, a resumed\n // GET connection inherits requestIds and must be allowed to receive\n // the eventual response.\n const matchingConnections = Array.from(\n agent.getConnections<TransportConnState>()\n ).filter((conn) => conn.state?.requestIds?.includes(requestId));\n const liveConnection =\n matchingConnections.find(\n (conn) => conn.id === originatingConnection?.id\n ) ?? (matchingConnections.length === 1 ? matchingConnections[0] : null);\n\n // Ambiguous routing: multiple live POST connections claim the same\n // request id, none of which is the originating connection. Terminate\n // each with a protocol error rather than guessing.\n if (!liveConnection && matchingConnections.length > 1) {\n const routingError: JSONRPCMessage = {\n jsonrpc: \"2.0\",\n id: requestId,\n error: { code: -32603, message: \"Internal error\" }\n };\n await Promise.all(\n matchingConnections.map((candidate) =>\n this.sendOnStream(\n agent,\n candidate.state?.streamId ?? candidate.id,\n candidate.state?.requestIds ?? [],\n candidate,\n routingError,\n requestId\n )\n )\n );\n return;\n }\n\n // Resolve streamId + relatedIds. Prefer the live connection's state;\n // when the originating WS has dropped fall back to the persisted\n // reverse lookup so the event can still be stored for replay —\n // mirrors the SDK's `_requestToStreamMapping` which outlives\n // connection loss.\n let streamId = liveConnection?.state?.streamId;\n let relatedIds = liveConnection?.state?.requestIds;\n if (!streamId) {\n const stored = await agent.getStreamForRequestId(requestId);\n if (!stored) {\n throw new Error(\n `No active stream found for request ID: ${String(requestId)}`\n );\n }\n streamId = stored.streamId;\n relatedIds = stored.requestIds;\n }\n\n await this.sendOnStream(\n agent,\n streamId,\n relatedIds ?? [],\n liveConnection,\n message,\n requestId\n );\n }\n}\n","import type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport type {\n EventStore,\n EventId,\n StreamId\n} from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\n\n/**\n * Durable Object–backed {@link EventStore} for SSE resumability.\n *\n * Default for `McpAgent`. Override `McpAgent.getEventStore()` to swap\n * or disable.\n *\n * ## Storage layout\n *\n * Events are stored under `__mcp_event__:<streamId>:<seqHex>`, where\n * `<seqHex>` is a 16-char zero-padded counter so events in a stream\n * sort lexicographically and `getStreamIdForEventId` can recover the\n * stream from `eventId` without a storage hit.\n *\n * ## Lifecycle\n *\n * Each POST tool-call stream's events live only until the final\n * response is delivered. The transport calls {@link clearStream}\n * immediately after writing the close frame, so storage growth is\n * bounded by the in-flight POST streams plus the standalone GET\n * stream. There is no background sweep — quiescent agents do no work,\n * and the DO itself dies with the session.\n *\n * Standalone GET stream events (`_GET_stream`) are *not* cleared\n * automatically; they accumulate for the lifetime of the DO. Bounded\n * by session length in practice.\n *\n * Trade-off: if the client TCP connection dies *after* the close\n * frame has been enqueued on the WS but before the bytes reach the\n * client, the final message is unreplayable. Every earlier event in\n * the stream is still replayable while the in-flight stream is open.\n *\n * ## Stream id constraints\n *\n * `streamId` MUST NOT contain `:`. `storeEvent` asserts this so\n * embedders using custom stream ids fail loudly rather than risk\n * prefix-scan collisions (e.g. clearing `a` accidentally hitting\n * `a:b`). Default ids (`connection.id` UUIDs and the literal\n * `_GET_stream`) already satisfy this.\n */\nexport class DurableObjectEventStore implements EventStore {\n private static readonly EVENT_KEY_PREFIX = \"__mcp_event__:\";\n private static readonly SEQ_PAD = 16;\n /** DO storage caps multi-key delete at 128. */\n private static readonly DELETE_CHUNK = 128;\n /** Defensive ceiling on a single replay batch. A live stream's\n * event count is small (progress notifications + final result);\n * this is here so a pathological history can't OOM the DO. */\n private static readonly REPLAY_LIMIT = 1000;\n\n private readonly storage: DurableObjectStorage;\n\n /** In-memory seq counters per stream, rehydrated lazily from storage. */\n private readonly seqByStream = new Map<StreamId, number>();\n private readonly seqInit = new Map<StreamId, Promise<void>>();\n\n constructor(storage: DurableObjectStorage) {\n this.storage = storage;\n }\n\n async storeEvent(\n streamId: StreamId,\n message: JSONRPCMessage\n ): Promise<EventId> {\n if (streamId.includes(\":\")) {\n // Event keys are `__mcp_event__:<streamId>:<seqHex>` — a `:` in\n // streamId would let prefix scans cross stream boundaries.\n throw new Error(\n `DurableObjectEventStore: streamId must not contain ':' (got ${JSON.stringify(streamId)})`\n );\n }\n await this.ensureSeqLoaded(streamId);\n const seq = (this.seqByStream.get(streamId) ?? 0) + 1;\n this.seqByStream.set(streamId, seq);\n\n const seqHex = seq\n .toString(16)\n .padStart(DurableObjectEventStore.SEQ_PAD, \"0\");\n const eventId = `${streamId}:${seqHex}`;\n const eventKey = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${eventId}`;\n\n await this.storage.put(eventKey, message);\n return eventId;\n }\n\n async getStreamIdForEventId(eventId: EventId): Promise<StreamId | undefined> {\n const idx = eventId.lastIndexOf(\":\");\n return idx > 0 ? eventId.slice(0, idx) : undefined;\n }\n\n async replayEventsAfter(\n lastEventId: EventId,\n {\n send\n }: { send: (eventId: EventId, message: JSONRPCMessage) => Promise<void> }\n ): Promise<StreamId> {\n const streamId = await this.getStreamIdForEventId(lastEventId);\n if (!streamId) return \"\";\n\n const prefix = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${streamId}:`;\n // `list({ start })` is inclusive, and we want strictly-after\n // semantics. Appending `\\x00` (the smallest byte) to the last\n // event's key produces a key that sorts immediately after it, so\n // the list excludes the boundary event without a post-filter.\n const startKey = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${lastEventId}\\x00`;\n // DO `storage.list()` with no `limit` loads everything into memory.\n // Stream histories are normally small (progress events + result),\n // but cap the batch defensively. Clients can reconnect again to\n // drain past the cap if they ever produce that many events.\n const rows = await this.storage.list<JSONRPCMessage>({\n prefix,\n start: startKey,\n limit: DurableObjectEventStore.REPLAY_LIMIT\n });\n\n for (const [key, message] of rows) {\n const eventId = key.slice(\n DurableObjectEventStore.EVENT_KEY_PREFIX.length\n );\n await send(eventId, message);\n }\n return streamId;\n }\n\n /**\n * Drop the event log for a single stream. Called by the transport\n * immediately after a POST's final response has been written to the\n * wire — no future `Last-Event-ID` for this stream is expected to\n * resolve.\n *\n * Lists and deletes in chunks of {@link DELETE_CHUNK} (128, the DO\n * storage cap) so we never load the entire event log into memory.\n * After deleting, the next `list` call won't see the deleted keys,\n * so passing `start: <prefix>` again is enough — no cursor bookkeeping.\n */\n async clearStream(streamId: StreamId): Promise<void> {\n const prefix = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${streamId}:`;\n for (;;) {\n const rows = await this.storage.list({\n prefix,\n limit: DurableObjectEventStore.DELETE_CHUNK\n });\n if (rows.size === 0) break;\n await this.storage.delete([...rows.keys()]);\n }\n this.seqByStream.delete(streamId);\n this.seqInit.delete(streamId);\n }\n\n private async ensureSeqLoaded(streamId: StreamId): Promise<void> {\n if (this.seqByStream.has(streamId)) return;\n let pending = this.seqInit.get(streamId);\n if (!pending) {\n pending = (async () => {\n const prefix = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${streamId}:`;\n const rows = await this.storage.list({\n prefix,\n reverse: true,\n limit: 1\n });\n let seq = 0;\n for (const key of rows.keys()) {\n const parsed = Number.parseInt(key.slice(prefix.length), 16);\n if (Number.isFinite(parsed)) seq = parsed;\n }\n if (!this.seqByStream.has(streamId)) {\n this.seqByStream.set(streamId, seq);\n }\n })();\n this.seqInit.set(streamId, pending);\n }\n try {\n await pending;\n } finally {\n this.seqInit.delete(streamId);\n }\n }\n}\n","/**\n * Deprecated transport wrappers\n */\n\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { SSEClientTransportOptions } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { StreamableHTTPClientTransportOptions } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n\nlet didWarnAboutSSEEdgeClientTransport = false;\n\n/**\n * @deprecated Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. This alias will be removed in the next major version.\n */\nexport class SSEEdgeClientTransport extends SSEClientTransport {\n constructor(url: URL, options: SSEClientTransportOptions) {\n super(url, options);\n if (!didWarnAboutSSEEdgeClientTransport) {\n didWarnAboutSSEEdgeClientTransport = true;\n console.warn(\n \"SSEEdgeClientTransport is deprecated. Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. SSEEdgeClientTransport will be removed in the next major version.\"\n );\n }\n }\n}\n\nlet didWarnAboutStreamableHTTPEdgeClientTransport = false;\n\n/**\n * @deprecated Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. This alias will be removed in the next major version.\n */\nexport class StreamableHTTPEdgeClientTransport extends StreamableHTTPClientTransport {\n constructor(url: URL, options: StreamableHTTPClientTransportOptions) {\n super(url, options);\n if (!didWarnAboutStreamableHTTPEdgeClientTransport) {\n didWarnAboutStreamableHTTPEdgeClientTransport = true;\n console.warn(\n \"StreamableHTTPEdgeClientTransport is deprecated. Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. StreamableHTTPEdgeClientTransport will be removed in the next major version.\"\n );\n }\n }\n}\n","/**\n * WorkerTransport\n *\n * Thin Cloudflare-Workers wrapper around the official MCP SDK\n * `WebStandardStreamableHTTPServerTransport`. The wrapper layers a couple of\n * Workers-specific concerns on top of the SDK transport without forking it:\n *\n * 1. **CORS** — preflight handling and response-header injection,\n * configurable via `corsOptions`.\n * 2. **Persistent transport state** — when a `storage` adapter\n * (`MCPStorageApi`) is supplied, the wrapper persists\n * `{sessionId, initialized, initializeParams}` so that an MCP session can\n * survive DO hibernation / eviction. On the first request after a cold\n * start, the saved initialize params are replayed through the `Server`\n * so client capabilities are re-established.\n * 3. **SSE keepalive** — SSE responses are wrapped in a TransformStream that\n * injects a `: keepalive\\n\\n` comment frame every 25s so the Cloudflare\n * edge ~5min idle-stream watchdog doesn't kill long-running tool calls.\n * Disabled on the standalone GET stream when an `eventStore` is\n * configured — clients recover idle drops via `Last-Event-ID` instead.\n * POST response streams always keepalive (no resumption path during a\n * mid-flight tool call). See cloudflare/agents#1583.\n *\n * Everything else (session validation, SSE streaming, protocol-version\n * negotiation, event-store resumability, etc.) is delegated to the SDK\n * transport.\n */\n\nimport {\n WebStandardStreamableHTTPServerTransport,\n type WebStandardStreamableHTTPServerTransportOptions\n} from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport type { TransportSendOptions } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n isInitializeRequest,\n isJSONRPCErrorResponse,\n isJSONRPCResultResponse,\n type InitializeRequestParams,\n type JSONRPCMessage,\n type RequestId\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { CORSOptions } from \"./types\";\nimport { KEEPALIVE_FRAME, KEEPALIVE_INTERVAL_MS } from \"./sse-keepalive\";\n\n/** Sentinel id used when replaying the persisted initialize request. */\nconst RESTORE_REQUEST_ID = \"__worker_transport_restore__\";\n\n/**\n * Pluggable storage adapter for persisting `WorkerTransport` state across\n * Durable Object hibernation / restart cycles.\n *\n * A typical implementation reads/writes a single key on `this.ctx.storage`\n * inside a Durable Object or Agent.\n */\nexport interface MCPStorageApi {\n get(): Promise<TransportState | undefined> | TransportState | undefined;\n set(state: TransportState): Promise<void> | void;\n}\n\n/** Shape of the persisted transport state. */\nexport interface TransportState {\n sessionId?: string;\n initialized: boolean;\n initializeParams?: InitializeRequestParams;\n}\n\nconst DEFAULT_CORS_OPTIONS: Required<\n Pick<\n CORSOptions,\n \"origin\" | \"headers\" | \"methods\" | \"exposeHeaders\" | \"maxAge\"\n >\n> = {\n origin: \"*\",\n headers:\n \"Content-Type, Accept, Authorization, mcp-session-id, MCP-Protocol-Version\",\n methods: \"GET, POST, DELETE, OPTIONS\",\n exposeHeaders: \"mcp-session-id\",\n maxAge: 86400\n};\n\nexport interface WorkerTransportOptions extends WebStandardStreamableHTTPServerTransportOptions {\n /**\n * CORS options applied to every response and to OPTIONS preflight.\n * Defaults: `origin: *`, expose `mcp-session-id`, allow the standard MCP\n * methods/headers, max-age 86400.\n */\n corsOptions?: CORSOptions;\n /**\n * Optional storage adapter for persisting transport state across DO\n * hibernation / restart. Use this to keep an MCP session alive across\n * Durable Object wake-ups.\n */\n storage?: MCPStorageApi;\n}\n\nexport class WorkerTransport extends WebStandardStreamableHTTPServerTransport {\n private readonly _corsOptions?: CORSOptions;\n private readonly _storage?: MCPStorageApi;\n private _stateRestored = false;\n private _capturedInitializeParams?: InitializeRequestParams;\n private _userOnSessionInitialized?: (\n sessionId: string\n ) => void | Promise<void>;\n private _bridgeInstalled = false;\n /**\n * Tracks keepalive interval cleanups so we can fire them eagerly when the\n * SDK closes the underlying SSE stream via `closeSSEStream(requestId)` or\n * `closeStandaloneSSEStream()`. Keyed by the JSON-RPC request id that\n * triggered the stream, or the sentinel for the standalone GET stream.\n */\n private readonly _keepaliveCleanups = new Map<\n RequestId | \"_standalone\",\n () => void\n >();\n /**\n * Most recent JSON-RPC request id seen on an incoming POST. Used to key\n * keepalive cleanups when the response is an SSE stream tied to that\n * request (so `closeSSEStream(id)` can find and clear the interval).\n */\n private _pendingRequestId?: RequestId;\n /**\n * Request ids whose SSE stream was deliberately torn down via\n * `closeSSEStream`. The SDK's `send()` throws \"No connection established\"\n * when a request id has no stream — a race that surfaces whenever the\n * server's tool handler resolves *after* the caller closed the stream\n * (e.g. polling-style early-close, or test fixtures closing mid-flight).\n * We swallow `send()` for these ids so the rejection doesn't bubble out\n * of the protocol layer as an unhandled rejection. Mirrors the\n * silent-noop behaviour of the pre-refactor `WorkerTransport`.\n */\n private readonly _closedRequestIds = new Set<RequestId>();\n\n constructor(options: WorkerTransportOptions = {}) {\n const { corsOptions, storage, onsessioninitialized, ...sdkOptions } =\n options;\n\n // `storage` is intentionally orthogonal to statefulness: stateful-vs-\n // stateless behaviour is driven solely by the SDK's `sessionIdGenerator`.\n // `storage` only persists whatever session state exists across DO\n // hibernation, so it's used alongside a `sessionIdGenerator`.\n //\n // We wrap onsessioninitialized so we can persist state to storage as soon\n // as the SDK transport assigns a session ID. The bridge gets installed\n // lazily on the first request so `this` is fully constructed when it fires.\n super({\n ...sdkOptions,\n onsessioninitialized: undefined\n });\n\n this._corsOptions = corsOptions;\n this._storage = storage;\n this._userOnSessionInitialized = onsessioninitialized;\n }\n\n /**\n * Backwards-compatible alias for the SDK's internal `_started` flag.\n * Several callers and tests check `transport.started` directly.\n */\n get started(): boolean {\n return (this as unknown as { _started: boolean })._started;\n }\n\n /**\n * Top-level request entry point. Handles CORS preflight, restores any\n * persisted state on first invocation, then delegates to the SDK transport\n * and finally appends CORS headers + keepalive to whatever response comes\n * back.\n */\n override async handleRequest(\n request: Request,\n options?: { parsedBody?: unknown; authInfo?: AuthInfo }\n ): Promise<Response> {\n if (request.method === \"OPTIONS\") {\n return new Response(null, {\n headers: this.getCorsHeaders({ forPreflight: true })\n });\n }\n\n await this.restoreState();\n this.installOnSessionInitializedBridge();\n\n // Capture the initialize params before delegating, so we can persist\n // them alongside the session id that the SDK assigns inside\n // handleRequest. Also captures the JSON-RPC request id of any POSTed\n // request so we can key the keepalive cleanup to it.\n await this.captureInitializeParams(request, options);\n const requestIdForKeepalive =\n request.method === \"GET\" ? \"_standalone\" : this._pendingRequestId;\n\n const response = await super.handleRequest(request, options);\n\n // State is persisted by the `onsessioninitialized` bridge, which the SDK\n // fires (and awaits) during `super.handleRequest` on the initialize path —\n // the only point session state actually changes. We deliberately do *not*\n // snapshot again here: that would write to storage on every request\n // (notifications, tool calls, GET, DELETE) where nothing changed, matching\n // neither the pre-refactor behaviour (one write at init) nor the intent of\n // the storage adapter.\n\n return this.withCorsHeaders(\n this.withKeepalive(\n this.normalizeAllowHeader(response),\n requestIdForKeepalive\n )\n );\n }\n\n /**\n * The SDK's 405 responses advertise `Allow: GET, POST, DELETE` because\n * OPTIONS is handled outside the SDK. Since our wrapper *does* handle\n * OPTIONS, advertise it in `Allow` so clients can probe accurately.\n */\n private normalizeAllowHeader(response: Response): Response {\n if (response.status !== 405) return response;\n const allow = response.headers.get(\"Allow\");\n if (!allow || allow.includes(\"OPTIONS\")) return response;\n const headers = new Headers(response.headers);\n headers.set(\"Allow\", `${allow}, OPTIONS`);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers\n });\n }\n\n override closeSSEStream(requestId: RequestId): void {\n this._keepaliveCleanups.get(requestId)?.();\n this._keepaliveCleanups.delete(requestId);\n this._closedRequestIds.add(requestId);\n super.closeSSEStream(requestId);\n }\n\n override closeStandaloneSSEStream(): void {\n this._keepaliveCleanups.get(\"_standalone\")?.();\n this._keepaliveCleanups.delete(\"_standalone\");\n super.closeStandaloneSSEStream();\n }\n\n override async close(): Promise<void> {\n for (const cleanup of Array.from(this._keepaliveCleanups.values())) {\n cleanup();\n }\n this._keepaliveCleanups.clear();\n this._closedRequestIds.clear();\n await super.close();\n }\n\n /**\n * Swallow two classes of message that would otherwise surface as\n * unhandled rejections from the SDK transport's `send()`:\n *\n * 1. Replayed initialize responses (the `RESTORE_REQUEST_ID` sentinel)\n * — we synthesise these in `restoreState()` to rebuild server\n * capabilities; there's no real client waiting for the response.\n * 2. Sends for a request id whose SSE stream has been deliberately\n * closed via `closeSSEStream`. The protocol layer's tool-handler\n * promise may settle after the close, and the SDK's `send()` throws\n * \"No connection established\" — a race the pre-refactor transport\n * silently swallowed.\n *\n * Everything else is delegated. We use `await super.send(...)` rather\n * than `return super.send(...)` so any rejection is observed inside this\n * async frame; without the await, the test runner's\n * unhandled-rejection tracker can fire before the caller's own `await`\n * observes it.\n */\n override async send(\n message: JSONRPCMessage,\n options?: TransportSendOptions\n ): Promise<void> {\n let requestId: RequestId | undefined = options?.relatedRequestId;\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n requestId = message.id;\n }\n if (requestId === RESTORE_REQUEST_ID) {\n return;\n }\n if (requestId !== undefined && this._closedRequestIds.has(requestId)) {\n return;\n }\n await super.send(message, options);\n }\n\n // ── SSE keepalive ──────────────────────────────────────────────────────\n\n /**\n * If the response is an SSE stream, tee the body through a TransformStream\n * that injects a `: keepalive\\n\\n` comment frame every 25s. The interval\n * is cleared when the wrapped stream closes — which happens both when the\n * SDK ends the underlying stream naturally and when `closeSSEStream` is\n * called.\n *\n * Keepalive policy:\n * - POST response streams (`key` is a request id): always keepalive.\n * In-progress tool calls have no recovery path — if the stream drops\n * mid-execution the result is lost — so we keep it under the\n * Cloudflare edge ~5min idle watchdog.\n * - Standalone GET stream (`key === \"_standalone\"`): keepalive only\n * when no `eventStore` is configured. When resumability is enabled,\n * clients reconnect with `Last-Event-ID` after an idle drop, so we\n * skip the keepalive and let the DO hibernate.\n *\n * Uses the shared `sse-keepalive` constants so both this wrapper and\n * `McpAgent.serve()` write identical frames at the same cadence.\n * See cloudflare/agents#1583.\n */\n private withKeepalive(\n response: Response,\n key: RequestId | \"_standalone\" | undefined\n ): Response {\n const contentType = response.headers.get(\"Content-Type\") ?? \"\";\n if (!contentType.includes(\"text/event-stream\") || !response.body) {\n return response;\n }\n\n // Skip keepalive on the standalone GET stream when an event store is\n // configured — the recovery path is Last-Event-ID reconnects, not\n // bytes-on-the-wire.\n if (key === \"_standalone\" && this.eventStoreConfigured()) {\n return response;\n }\n\n const encoder = new TextEncoder();\n let intervalId: ReturnType<typeof setInterval> | undefined;\n let controllerRef: TransformStreamDefaultController<Uint8Array> | undefined;\n\n const clear = () => {\n if (intervalId !== undefined) {\n clearInterval(intervalId);\n intervalId = undefined;\n }\n if (key !== undefined) this._keepaliveCleanups.delete(key);\n };\n\n const transform = new TransformStream<Uint8Array, Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n intervalId = setInterval(() => {\n try {\n controllerRef?.enqueue(encoder.encode(KEEPALIVE_FRAME));\n } catch {\n clear();\n }\n }, KEEPALIVE_INTERVAL_MS);\n if (key !== undefined) this._keepaliveCleanups.set(key, clear);\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n flush() {\n clear();\n },\n cancel() {\n clear();\n }\n });\n\n const piped = response.body.pipeThrough(transform);\n return new Response(piped, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers\n });\n }\n\n /**\n * Does the SDK transport have an `eventStore`? Reaches into the SDK's\n * private field because the option isn't surfaced on the public API —\n * we only need a yes/no for keepalive policy.\n */\n private eventStoreConfigured(): boolean {\n return (\n (this as unknown as { _eventStore?: unknown })._eventStore !== undefined\n );\n }\n\n // ── CORS ───────────────────────────────────────────────────────────────\n\n private getCorsHeaders({\n forPreflight\n }: { forPreflight?: boolean } = {}): Record<string, string> {\n const merged = { ...DEFAULT_CORS_OPTIONS, ...this._corsOptions };\n if (forPreflight) {\n return {\n \"Access-Control-Allow-Origin\": merged.origin,\n \"Access-Control-Allow-Headers\": merged.headers,\n \"Access-Control-Allow-Methods\": merged.methods,\n \"Access-Control-Max-Age\": String(merged.maxAge)\n };\n }\n return {\n \"Access-Control-Allow-Origin\": merged.origin,\n \"Access-Control-Expose-Headers\": merged.exposeHeaders\n };\n }\n\n private withCorsHeaders(response: Response): Response {\n const headers = new Headers(response.headers);\n for (const [k, v] of Object.entries(this.getCorsHeaders())) {\n headers.set(k, v);\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers\n });\n }\n\n // ── State persistence ──────────────────────────────────────────────────\n\n private installOnSessionInitializedBridge(): void {\n if (this._bridgeInstalled) return;\n const sdk = this as unknown as {\n _onsessioninitialized?: (id: string) => void | Promise<void>;\n };\n sdk._onsessioninitialized = async (sessionId: string): Promise<void> => {\n if (this._userOnSessionInitialized) {\n await Promise.resolve(this._userOnSessionInitialized(sessionId));\n }\n await this.saveState();\n };\n this._bridgeInstalled = true;\n }\n\n private async captureInitializeParams(\n request: Request,\n handleOptions?: { parsedBody?: unknown }\n ): Promise<void> {\n this._pendingRequestId = undefined;\n if (request.method !== \"POST\") return;\n try {\n const parsed =\n handleOptions?.parsedBody ?? (await request.clone().json());\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n const init = messages.find(\n (m): m is JSONRPCMessage =>\n typeof m === \"object\" && m !== null && isInitializeRequest(m)\n );\n if (init && isInitializeRequest(init)) {\n this._capturedInitializeParams = {\n capabilities: init.params.capabilities,\n clientInfo: init.params.clientInfo,\n protocolVersion: init.params.protocolVersion\n };\n }\n // Record the first JSON-RPC request id so we can key keepalive cleanup\n // to it. Batch requests share a single SSE stream in the SDK, so we\n // pick the first request id we see. Eager cleanup via `closeSSEStream`\n // only matches that first id; closing any other id in the batch tears\n // down the same shared stream, and the keepalive interval is cleared by\n // the TransformStream's flush/cancel when that stream actually closes.\n const firstRequest = messages.find(\n (m): m is JSONRPCMessage & { id: RequestId } =>\n typeof m === \"object\" && m !== null && \"id\" in m && \"method\" in m\n );\n if (firstRequest) {\n this._pendingRequestId = firstRequest.id;\n }\n } catch {\n // Body wasn't JSON or already consumed — the SDK transport will\n // surface a proper error response.\n }\n }\n\n private async restoreState(): Promise<void> {\n if (!this._storage || this._stateRestored) return;\n // Set the guard up-front so a re-entrant call (a second request reaching\n // this `await` before the first resolves) doesn't restore twice. If the\n // storage read throws we reset it so a transient failure can be retried\n // on the next request rather than leaving the session permanently\n // un-restored for this DO instance's lifetime.\n this._stateRestored = true;\n\n let state: TransportState | undefined;\n try {\n state = await Promise.resolve(this._storage.get());\n } catch (error) {\n this._stateRestored = false;\n throw error;\n }\n if (!state) return;\n\n // Restore SDK private state. We intentionally reach in here — the SDK\n // doesn't expose hooks for this, and the alternative (a fresh initialize\n // round-trip per cold start) would defeat the point of session\n // persistence.\n const sdk = this as unknown as {\n sessionId?: string;\n _initialized: boolean;\n };\n sdk.sessionId = state.sessionId;\n sdk._initialized = state.initialized;\n this._capturedInitializeParams = state.initializeParams;\n\n if (state.initializeParams && this.onmessage) {\n // Replay through the Server so `_clientCapabilities` etc. are\n // restored. `send()` filters out the resulting response by request id.\n this.onmessage({\n jsonrpc: \"2.0\",\n id: RESTORE_REQUEST_ID,\n method: \"initialize\",\n params: state.initializeParams\n });\n }\n }\n\n private async saveState(): Promise<void> {\n if (!this._storage) return;\n const sdk = this as unknown as {\n sessionId?: string;\n _initialized: boolean;\n };\n const state: TransportState = {\n sessionId: sdk.sessionId,\n initialized: sdk._initialized,\n initializeParams: this._capturedInitializeParams\n };\n await Promise.resolve(this._storage.set(state));\n }\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport interface McpAuthContext {\n props: Record<string, unknown>;\n}\n\nconst authContextStorage = new AsyncLocalStorage<McpAuthContext>();\n\nexport function getMcpAuthContext(): McpAuthContext | undefined {\n return authContextStorage.getStore();\n}\n\nexport function runWithAuthContext<T>(context: McpAuthContext, fn: () => T): T {\n return authContextStorage.run(context, fn);\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n WorkerTransport,\n type WorkerTransportOptions\n} from \"./worker-transport\";\nimport { runWithAuthContext, type McpAuthContext } from \"./auth-context\";\n\nexport interface CreateMcpHandlerOptions extends WorkerTransportOptions {\n /**\n * The route path that this MCP handler should respond to.\n * If specified, the handler will only process requests that match this route.\n * @default \"/mcp\"\n */\n route?: string;\n /**\n * An optional auth context to use for handling MCP requests.\n * If not provided, the handler will look for props in the execution context.\n */\n authContext?: McpAuthContext;\n /**\n * An optional transport to use for handling MCP requests.\n * If not provided, a WorkerTransport will be created with the provided WorkerTransportOptions.\n */\n transport?: WorkerTransport;\n}\n\nexport function createMcpHandler(\n server: McpServer | Server,\n options: CreateMcpHandlerOptions = {}\n): (\n request: Request,\n env: unknown,\n ctx: ExecutionContext\n) => Promise<Response> {\n const route = options.route ?? \"/mcp\";\n const {\n route: _route,\n authContext,\n transport: providedTransport,\n ...transportOptions\n } = options;\n\n return async (\n request: Request,\n _env: unknown,\n ctx: ExecutionContext\n ): Promise<Response> => {\n const url = new URL(request.url);\n if (route && url.pathname !== route) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const transport =\n providedTransport ?? new WorkerTransport(transportOptions);\n\n const buildAuthContext = () => {\n if (authContext) {\n return authContext;\n }\n\n if (ctx.props && Object.keys(ctx.props).length > 0) {\n return {\n props: ctx.props as Record<string, unknown>\n };\n }\n\n return undefined;\n };\n\n const handleRequest = async () => {\n return await transport.handleRequest(request);\n };\n\n const resolvedAuthContext = buildAuthContext();\n\n // Guard for stateful usage where a pre-connected transport is passed via options.\n // If someone passes a transport that's already connected to this server, skip reconnecting.\n // Note: If a developer incorrectly uses a global server with per-request transports,\n // the MCP SDK 1.26.0+ will throw an error when trying to connect an already-connected server.\n if (!transport.started) {\n // Check if server is already connected (McpServer has isConnected(), Server uses transport getter)\n const isServerConnected =\n server instanceof McpServer\n ? server.isConnected()\n : server.transport !== undefined;\n\n if (isServerConnected) {\n throw new Error(\n \"Server is already connected to a transport. Create a new McpServer instance per request for stateless handlers.\"\n );\n }\n\n await server.connect(transport);\n }\n\n try {\n if (resolvedAuthContext) {\n return await runWithAuthContext(resolvedAuthContext, handleRequest);\n } else {\n return await handleRequest();\n }\n } catch (error) {\n console.error(\"MCP handler error:\", error);\n\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message:\n error instanceof Error ? error.message : \"Internal server error\"\n },\n id: null\n }),\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n };\n}\n\nlet didWarnAboutExperimentalCreateMcpHandler = false;\n\n/**\n * @deprecated This has been renamed to createMcpHandler, and experimental_createMcpHandler will be removed in the next major version\n */\nexport function experimental_createMcpHandler(\n server: McpServer | Server,\n options: CreateMcpHandlerOptions = {}\n): (\n request: Request,\n env: unknown,\n ctx: ExecutionContext\n) => Promise<Response> {\n if (!didWarnAboutExperimentalCreateMcpHandler) {\n didWarnAboutExperimentalCreateMcpHandler = true;\n console.warn(\n \"experimental_createMcpHandler is deprecated, use createMcpHandler instead. experimental_createMcpHandler will be removed in the next major version.\"\n );\n }\n return createMcpHandler(server, options);\n}\n","import type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type {\n JSONRPCMessage,\n MessageExtraInfo,\n RequestId\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n JSONRPCMessageSchema,\n isJSONRPCErrorResponse,\n isJSONRPCResultResponse,\n type ElicitResult\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Connection, ConnectionContext } from \"../\";\nimport { Agent } from \"../index\";\nimport type { BaseTransportType, MaybePromise, ServeOptions } from \"./types\";\nimport {\n createAutoHandler,\n createLegacySseHandler,\n createStreamingHttpHandler,\n handleCORS,\n isDurableObjectNamespace,\n MCP_HTTP_METHOD_HEADER,\n MCP_MESSAGE_HEADER\n} from \"./utils\";\nimport { McpSSETransport, StreamableHTTPServerTransport } from \"./transport\";\nimport type { EventStore } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { DurableObjectEventStore } from \"./event-store\";\nimport { RPCServerTransport, type RPCServerTransportOptions } from \"./rpc\";\n\nexport abstract class McpAgent<\n Env extends Cloudflare.Env = Cloudflare.Env,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>\n> extends Agent<Env, State, Props> {\n private _transport?: Transport;\n private _pendingElicitations = new Map<\n string,\n { resolve: (result: ElicitResult) => void; reject: (err: Error) => void }\n >();\n props?: Props;\n\n // MCP WebSocket connections are transport bridges — they use their own\n // protocol and don't need agent identity, state sync, or other protocol\n // messages. Regular WebSocket connections are left untouched.\n override shouldSendProtocolMessages(\n _connection: Connection,\n ctx: ConnectionContext\n ): boolean {\n return !ctx.request.headers.get(MCP_HTTP_METHOD_HEADER);\n }\n\n abstract server: MaybePromise<McpServer | Server>;\n abstract init(): Promise<void>;\n\n /*\n * Helpers\n */\n\n async setInitializeRequest(initializeRequest: JSONRPCMessage) {\n await this.ctx.storage.put(\"initializeRequest\", initializeRequest);\n }\n\n async getInitializeRequest() {\n return this.ctx.storage.get<JSONRPCMessage>(\"initializeRequest\");\n }\n\n /**\n * Storage key prefix for the `streamId -> requestIds` mapping used\n * to support POST stream resumption across WebSocket reconnects.\n *\n * @internal\n */\n private static readonly STREAM_REQS_KEY_PREFIX = \"__mcp_stream_reqs__:\";\n\n /** Persist the `requestIds` for a POST stream. @internal */\n async setStreamRequestIds(\n streamId: string,\n requestIds: RequestId[]\n ): Promise<void> {\n await this.ctx.storage.put<RequestId[]>(\n `${McpAgent.STREAM_REQS_KEY_PREFIX}${streamId}`,\n requestIds\n );\n }\n\n /** Read the persisted `requestIds` for a POST stream. @internal */\n async getStreamRequestIds(\n streamId: string\n ): Promise<RequestId[] | undefined> {\n return this.ctx.storage.get<RequestId[]>(\n `${McpAgent.STREAM_REQS_KEY_PREFIX}${streamId}`\n );\n }\n\n /** Drop the persisted `requestIds` for a POST stream. @internal */\n async deleteStreamRequestIds(streamId: string): Promise<void> {\n await this.ctx.storage.delete(\n `${McpAgent.STREAM_REQS_KEY_PREFIX}${streamId}`\n );\n }\n\n /**\n * Reverse lookup: find which POST stream a given `requestId` belongs\n * to, and return the stream's full `requestIds` list in the same\n * pass. Used by the transport when the originating WS has dropped,\n * so `send()` can still record events for replay and decide whether\n * the stream is fully responded — mirrors the SDK's\n * `_requestToStreamMapping` which outlives connection loss.\n *\n * Returning `requestIds` alongside `streamId` lets `send()` skip a\n * second `getStreamRequestIds` read on the same key.\n *\n * O(n) in the number of in-flight POST streams — single-digit in\n * practice since each stream is cleaned up on its final response.\n * The `limit` is a defensive ceiling so an abandoned-POST leak can't\n * unbounded-load this scan; if you hit it, something else has gone\n * wrong and `send()` will throw `No active stream found`.\n *\n * @internal\n */\n async getStreamForRequestId(\n requestId: RequestId\n ): Promise<{ streamId: string; requestIds: RequestId[] } | undefined> {\n const STREAM_REQS_SCAN_LIMIT = 1000;\n const rows = await this.ctx.storage.list<RequestId[]>({\n prefix: McpAgent.STREAM_REQS_KEY_PREFIX,\n limit: STREAM_REQS_SCAN_LIMIT\n });\n if (rows.size === STREAM_REQS_SCAN_LIMIT) {\n console.warn(\n `McpAgent: getStreamForRequestId hit the ${STREAM_REQS_SCAN_LIMIT}-key scan cap; ` +\n `stale __mcp_stream_reqs__ entries may be accumulating from abandoned POSTs`\n );\n }\n for (const [key, requestIds] of rows) {\n if (requestIds?.includes(requestId)) {\n return {\n streamId: key.slice(McpAgent.STREAM_REQS_KEY_PREFIX.length),\n requestIds\n };\n }\n }\n return undefined;\n }\n\n /** Read the transport type for this agent.\n * This relies on the naming scheme being `sse:${sessionId}`,\n * `streamable-http:${sessionId}`, or `rpc:${sessionId}`.\n */\n getTransportType(): BaseTransportType {\n const [t, ..._] = this.name.split(\":\");\n switch (t) {\n case \"sse\":\n return \"sse\";\n case \"streamable-http\":\n return \"streamable-http\";\n case \"rpc\":\n return \"rpc\";\n default:\n throw new Error(\n \"Invalid transport type. McpAgent must be addressed with a valid protocol.\"\n );\n }\n }\n\n /** Read the sessionId for this agent.\n * This relies on the naming scheme being `sse:${sessionId}`\n * or `streamable-http:${sessionId}`.\n */\n getSessionId(): string {\n const [_, sessionId] = this.name.split(\":\");\n if (!sessionId) {\n throw new Error(\n \"Invalid session id. McpAgent must be addressed with a valid session id.\"\n );\n }\n return sessionId;\n }\n\n /** Get the unique WebSocket. SSE transport only. */\n getWebSocket() {\n const websockets = Array.from(this.getConnections());\n if (websockets.length === 0) {\n return null;\n }\n return websockets[0];\n }\n\n /**\n * Returns options for configuring the RPC server transport.\n * Override this method to customize RPC transport behavior (e.g., timeout).\n *\n * @example\n * ```typescript\n * class MyMCP extends McpAgent {\n * protected getRpcTransportOptions() {\n * return { timeout: 120000 }; // 2 minutes\n * }\n * }\n * ```\n */\n protected getRpcTransportOptions(): RPCServerTransportOptions {\n return {};\n }\n\n /**\n * Returns the {@link EventStore} for SSE resumability. Defaults to a\n * {@link DurableObjectEventStore} backed by this agent's storage,\n * letting clients reconnect with `Last-Event-ID` after the Cloudflare\n * edge closes an idle SSE stream (~5 minute watchdog) instead of\n * relying on a server-side keepalive that would block hibernation.\n *\n * Per-stream events are cleared by the transport immediately after\n * the final response is written to the wire, so there's no\n * background cleanup — storage cost is bounded by the in-flight\n * streams alone.\n *\n * Override to disable (`return undefined`) or swap implementations.\n */\n protected getEventStore(): EventStore | undefined {\n return new DurableObjectEventStore(this.ctx.storage);\n }\n\n /** Returns a new transport matching the type of the Agent. */\n private initTransport() {\n switch (this.getTransportType()) {\n case \"sse\": {\n return new McpSSETransport();\n }\n case \"streamable-http\": {\n const transport = new StreamableHTTPServerTransport({\n eventStore: this.getEventStore()\n });\n transport.messageInterceptor = (message) => {\n return Promise.resolve(this._handleElicitationResponse(message));\n };\n return transport;\n }\n case \"rpc\": {\n return new RPCServerTransport(this.getRpcTransportOptions());\n }\n }\n }\n\n /** Update and store the props */\n async updateProps(props?: Props) {\n await this.ctx.storage.put(\"props\", props ?? {});\n this.props = props;\n }\n\n async reinitializeServer() {\n // If the agent was previously initialized, we have to populate\n // the server again by sending the initialize request to make\n // client information available to the server.\n const initializeRequest = await this.getInitializeRequest();\n if (initializeRequest) {\n this._transport?.onmessage?.(initializeRequest);\n }\n }\n\n /*\n * Base Agent / Partykit Server overrides\n */\n\n /** Sets up the MCP transport and server every time the Agent is started.*/\n async onStart(props?: Props) {\n if (props) {\n // Fresh start with props — save to storage (also sets this.props)\n await this.updateProps(props);\n } else {\n // Hibernation recovery — restore props from storage\n this.props = await this.ctx.storage.get(\"props\");\n }\n\n await this.init();\n const server = await this.server;\n // Connect to the MCP server\n this._transport = this.initTransport();\n\n if (!this._transport) {\n throw new Error(\"Failed to initialize transport\");\n }\n await server.connect(this._transport);\n\n await this.reinitializeServer();\n }\n\n /** Validates new WebSocket connections. */\n async onConnect(\n conn: Connection,\n { request: req }: ConnectionContext\n ): Promise<void> {\n switch (this.getTransportType()) {\n case \"sse\": {\n // For SSE connections, we can only have one open connection per session\n // If we get an upgrade while already connected, we should error\n const websockets = Array.from(this.getConnections());\n if (websockets.length > 1) {\n conn.close(1008, \"Websocket already connected\");\n return;\n }\n break;\n }\n case \"streamable-http\":\n if (this._transport instanceof StreamableHTTPServerTransport) {\n switch (req.headers.get(MCP_HTTP_METHOD_HEADER)) {\n case \"POST\": {\n // This returns the response directly to the client\n const payloadHeader = req.headers.get(MCP_MESSAGE_HEADER);\n let rawPayload: string;\n\n if (!payloadHeader) {\n rawPayload = \"{}\";\n } else {\n try {\n rawPayload = Buffer.from(payloadHeader, \"base64\").toString(\n \"utf-8\"\n );\n } catch (_error) {\n throw new Error(\n \"Internal Server Error: Failed to decode MCP message header\"\n );\n }\n }\n\n const parsedBody = JSON.parse(rawPayload);\n this._transport?.handlePostRequest(req, parsedBody);\n break;\n }\n case \"GET\":\n this._transport?.handleGetRequest(req);\n break;\n }\n }\n }\n }\n\n /*\n * Transport ingress and routing\n */\n\n /** Handles MCP Messages for the legacy SSE transport. */\n async onSSEMcpMessage(\n _sessionId: string,\n messageBody: unknown,\n extraInfo?: MessageExtraInfo\n ): Promise<Error | null> {\n // Since we address the DO via both the protocol and the session id,\n // this should never happen, but let's enforce it just in case\n if (this.getTransportType() !== \"sse\") {\n return new Error(\"Internal Server Error: Expected SSE transport\");\n }\n\n try {\n let parsedMessage: JSONRPCMessage;\n try {\n parsedMessage = JSONRPCMessageSchema.parse(messageBody);\n } catch (error) {\n this._transport?.onerror?.(error as Error);\n throw error;\n }\n\n // Check if this is an elicitation response before passing to transport\n if (this._handleElicitationResponse(parsedMessage)) {\n return null; // Message was handled by elicitation system\n }\n\n this._transport?.onmessage?.(parsedMessage, extraInfo);\n return null;\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n this._transport?.onerror?.(error as Error);\n return error as Error;\n }\n }\n\n /** Elicit user input with a message and schema */\n async elicitInput(\n params: {\n message: string;\n requestedSchema: unknown;\n },\n options?: { relatedRequestId?: RequestId }\n ): Promise<ElicitResult> {\n const requestId = `elicit_${Math.random().toString(36).substring(2, 11)}`;\n\n const elicitRequest = {\n jsonrpc: \"2.0\" as const,\n id: requestId,\n method: \"elicitation/create\",\n params: {\n message: params.message,\n requestedSchema: params.requestedSchema\n }\n };\n\n // Create a Promise that will be resolved when the response arrives.\n // timeoutId is hoisted so error paths below can clear it and avoid\n // an unhandled rejection on the orphaned responsePromise.\n let timeoutId: ReturnType<typeof setTimeout>;\n const responsePromise = new Promise<ElicitResult>((resolve, reject) => {\n timeoutId = setTimeout(() => {\n this._pendingElicitations.delete(requestId);\n reject(new Error(\"Elicitation request timed out\"));\n }, 60000);\n\n this._pendingElicitations.set(requestId, {\n resolve: (result: ElicitResult) => {\n clearTimeout(timeoutId);\n this._pendingElicitations.delete(requestId);\n resolve(result);\n },\n reject: (err: Error) => {\n clearTimeout(timeoutId);\n this._pendingElicitations.delete(requestId);\n reject(err);\n }\n });\n });\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n this._pendingElicitations.delete(requestId);\n };\n\n // Keep the DO alive while we wait for the user's elicitation response.\n // An unresolved Promise alone isn't enough to prevent hibernation.\n return this.keepAliveWhile(async () => {\n // Send through MCP transport\n if (this._transport) {\n try {\n await this._transport.send(elicitRequest, options);\n } catch (error) {\n cleanup();\n throw error;\n }\n } else {\n const connections = this.getConnections();\n if (!connections || Array.from(connections).length === 0) {\n cleanup();\n throw new Error(\"No active connections available for elicitation\");\n }\n\n const connectionList = Array.from(connections);\n for (const connection of connectionList) {\n try {\n connection.send(JSON.stringify(elicitRequest));\n } catch (error) {\n console.error(\"Failed to send elicitation request:\", error);\n }\n }\n }\n\n return responsePromise;\n });\n }\n\n /** Handle elicitation responses via in-memory resolver */\n private _handleElicitationResponse(message: JSONRPCMessage): boolean {\n if (isJSONRPCResultResponse(message) && message.result) {\n const requestId = message.id?.toString();\n if (!requestId || !requestId.startsWith(\"elicit_\")) return false;\n\n const pending = this._pendingElicitations.get(requestId);\n if (!pending) return false;\n\n pending.resolve(message.result as ElicitResult);\n return true;\n }\n\n if (isJSONRPCErrorResponse(message)) {\n const requestId = message.id?.toString();\n if (!requestId || !requestId.startsWith(\"elicit_\")) return false;\n\n const pending = this._pendingElicitations.get(requestId);\n if (!pending) return false;\n\n pending.resolve({\n action: \"cancel\",\n content: {\n error: message.error.message || \"Elicitation request failed\"\n }\n });\n return true;\n }\n\n return false;\n }\n\n /**\n * Handle an RPC message for MCP\n * This method is called by the RPC stub to process MCP messages\n * @param message The JSON-RPC message(s) to handle\n * @returns The response message(s) or undefined\n */\n async handleMcpMessage(\n message: JSONRPCMessage | JSONRPCMessage[]\n ): Promise<JSONRPCMessage | JSONRPCMessage[] | undefined> {\n await this.__unsafe_ensureInitialized();\n\n if (!(this._transport instanceof RPCServerTransport)) {\n throw new Error(\"Expected RPC transport\");\n }\n\n const transport = this._transport;\n\n // RPC waits are intentionally in-memory. While a request/continuation is\n // pending, keep the object alive so hibernation does not drop the resolver\n // maps or the suspended tool stack. Making this sleep/resume across\n // hibernation would require a durable continuation model instead.\n return await this.keepAliveWhile(async () => {\n // Intercept elicitation responses before they reach the transport.\n // Mirrors what onSSEMcpMessage() and StreamableHTTPServerTransport's\n // messageInterceptor already do for their respective transports.\n if (!Array.isArray(message)) {\n const parseResult = JSONRPCMessageSchema.safeParse(message);\n if (\n parseResult.success &&\n this._handleElicitationResponse(parseResult.data)\n ) {\n // Resolved a pending elicitation — now wait for the tool handler\n // to send its next message (another elicitation request or the\n // final tool result).\n return await transport._awaitPendingResponse();\n }\n }\n\n return await transport.handle(message);\n });\n }\n\n /** Return a handler for the given path for this MCP.\n * Defaults to Streamable HTTP transport.\n */\n static serve(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n transport = \"streamable-http\",\n jurisdiction\n }: ServeOptions = {}\n ) {\n return {\n async fetch<Env>(\n this: void,\n request: Request,\n env: Env,\n ctx: ExecutionContext\n ): Promise<Response> {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) {\n return corsResponse;\n }\n\n const bindingValue = env[binding as keyof typeof env] as unknown;\n\n // Ensure we have a binding of some sort\n if (bindingValue == null || typeof bindingValue !== \"object\") {\n throw new Error(\n `Could not find McpAgent binding for ${binding}. Did you update your wrangler configuration?`\n );\n }\n\n // Ensure that the binding is to a DurableObject\n if (!isDurableObjectNamespace(bindingValue)) {\n throw new Error(\n `Invalid McpAgent binding for ${binding}. Make sure it's a Durable Object binding.`\n );\n }\n\n const namespace =\n bindingValue satisfies DurableObjectNamespace<McpAgent>;\n\n switch (transport) {\n case \"streamable-http\": {\n const handleStreamableHttp = createStreamingHttpHandler(\n path,\n namespace,\n { corsOptions, jurisdiction }\n );\n return handleStreamableHttp(request, ctx);\n }\n case \"sse\": {\n const handleLegacySse = createLegacySseHandler(path, namespace, {\n corsOptions,\n jurisdiction\n });\n return handleLegacySse(request, ctx);\n }\n case \"auto\": {\n const handleAuto = createAutoHandler(path, namespace, {\n corsOptions,\n jurisdiction\n });\n return handleAuto(request, ctx);\n }\n default:\n return new Response(\n \"Invalid MCP transport mode. Only `streamable-http`, `sse`, or `auto` are allowed.\",\n { status: 500 }\n );\n }\n }\n };\n }\n /**\n * Legacy api\n **/\n static mount(path: string, opts: Omit<ServeOptions, \"transport\"> = {}) {\n return McpAgent.serveSSE(path, opts);\n }\n\n static serveSSE(path: string, opts: Omit<ServeOptions, \"transport\"> = {}) {\n return McpAgent.serve(path, { ...opts, transport: \"sse\" });\n }\n}\n\nexport {\n SSEEdgeClientTransport,\n StreamableHTTPEdgeClientTransport\n} from \"./client-transports\";\nexport {\n RPC_DO_PREFIX,\n RPCClientTransport,\n RPCServerTransport,\n type RPCClientTransportOptions,\n type RPCServerTransportOptions\n} from \"./rpc\";\n\nexport {\n ElicitRequestSchema,\n type ElicitRequest,\n type ElicitResult\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nexport type {\n MCPClientOAuthResult,\n MCPClientOAuthCallbackConfig,\n MCPServerOptions,\n MCPConnectionResult,\n MCPDiscoverResult\n} from \"./client\";\n\nexport { normalizeServerId, MCP_SERVER_ID_MAX_LENGTH } from \"./client\";\n\nexport type { McpClientOptions } from \"./types\";\n\nexport {\n createMcpHandler,\n experimental_createMcpHandler,\n type CreateMcpHandlerOptions\n} from \"./handler\";\n\nexport { getMcpAuthContext, type McpAuthContext } from \"./auth-context\";\n\nexport { DurableObjectEventStore } from \"./event-store\";\nexport type { ClearableEventStore } from \"./transport\";\n\nexport {\n WorkerTransport,\n type WorkerTransportOptions,\n type TransportState\n} from \"./worker-transport\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/mcp/sse-keepalive.ts","../../src/mcp/utils.ts","../../src/mcp/transport.ts","../../src/mcp/event-store.ts","../../src/mcp/client-transports.ts","../../src/mcp/worker-transport.ts","../../src/mcp/auth-context.ts","../../src/mcp/handler.ts","../../src/mcp/index.ts"],"sourcesContent":["/**\n * Shared SSE keepalive utility for MCP transports.\n *\n * Cloudflare's edge closes idle SSE responses after ~5 minutes. Writers\n * that may sit silent for that long (long-running tool calls, idle\n * standalone GET streams) arm a keepalive to keep the response under the\n * watchdog.\n *\n * See cloudflare/agents#1583.\n */\n\n/** Interval between SSE keepalive comment frames, in ms.\n *\n * The WHATWG SSE spec recommends a comment line every \"15 seconds or so\"\n * (html.spec.whatwg.org §9.2.7). 25s gives comfortable headroom below\n * both the ~30s post-handler background-work cancellation window on\n * Workers and the ~5min Cloudflare edge idle-stream watchdog.\n */\nexport const KEEPALIVE_INTERVAL_MS = 25_000;\n\n/** SSE comment frame the parser drops before any event dispatch. */\nexport const KEEPALIVE_FRAME = \": keepalive\\n\\n\";\n\n/**\n * Start an SSE keepalive on `writer`. Returns a `clearInterval` handle\n * that the stream cleanup must invoke when the stream closes.\n */\nexport function startKeepalive(\n writer: WritableStreamDefaultWriter<Uint8Array>,\n encoder: TextEncoder\n): ReturnType<typeof setInterval> {\n const handle = setInterval(() => {\n writer\n .write(encoder.encode(KEEPALIVE_FRAME))\n .catch(() => clearInterval(handle));\n }, KEEPALIVE_INTERVAL_MS);\n return handle;\n}\n","import {\n JSONRPCMessageSchema,\n type JSONRPCMessage,\n type MessageExtraInfo,\n InitializeRequestSchema,\n isJSONRPCResultResponse,\n isJSONRPCNotification\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpAgent } from \".\";\nimport { getAgentByName } from \"..\";\nimport type { CORSOptions } from \"./types\";\nimport { MessageType } from \"../types\";\nimport { startKeepalive } from \"./sse-keepalive\";\n\n/**\n * Since we use WebSockets to bridge the client to the\n * MCP transport in the Agent, we use this header to signal\n * the method of the original request the user made, while\n * leaving the WS Upgrade request as GET.\n */\nexport const MCP_HTTP_METHOD_HEADER = \"cf-mcp-method\";\n\n/**\n * Since we use WebSockets to bridge the client to the\n * MCP transport in the Agent, we use this header to include\n * the original request body.\n */\nexport const MCP_MESSAGE_HEADER = \"cf-mcp-message\";\n\nconst MAXIMUM_MESSAGE_SIZE_BYTES = 4 * 1024 * 1024; // 4MB\n\nexport const createStreamingHttpHandler = (\n basePath: string,\n namespace: DurableObjectNamespace<McpAgent>,\n options: {\n corsOptions?: CORSOptions;\n jurisdiction?: DurableObjectJurisdiction;\n } = {}\n) => {\n let pathname = basePath;\n if (basePath === \"/\") pathname = \"/*\";\n\n const basePattern = new URLPattern({ pathname });\n return async (request: Request, ctx: ExecutionContext) => {\n const url = new URL(request.url);\n if (basePattern.test(url)) {\n if (request.method === \"POST\") {\n // Validate the Accept header\n const acceptHeader = request.headers.get(\"accept\");\n // The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.\n if (\n !acceptHeader?.includes(\"application/json\") ||\n !acceptHeader.includes(\"text/event-stream\")\n ) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message:\n \"Not Acceptable: Client must accept both application/json and text/event-stream\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 406 });\n }\n\n const ct = request.headers.get(\"content-type\");\n if (!ct || !ct.includes(\"application/json\")) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message:\n \"Unsupported Media Type: Content-Type must be application/json\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 415 });\n }\n\n // Check content length against maximum allowed size\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") ?? \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message: `Request body too large. Maximum size is ${MAXIMUM_MESSAGE_SIZE_BYTES} bytes`\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 413 });\n }\n\n let sessionId = request.headers.get(\"mcp-session-id\");\n let rawMessage: unknown;\n\n try {\n rawMessage = await request.json();\n } catch (_error) {\n const body = JSON.stringify({\n error: {\n code: -32700,\n message: \"Parse error: Invalid JSON\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // Make sure the message is an array to simplify logic\n let arrayMessage: unknown[];\n if (Array.isArray(rawMessage)) {\n arrayMessage = rawMessage;\n } else {\n arrayMessage = [rawMessage];\n }\n\n let messages: JSONRPCMessage[] = [];\n\n // Try to parse each message as JSON RPC. Fail if any message is invalid\n for (const msg of arrayMessage) {\n if (!JSONRPCMessageSchema.safeParse(msg).success) {\n const body = JSON.stringify({\n error: {\n code: -32700,\n message: \"Parse error: Invalid JSON-RPC message\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n }\n\n messages = arrayMessage.map((msg) => JSONRPCMessageSchema.parse(msg));\n\n // Before we pass the messages to the agent, there's another error condition we need to enforce\n // Check if this is an initialization request\n // https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle/\n const maybeInitializeRequest = messages.find(\n (msg) => InitializeRequestSchema.safeParse(msg).success\n );\n\n if (!!maybeInitializeRequest && sessionId) {\n const body = JSON.stringify({\n error: {\n code: -32600,\n message:\n \"Invalid Request: Initialization requests must not include a sessionId\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // The initialization request must be the only request in the batch\n if (!!maybeInitializeRequest && messages.length > 1) {\n const body = JSON.stringify({\n error: {\n code: -32600,\n message:\n \"Invalid Request: Only one initialization request is allowed\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // If an Mcp-Session-Id is returned by the server during initialization,\n // clients using the Streamable HTTP transport MUST include it\n // in the Mcp-Session-Id header on all of their subsequent HTTP requests.\n if (!maybeInitializeRequest && !sessionId) {\n const body = JSON.stringify({\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 400 });\n }\n\n // If we don't have a sessionId, we are serving an initialization request\n // and need to generate a new sessionId\n sessionId = sessionId ?? namespace.newUniqueId().toString();\n\n // Get the agent and set props\n const agent = await getAgentByName(\n namespace,\n `streamable-http:${sessionId}`,\n {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n }\n );\n const isInitialized = await agent.getInitializeRequest();\n\n if (maybeInitializeRequest) {\n await agent.setInitializeRequest(maybeInitializeRequest);\n } else if (!isInitialized) {\n // if we have gotten here, then a session id that was never initialized\n // was provided\n const body = JSON.stringify({\n error: {\n code: -32001,\n message: \"Session not found\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 404 });\n }\n\n // We've evaluated all the error conditions! Now it's time to establish\n // all the streams\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Connect to the Durable Object via WebSocket\n const existingHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n existingHeaders[key] = value;\n });\n\n const req = new Request(request.url, {\n headers: {\n ...existingHeaders,\n [MCP_HTTP_METHOD_HEADER]: \"POST\",\n [MCP_MESSAGE_HEADER]: Buffer.from(\n JSON.stringify(messages)\n ).toString(\"base64\"),\n Upgrade: \"websocket\"\n }\n });\n const response = await agent.fetch(req);\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n\n await writer.close();\n const body = JSON.stringify({\n error: {\n code: -32001,\n message: \"Failed to establish WebSocket connection\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 500 });\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // If there are no requests, we send the messages to the agent and\n // acknowledge the request with a 202 since we don't expect any\n // responses back through this connection. Decide this *before*\n // arming a keepalive on the SSE writer so we don't leak a timer.\n const hasOnlyNotificationsOrResponses = messages.every(\n (msg) => isJSONRPCNotification(msg) || isJSONRPCResultResponse(msg)\n );\n if (hasOnlyNotificationsOrResponses) {\n // closing the websocket will also close the SSE connection\n ws.close();\n\n return new Response(null, {\n headers: corsHeaders(request, options.corsOptions),\n status: 202\n });\n }\n\n // Long-running tool calls can sit silent for many seconds while\n // the DO runs the handler. Arm a keepalive on the response stream\n // so the Cloudflare edge ~5min idle watchdog doesn't close us\n // before the tool result arrives. POST streams are scoped to a\n // specific request id and can't be resumed via Last-Event-ID,\n // so there's no alternative recovery path here.\n const keepAlive = startKeepalive(writer, encoder);\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", (event) => {\n async function onMessage(event: MessageEvent) {\n try {\n const data =\n typeof event.data === \"string\"\n ? event.data\n : new TextDecoder().decode(event.data);\n const message = JSON.parse(data);\n\n // We only forward events from the MCP server\n if (message.type !== MessageType.CF_MCP_AGENT_EVENT) {\n return;\n }\n\n // Send the message as an SSE event\n await writer.write(encoder.encode(message.event));\n\n // If we have received all the responses, close the connection\n if (message.close) {\n clearInterval(keepAlive);\n ws?.close();\n await writer.close().catch(() => {});\n }\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n }\n onMessage(event).catch(console.error);\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", (error) => {\n async function onError(_error: Event) {\n clearInterval(keepAlive);\n await writer.close().catch(() => {});\n }\n onError(error).catch(console.error);\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", () => {\n async function onClose() {\n clearInterval(keepAlive);\n await writer.close().catch(() => {});\n }\n onClose().catch(console.error);\n });\n\n // Return the SSE response. We handle closing the stream in the ws \"message\"\n // handler\n return new Response(readable, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n \"mcp-session-id\": sessionId,\n ...corsHeaders(request, options.corsOptions)\n },\n status: 200\n });\n } else if (request.method === \"GET\") {\n // Validate the Accept header\n const acceptHeader = request.headers.get(\"accept\");\n // The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.\n if (!acceptHeader?.includes(\"text/event-stream\")) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Not Acceptable: Client must accept text/event-stream\"\n },\n id: null\n });\n return new Response(body, { status: 406 });\n }\n\n // Require sessionId\n const sessionId = request.headers.get(\"mcp-session-id\");\n if (!sessionId)\n return new Response(\n JSON.stringify({\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\"\n },\n id: null,\n jsonrpc: \"2.0\"\n }),\n { status: 400 }\n );\n\n // Create SSE stream\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n const agent = await getAgentByName(\n namespace,\n `streamable-http:${sessionId}`,\n {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n }\n );\n const isInitialized = await agent.getInitializeRequest();\n if (!isInitialized) {\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32001, message: \"Session not found\" },\n id: null\n }),\n { status: 404 }\n );\n }\n\n const existingHeaders: Record<string, string> = {};\n request.headers.forEach((v, k) => {\n existingHeaders[k] = v;\n });\n\n const response = await agent.fetch(\n new Request(request.url, {\n headers: {\n ...existingHeaders,\n [MCP_HTTP_METHOD_HEADER]: \"GET\",\n Upgrade: \"websocket\"\n }\n })\n );\n\n const ws = response.webSocket;\n if (!ws) {\n await writer.close();\n return new Response(\"Failed to establish WS to DO\", {\n status: 500\n });\n }\n ws.accept();\n\n // Forward DO messages as SSE\n ws.addEventListener(\"message\", (event) => {\n try {\n async function onMessage(ev: MessageEvent) {\n const data =\n typeof ev.data === \"string\"\n ? ev.data\n : new TextDecoder().decode(ev.data);\n const message = JSON.parse(data);\n\n // We only forward events from the MCP server\n if (message.type !== MessageType.CF_MCP_AGENT_EVENT) {\n return;\n }\n await writer.write(encoder.encode(message.event));\n }\n onMessage(event).catch(console.error);\n } catch (e) {\n console.error(\"Error forwarding message to SSE:\", e);\n }\n });\n\n ws.addEventListener(\"error\", () => {\n writer.close().catch(() => {});\n });\n ws.addEventListener(\"close\", () => {\n writer.close().catch(() => {});\n });\n\n return new Response(readable, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n \"mcp-session-id\": sessionId,\n ...corsHeaders(request, options.corsOptions)\n },\n status: 200\n });\n } else if (request.method === \"DELETE\") {\n const sessionId = request.headers.get(\"mcp-session-id\");\n if (!sessionId) {\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\"\n },\n id: null\n }),\n { status: 400, headers: corsHeaders(request, options.corsOptions) }\n );\n }\n const agent = await getAgentByName(\n namespace,\n `streamable-http:${sessionId}`,\n { jurisdiction: options.jurisdiction }\n );\n const isInitialized = await agent.getInitializeRequest();\n if (!isInitialized) {\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32001, message: \"Session not found\" },\n id: null\n }),\n { status: 404, headers: corsHeaders(request, options.corsOptions) }\n );\n }\n // Defer the actual teardown to the agent's own alarm invocation\n // (#1625). Running `destroy()` on this request's `waitUntil` was\n // unreliable: the client is usually already gone by the time the\n // DELETE lands, the runtime gives a canceled request's trailing\n // work little to no grace, and the multi-step teardown got cut\n // short — leaving half-deleted session DOs. Scheduling is two fast\n // storage writes, so it is awaited before responding; the alarm\n // then runs the real teardown with a fresh execution budget and a\n // durable marker that survives any further interruption.\n await agent._cf_scheduleDestroy();\n return new Response(null, {\n status: 204,\n headers: corsHeaders(request, options.corsOptions)\n });\n }\n }\n\n // Route not found\n const body = JSON.stringify({\n error: {\n code: -32000,\n message: \"Not found\"\n },\n id: null,\n jsonrpc: \"2.0\"\n });\n return new Response(body, { status: 404 });\n };\n};\n\nexport const createLegacySseHandler = (\n basePath: string,\n namespace: DurableObjectNamespace<McpAgent>,\n options: {\n corsOptions?: CORSOptions;\n jurisdiction?: DurableObjectJurisdiction;\n } = {}\n) => {\n let pathname = basePath;\n if (basePath === \"/\") pathname = \"/*\";\n\n const basePattern = new URLPattern({ pathname });\n const messagePattern = new URLPattern({ pathname: `${basePath}/message` }); // SSE only\n return async (request: Request, ctx: ExecutionContext) => {\n const url = new URL(request.url);\n // Handle initial SSE connection\n if (request.method === \"GET\" && basePattern.test(url)) {\n // Use a session ID if one is passed in, or create a unique\n // session ID for this connection\n const sessionId =\n url.searchParams.get(\"sessionId\") || namespace.newUniqueId().toString();\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Send the endpoint event\n const endpointUrl = new URL(request.url);\n endpointUrl.pathname = encodeURI(`${basePath}/message`);\n endpointUrl.searchParams.set(\"sessionId\", sessionId);\n const relativeUrlWithSession =\n endpointUrl.pathname + endpointUrl.search + endpointUrl.hash;\n const endpointMessage = `event: endpoint\\ndata: ${relativeUrlWithSession}\\n\\n`;\n writer.write(encoder.encode(endpointMessage));\n\n // Get the Durable Object\n const agent = await getAgentByName(namespace, `sse:${sessionId}`, {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n });\n\n // Connect to the Durable Object via WebSocket\n const existingHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n existingHeaders[key] = value;\n });\n const response = await agent.fetch(\n new Request(request.url, {\n headers: {\n ...existingHeaders,\n [MCP_HTTP_METHOD_HEADER]: \"SSE\",\n Upgrade: \"websocket\"\n }\n })\n );\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n await writer.close();\n return new Response(\"Failed to establish WebSocket connection\", {\n status: 500\n });\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", (event) => {\n async function onMessage(event: MessageEvent) {\n try {\n const message = JSON.parse(event.data);\n\n // validate that the message is a valid JSONRPC message\n const result = JSONRPCMessageSchema.safeParse(message);\n if (!result.success) {\n // The message was not a valid JSONRPC message, so we will drop it\n // PartyKit will broadcast state change messages to all connected clients\n // and we need to filter those out so they are not passed to MCP clients\n return;\n }\n\n // Send the message as an SSE event\n const messageText = `event: message\\ndata: ${JSON.stringify(result.data)}\\n\\n`;\n await writer.write(encoder.encode(messageText));\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n }\n onMessage(event).catch(console.error);\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", (error) => {\n async function onError(_error: Event) {\n try {\n await writer.close();\n } catch (_e) {\n // Ignore errors when closing\n }\n }\n onError(error).catch(console.error);\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", () => {\n async function onClose() {\n try {\n await writer.close();\n } catch (error) {\n console.error(\"Error closing SSE connection:\", error);\n }\n }\n onClose().catch(console.error);\n });\n\n // Return the SSE response\n return new Response(readable, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n ...corsHeaders(request, options.corsOptions)\n }\n });\n }\n\n // Handle incoming MCP messages. These will be passed to McpAgent\n // but the response will be sent back via the open SSE connection\n // so we only need to return a 202 Accepted response for success\n if (request.method === \"POST\" && messagePattern.test(url)) {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\n `Missing sessionId. Expected POST to ${basePath} to initiate new one`,\n { status: 400 }\n );\n }\n\n const contentType = request.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"application/json\")) {\n return new Response(`Unsupported content-type: ${contentType}`, {\n status: 400\n });\n }\n\n // check if the request body is too large\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") || \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {\n return new Response(`Request body too large: ${contentLength} bytes`, {\n status: 400\n });\n }\n\n // Get the Durable Object\n const agent = await getAgentByName(namespace, `sse:${sessionId}`, {\n props: ctx.props as Record<string, unknown> | undefined,\n jurisdiction: options.jurisdiction\n });\n\n const messageBody = await request.json();\n\n // Build MessageExtraInfo with filtered headers\n const headers = Object.fromEntries(request.headers.entries());\n\n const extraInfo: MessageExtraInfo = {\n requestInfo: { headers }\n };\n\n const error = await agent.onSSEMcpMessage(\n sessionId,\n messageBody,\n extraInfo\n );\n\n if (error) {\n return new Response(error.message, {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n ...corsHeaders(request, options.corsOptions)\n },\n status: 400\n });\n }\n\n return new Response(\"Accepted\", {\n headers: {\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream\",\n ...corsHeaders(request, options.corsOptions)\n },\n status: 202\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n };\n};\n\n/**\n * Auto-negotiating handler that serves both streamable HTTP and legacy SSE\n * on the same path. Streamable-HTTP-capable clients are preferred; legacy SSE\n * clients fall back transparently.\n *\n * Discrimination rules:\n * - POST to `{basePath}/message` → legacy SSE (the sub-path is SSE-only)\n * - POST to `{basePath}` → streamable HTTP\n * - GET with `mcp-session-id` header → streamable HTTP (standalone SSE reconnect)\n * - GET without `mcp-session-id` → legacy SSE (new SSE connection)\n * - DELETE → streamable HTTP (SSE has no session teardown)\n */\nexport const createAutoHandler = (\n basePath: string,\n namespace: DurableObjectNamespace<McpAgent>,\n options: {\n corsOptions?: CORSOptions;\n jurisdiction?: DurableObjectJurisdiction;\n } = {}\n) => {\n const handleStreamableHttp = createStreamingHttpHandler(\n basePath,\n namespace,\n options\n );\n const handleLegacySse = createLegacySseHandler(basePath, namespace, options);\n\n const messagePattern = new URLPattern({\n pathname: `${basePath}/message`\n });\n\n return async (request: Request, ctx: ExecutionContext) => {\n const url = new URL(request.url);\n\n if (request.method === \"DELETE\") {\n return handleStreamableHttp(request, ctx);\n }\n\n if (request.method === \"POST\" && messagePattern.test(url)) {\n return handleLegacySse(request, ctx);\n }\n\n if (request.method === \"POST\") {\n return handleStreamableHttp(request, ctx);\n }\n\n if (request.method === \"GET\" && request.headers.has(\"mcp-session-id\")) {\n return handleStreamableHttp(request, ctx);\n }\n\n if (request.method === \"GET\") {\n return handleLegacySse(request, ctx);\n }\n\n return new Response(\"Method Not Allowed\", {\n status: 405,\n headers: {\n Allow: \"GET, POST, DELETE\",\n ...corsHeaders(request, options.corsOptions)\n }\n });\n };\n};\n\n// CORS helper functions\nexport function corsHeaders(_request: Request, corsOptions: CORSOptions = {}) {\n const origin = corsOptions.origin || \"*\";\n const headers =\n corsOptions.headers ||\n \"Content-Type, Accept, Authorization, mcp-session-id, mcp-protocol-version\";\n\n return {\n \"Access-Control-Allow-Headers\": headers,\n \"Access-Control-Allow-Methods\":\n corsOptions.methods || \"GET, POST, DELETE, OPTIONS\",\n \"Access-Control-Allow-Origin\": origin,\n \"Access-Control-Expose-Headers\":\n corsOptions.exposeHeaders || \"mcp-session-id\",\n \"Access-Control-Max-Age\": (corsOptions.maxAge || 86400).toString()\n };\n}\n\nexport function handleCORS(\n request: Request,\n corsOptions?: CORSOptions\n): Response | null {\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: corsHeaders(request, corsOptions) });\n }\n\n return null;\n}\n\nexport function isDurableObjectNamespace(\n namespace: unknown\n): namespace is DurableObjectNamespace<McpAgent> {\n return (\n typeof namespace === \"object\" &&\n namespace !== null &&\n \"newUniqueId\" in namespace &&\n typeof namespace.newUniqueId === \"function\" &&\n \"idFromName\" in namespace &&\n typeof namespace.idFromName === \"function\"\n );\n}\n","import type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n type MessageExtraInfo,\n type RequestInfo,\n isJSONRPCErrorResponse,\n isJSONRPCRequest,\n isJSONRPCResultResponse,\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type RequestId\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport type {\n EventStore,\n StreamId,\n EventId\n} from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport { getCurrentAgent, type Connection } from \"..\";\nimport type { McpAgent } from \".\";\nimport { MessageType } from \"../types\";\nimport { MCP_HTTP_METHOD_HEADER, MCP_MESSAGE_HEADER } from \"./utils\";\n\nexport type { EventStore, StreamId, EventId };\n\nexport class McpSSETransport implements Transport {\n sessionId: string;\n // Set by the server in `server.connect(transport)`\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n private _getWebSocket: () => WebSocket | null;\n private _started = false;\n constructor() {\n const { agent } = getCurrentAgent<McpAgent>();\n if (!agent)\n throw new Error(\"McpAgent was not found in Transport constructor\");\n\n this.sessionId = agent.getSessionId();\n this._getWebSocket = () => agent.getWebSocket();\n }\n\n async start() {\n // The transport does not manage the WebSocket connection since it's terminated\n // by the Durable Object in order to allow hibernation. There's nothing to initialize.\n if (this._started) {\n throw new Error(\"Transport already started\");\n }\n this._started = true;\n }\n\n async send(message: JSONRPCMessage) {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n const websocket = this._getWebSocket();\n if (!websocket) {\n throw new Error(\"WebSocket not connected\");\n }\n try {\n websocket.send(JSON.stringify(message));\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n\n async close() {\n // Similar to start, the only thing to do is to pass the event on to the server\n this.onclose?.();\n }\n}\n\n/**\n * Configuration options for StreamableHTTPServerTransport\n */\nexport interface StreamableHTTPServerTransportOptions {\n /**\n * Event store for resumability support.\n * If provided, resumability will be enabled, allowing clients to\n * reconnect and resume messages.\n *\n * If the store also implements {@link ClearableEventStore.clearStream}\n * the transport will call it after the final response of a POST\n * stream is written, so storage stays bounded without any background\n * sweep. {@link DurableObjectEventStore} is the canonical example.\n */\n eventStore?: EventStore | ClearableEventStore;\n}\n\n/**\n * An {@link EventStore} that supports dropping all events for a single\n * stream id. Implemented by {@link DurableObjectEventStore}.\n */\nexport interface ClearableEventStore extends EventStore {\n clearStream(streamId: StreamId): Promise<void>;\n}\n\nfunction isClearableEventStore(\n store: EventStore | ClearableEventStore\n): store is ClearableEventStore {\n return typeof (store as ClearableEventStore).clearStream === \"function\";\n}\n\n/**\n * Adapted from: https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts\n * - Validation and initialization are removed as they're handled in `McpAgent.serve()` handler.\n * - Replaces the Node-style `req`/`res` with Worker's `Request`.\n * - Writes events as WS messages that the Worker forwards to the client as SSE events.\n * - Replaces the in-memory maps that track requestID/stream by using `connection.setState()` and `agent.getConnections()`.\n *\n * Besides these points, the implementation is the same and should be updated to match the original as new features are added.\n */\n/** Fixed streamId for the standalone GET listen stream. */\nconst STANDALONE_STREAM_ID = \"_GET_stream\";\n\n/** State persisted on each WebSocket connection by the transport. */\ntype TransportConnState = {\n /** Stable identifier for the SSE stream this connection serves.\n * Used as the event-store key. Survives WS reconnects via Last-Event-ID. */\n streamId?: string;\n /** True iff this connection is the standalone GET listen stream. */\n _standaloneSse?: boolean;\n /** Request ids whose responses must flow through this connection. */\n requestIds?: RequestId[];\n};\n\nexport class StreamableHTTPServerTransport implements Transport {\n private _started = false;\n private _eventStore?: EventStore | ClearableEventStore;\n\n // This tracks which messages on each POST stream have been answered.\n // It is fine that we do not persist this since it only supports backwards\n // compatibility for clients batching requests, which the spec discourages.\n // Keying by stream avoids colliding ids on independent POST streams sharing\n // completion state with one another.\n private _streamResponseIds: Map<string, Set<RequestId>> = new Map();\n\n sessionId: string;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n /**\n * Optional message interceptor that can intercept messages before they are passed to onmessage.\n * If the interceptor returns true, the message is considered handled and won't be forwarded.\n * This is used by McpAgent to intercept elicitation responses.\n */\n messageInterceptor?: (\n message: JSONRPCMessage,\n extra?: MessageExtraInfo\n ) => Promise<boolean>;\n\n constructor(options: StreamableHTTPServerTransportOptions) {\n const { agent } = getCurrentAgent<McpAgent>();\n if (!agent)\n throw new Error(\"McpAgent was not found in Transport constructor\");\n\n // Initialization is handled in `McpAgent.serve()` and agents are addressed by sessionId,\n // so we'll always have this available.\n this.sessionId = agent.getSessionId();\n this._eventStore = options.eventStore;\n }\n\n /**\n * Starts the transport. This is required by the Transport interface but is a no-op\n * for the Streamable HTTP transport as connections are managed per-request.\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\"Transport already started\");\n }\n this._started = true;\n }\n\n /**\n * Handles GET requests for SSE stream.\n *\n * Two roles a GET can play:\n * 1. Fresh standalone listen stream — carries server-initiated\n * requests/notifications unrelated to any in-progress POST.\n * 2. Resumption of a previously-disconnected stream via\n * `Last-Event-ID`. The disconnected stream may have been the\n * standalone stream OR a POST tool-call response stream; per the\n * MCP 2025-03-26 spec the server replays missed messages \"on the\n * stream that was disconnected\" and continues delivering\n * subsequent messages on that same stream.\n *\n * To resume a POST stream we recover the original streamId from the\n * event-store and the original `requestIds` from durable storage,\n * then write them onto the new WS connection so `send()` keeps\n * routing in-flight tool responses to it.\n */\n async handleGetRequest(req: Request): Promise<void> {\n const { connection, agent } = getCurrentAgent<McpAgent>();\n if (!connection)\n throw new Error(\"Connection was not found in handleGetRequest\");\n if (!agent) throw new Error(\"Agent was not found in handleGetRequest\");\n\n const lastEventId = req.headers.get(\"last-event-id\");\n\n // Resume path: the client identifies which stream it lost via\n // Last-Event-ID. Recover the original streamId from the event\n // store and register this connection under it. Matches the SDK\n // reference implementation (typescript-sdk's `replayEvents`):\n // the resumed connection is mapped to the *original* streamId,\n // no dual-role tagging.\n //\n // Forward routing then depends on what kind of stream it was:\n // - active POST: persisted requestIds are restored so further\n // tool responses route to this new WS via state.requestIds.\n // - standalone listen stream: tag with _standaloneSse so\n // server-initiated notifications continue to land here.\n // - completed POST / unknown: this connection is a one-shot\n // replay channel. No future messages will be routed to it.\n //\n // In every resumable case we supersede any prior connection bound\n // to the same streamId by closing it, so there is at most one live\n // connection per stream. This keeps `send()` routing deterministic\n // and keeps us within the MCP rule that each message goes out on\n // exactly one stream.\n if (this._eventStore && lastEventId) {\n const resumedStreamId =\n await this._eventStore.getStreamIdForEventId?.(lastEventId);\n if (resumedStreamId) {\n const resumeState: TransportConnState = {\n streamId: resumedStreamId\n };\n if (resumedStreamId === STANDALONE_STREAM_ID) {\n resumeState._standaloneSse = true;\n } else {\n const persistedReqs =\n await agent.getStreamRequestIds(resumedStreamId);\n if (persistedReqs && persistedReqs.length > 0) {\n resumeState.requestIds = persistedReqs;\n }\n }\n this.supersedePriorStreamConnections(\n agent,\n connection.id,\n resumedStreamId\n );\n connection.setState(resumeState);\n await this.replayEvents(lastEventId);\n return;\n }\n }\n\n // Fresh standalone listen stream. The MCP spec allows only one\n // standalone GET per session, so supersede any existing one.\n this.supersedePriorStreamConnections(\n agent,\n connection.id,\n STANDALONE_STREAM_ID\n );\n const standaloneState: TransportConnState = {\n streamId: STANDALONE_STREAM_ID,\n _standaloneSse: true\n };\n connection.setState(standaloneState);\n }\n\n /**\n * Close any connection (other than `selfId`) currently bound to\n * `streamId`, so at most one live connection serves a given stream.\n * Closing rather than mutating sibling state mirrors how the SDK's\n * single `_streamMapping` entry gives last-writer-wins for free, and\n * keeps `send()` from routing to a stale bridge.\n */\n private supersedePriorStreamConnections(\n agent: McpAgent,\n selfId: string,\n streamId: string\n ): void {\n for (const other of agent.getConnections<TransportConnState>()) {\n if (other.id === selfId) continue;\n if (other.state?.streamId !== streamId) continue;\n other.close(1000, \"Superseded by resumed stream\");\n }\n }\n\n /**\n * Replays events that would have been sent after the specified event ID\n * Only used when resumability is enabled\n */\n private async replayEvents(lastEventId: string): Promise<void> {\n if (!this._eventStore) {\n return;\n }\n\n const { connection } = getCurrentAgent();\n if (!connection)\n throw new Error(\"Connection was not available in replayEvents\");\n\n try {\n await this._eventStore?.replayEventsAfter(lastEventId, {\n send: async (eventId: string, message: JSONRPCMessage) => {\n try {\n this.writeSSEEvent(connection, message, eventId);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n });\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n\n /**\n * Writes an event to the SSE stream with proper formatting\n */\n private writeSSEEvent(\n connection: Connection,\n message: JSONRPCMessage,\n eventId?: string,\n close?: boolean\n ) {\n let eventData = \"event: message\\n\";\n // Include event ID if provided - this is important for resumability\n if (eventId) {\n eventData += `id: ${eventId}\\n`;\n }\n eventData += `data: ${JSON.stringify(message)}\\n\\n`;\n\n return connection.send(\n JSON.stringify({\n type: MessageType.CF_MCP_AGENT_EVENT,\n event: eventData,\n close\n })\n );\n }\n\n /**\n * Handles POST requests containing JSON-RPC messages\n */\n async handlePostRequest(\n req: Request & { auth?: AuthInfo },\n parsedBody: unknown\n ): Promise<void> {\n const authInfo: AuthInfo | undefined = req.auth;\n const requestInfo: RequestInfo = {\n headers: Object.fromEntries(req.headers.entries()),\n url: new URL(req.url)\n };\n // Remove headers that are not part of the original request\n delete requestInfo.headers[MCP_HTTP_METHOD_HEADER];\n delete requestInfo.headers[MCP_MESSAGE_HEADER];\n delete requestInfo.headers.upgrade;\n\n const rawMessage = parsedBody;\n let messages: JSONRPCMessage[];\n\n // handle batch and single messages\n if (Array.isArray(rawMessage)) {\n messages = rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg));\n } else {\n messages = [JSONRPCMessageSchema.parse(rawMessage)];\n }\n\n // check if it contains requests\n const hasRequests = messages.some(isJSONRPCRequest);\n\n if (!hasRequests) {\n // We process without sending anything\n for (const message of messages) {\n // check if message should be intercepted (i.e. elicitation responses)\n if (this.messageInterceptor) {\n const handled = await this.messageInterceptor(message, {\n authInfo,\n requestInfo\n });\n if (handled) {\n continue; // msg was handled by interceptor, skip onmessage\n }\n }\n this.onmessage?.(message, { authInfo, requestInfo });\n }\n } else if (hasRequests) {\n const { connection, agent } = getCurrentAgent<McpAgent>();\n if (!connection)\n throw new Error(\"Connection was not found in handlePostRequest\");\n if (!agent) throw new Error(\"Agent was not found in handlePostRequest\");\n\n // We need to track by request ID to maintain the connection\n const requestIds = messages\n .filter(isJSONRPCRequest)\n .map((message) => message.id);\n\n // The streamId is stable for the lifetime of this POST's stream.\n // We seed it with the WS connection id (unique per POST), and a\n // resumed GET later inherits the *same* streamId via Last-Event-ID.\n const streamId = connection.id;\n const postState: TransportConnState = { streamId, requestIds };\n connection.setState(postState);\n\n // Persist the mapping so a future GET-with-Last-Event-ID can\n // restore `requestIds` onto a fresh WS connection. Only relevant\n // when an event store is configured — without one the client has\n // no `id:` to resume from anyway. Cleaned up in `send()` on the\n // final response.\n if (this._eventStore) {\n await agent.setStreamRequestIds(streamId, requestIds);\n }\n\n // handle each message\n for (const message of messages) {\n if (this.messageInterceptor) {\n const handled = await this.messageInterceptor(message, {\n authInfo,\n requestInfo\n });\n if (handled) {\n continue; // Message was handled by interceptor, skip onmessage\n }\n }\n this.onmessage?.(message, { authInfo, requestInfo });\n }\n // The server SHOULD NOT close the SSE stream before sending all JSON-RPC responses\n // This will be handled by the send() method when responses are ready\n }\n }\n\n async close(): Promise<void> {\n // Close all SSE connections\n const { agent } = getCurrentAgent();\n if (!agent) throw new Error(\"Agent was not found in close\");\n\n for (const conn of agent.getConnections()) {\n conn.close(1000, \"Session closed\");\n }\n this.onclose?.();\n }\n\n /**\n * Store the event, decide whether this is the final response, write\n * the SSE frame iff a live connection is attached, then run cleanup.\n * Caller resolves `streamId` and `relatedIds` (from connection state\n * or persisted reverse lookup) and passes `liveConnection` as null\n * when the originating WS has dropped.\n */\n private async sendOnStream(\n agent: McpAgent,\n streamId: string,\n relatedIds: readonly RequestId[],\n liveConnection: Connection<TransportConnState> | null,\n message: JSONRPCMessage,\n requestId: RequestId\n ): Promise<void> {\n const eventId = await this._eventStore?.storeEvent(streamId, message);\n\n let shouldClose = false;\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n let responseIds = this._streamResponseIds.get(streamId);\n if (!responseIds) {\n responseIds = new Set<RequestId>();\n this._streamResponseIds.set(streamId, responseIds);\n }\n responseIds.add(requestId);\n shouldClose = relatedIds.every((id) => responseIds.has(id));\n if (shouldClose) this._streamResponseIds.delete(streamId);\n }\n\n // Write FIRST, clean up SECOND. Clearing before the write would\n // leave a mid-flight client with a wiped stream on reconnect.\n // `writeSSEEvent` is sync (enqueues, doesn't await), so the bytes\n // are committed before any cleanup await can interleave. Wrap in\n // try/catch so a dead WS can't skip cleanup and orphan the\n // stream-reqs + stored events.\n if (liveConnection) {\n try {\n this.writeSSEEvent(liveConnection, message, eventId, shouldClose);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n\n if (shouldClose) {\n // A concurrent GET resume between these awaits would replay\n // events about to be deleted — benign.\n await agent.deleteStreamRequestIds(streamId);\n if (this._eventStore && isClearableEventStore(this._eventStore)) {\n await this._eventStore.clearStream(streamId);\n }\n }\n }\n\n async send(\n message: JSONRPCMessage,\n options?: { relatedRequestId?: RequestId }\n ): Promise<void> {\n // Request-scoped (response / `relatedRequestId` notification) vs\n // server-initiated on the standalone GET stream. Two helpers.\n const isResponse =\n isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);\n const requestId = isResponse ? message.id : options?.relatedRequestId;\n\n if (requestId === undefined) {\n if (isResponse) {\n throw new Error(\n \"Cannot send a response on a standalone SSE stream unless resuming a previous client request\"\n );\n }\n return this.sendStandalone(message);\n }\n\n return this.sendForRequest(message, requestId);\n }\n\n /**\n * Server-initiated message on the standalone GET stream. Stored under\n * a fixed streamId so it's replayable even when no live connection is\n * currently attached.\n *\n * Sent on exactly one stream, per MCP: \"the server MUST send each of\n * its JSON-RPC messages on only one of the connected streams; it MUST\n * NOT broadcast the same message across multiple streams.\"\n * `handleGetRequest` supersedes prior standalone connections, so\n * there is at most one to send on.\n */\n private async sendStandalone(message: JSONRPCMessage): Promise<void> {\n const { agent } = getCurrentAgent<McpAgent>();\n if (!agent) throw new Error(\"Agent was not found in send\");\n\n const eventId = await this._eventStore?.storeEvent(\n STANDALONE_STREAM_ID,\n message\n );\n\n const standalone = Array.from(\n agent.getConnections<TransportConnState>()\n ).find((conn) => conn.state?._standaloneSse);\n // No live standalone stream: the event is stored above and replays\n // when a client reconnects with Last-Event-ID. Per spec the server\n // MAY send on the stream, so dropping the live write is fine.\n if (standalone) {\n this.writeSSEEvent(standalone, message, eventId);\n }\n }\n\n /**\n * Message scoped to a specific in-flight client request: a tool\n * response, error, or progress notification. Resolves which stream\n * owns the request id (live POST connection, resumed GET, or\n * persisted reverse lookup for a dropped WS) and delegates to\n * {@link sendOnStream} for the actual store / write / cleanup.\n */\n private async sendForRequest(\n message: JSONRPCMessage,\n requestId: RequestId\n ): Promise<void> {\n const { agent, connection: originatingConnection } =\n getCurrentAgent<McpAgent>();\n if (!agent) throw new Error(\"Agent was not found in send\");\n\n // Pick the live connection that should receive this message. Normally\n // request ids uniquely identify a POST connection. If a client violates\n // that constraint, prefer the connection whose handler is currently\n // producing this message rather than leaking a plausible response to\n // the first matching POST stream. Only prefer an originating connection\n // while it is still live: after a POST stream disconnects, a resumed\n // GET connection inherits requestIds and must be allowed to receive\n // the eventual response.\n const matchingConnections = Array.from(\n agent.getConnections<TransportConnState>()\n ).filter((conn) => conn.state?.requestIds?.includes(requestId));\n const liveConnection =\n matchingConnections.find(\n (conn) => conn.id === originatingConnection?.id\n ) ?? (matchingConnections.length === 1 ? matchingConnections[0] : null);\n\n // Ambiguous routing: multiple live POST connections claim the same\n // request id, none of which is the originating connection. Terminate\n // each with a protocol error rather than guessing.\n if (!liveConnection && matchingConnections.length > 1) {\n const routingError: JSONRPCMessage = {\n jsonrpc: \"2.0\",\n id: requestId,\n error: { code: -32603, message: \"Internal error\" }\n };\n await Promise.all(\n matchingConnections.map((candidate) =>\n this.sendOnStream(\n agent,\n candidate.state?.streamId ?? candidate.id,\n candidate.state?.requestIds ?? [],\n candidate,\n routingError,\n requestId\n )\n )\n );\n return;\n }\n\n // Resolve streamId + relatedIds. Prefer the live connection's state;\n // when the originating WS has dropped fall back to the persisted\n // reverse lookup so the event can still be stored for replay —\n // mirrors the SDK's `_requestToStreamMapping` which outlives\n // connection loss.\n let streamId = liveConnection?.state?.streamId;\n let relatedIds = liveConnection?.state?.requestIds;\n if (!streamId) {\n const stored = await agent.getStreamForRequestId(requestId);\n if (!stored) {\n throw new Error(\n `No active stream found for request ID: ${String(requestId)}`\n );\n }\n streamId = stored.streamId;\n relatedIds = stored.requestIds;\n }\n\n await this.sendOnStream(\n agent,\n streamId,\n relatedIds ?? [],\n liveConnection,\n message,\n requestId\n );\n }\n}\n","import type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport type {\n EventStore,\n EventId,\n StreamId\n} from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\n\n/**\n * Durable Object–backed {@link EventStore} for SSE resumability.\n *\n * Default for `McpAgent`. Override `McpAgent.getEventStore()` to swap\n * or disable.\n *\n * ## Storage layout\n *\n * Events are stored under `__mcp_event__:<streamId>:<seqHex>`, where\n * `<seqHex>` is a 16-char zero-padded counter so events in a stream\n * sort lexicographically and `getStreamIdForEventId` can recover the\n * stream from `eventId` without a storage hit.\n *\n * ## Lifecycle\n *\n * Each POST tool-call stream's events live only until the final\n * response is delivered. The transport calls {@link clearStream}\n * immediately after writing the close frame, so storage growth is\n * bounded by the in-flight POST streams plus the standalone GET\n * stream. There is no background sweep — quiescent agents do no work,\n * and the DO itself dies with the session.\n *\n * Standalone GET stream events (`_GET_stream`) are *not* cleared\n * automatically; they accumulate for the lifetime of the DO. Bounded\n * by session length in practice.\n *\n * Trade-off: if the client TCP connection dies *after* the close\n * frame has been enqueued on the WS but before the bytes reach the\n * client, the final message is unreplayable. Every earlier event in\n * the stream is still replayable while the in-flight stream is open.\n *\n * ## Stream id constraints\n *\n * `streamId` MUST NOT contain `:`. `storeEvent` asserts this so\n * embedders using custom stream ids fail loudly rather than risk\n * prefix-scan collisions (e.g. clearing `a` accidentally hitting\n * `a:b`). Default ids (`connection.id` UUIDs and the literal\n * `_GET_stream`) already satisfy this.\n */\nexport class DurableObjectEventStore implements EventStore {\n private static readonly EVENT_KEY_PREFIX = \"__mcp_event__:\";\n private static readonly SEQ_PAD = 16;\n /** DO storage caps multi-key delete at 128. */\n private static readonly DELETE_CHUNK = 128;\n /** Defensive ceiling on a single replay batch. A live stream's\n * event count is small (progress notifications + final result);\n * this is here so a pathological history can't OOM the DO. */\n private static readonly REPLAY_LIMIT = 1000;\n\n private readonly storage: DurableObjectStorage;\n\n /** In-memory seq counters per stream, rehydrated lazily from storage. */\n private readonly seqByStream = new Map<StreamId, number>();\n private readonly seqInit = new Map<StreamId, Promise<void>>();\n\n constructor(storage: DurableObjectStorage) {\n this.storage = storage;\n }\n\n async storeEvent(\n streamId: StreamId,\n message: JSONRPCMessage\n ): Promise<EventId> {\n if (streamId.includes(\":\")) {\n // Event keys are `__mcp_event__:<streamId>:<seqHex>` — a `:` in\n // streamId would let prefix scans cross stream boundaries.\n throw new Error(\n `DurableObjectEventStore: streamId must not contain ':' (got ${JSON.stringify(streamId)})`\n );\n }\n await this.ensureSeqLoaded(streamId);\n const seq = (this.seqByStream.get(streamId) ?? 0) + 1;\n this.seqByStream.set(streamId, seq);\n\n const seqHex = seq\n .toString(16)\n .padStart(DurableObjectEventStore.SEQ_PAD, \"0\");\n const eventId = `${streamId}:${seqHex}`;\n const eventKey = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${eventId}`;\n\n await this.storage.put(eventKey, message);\n return eventId;\n }\n\n async getStreamIdForEventId(eventId: EventId): Promise<StreamId | undefined> {\n const idx = eventId.lastIndexOf(\":\");\n return idx > 0 ? eventId.slice(0, idx) : undefined;\n }\n\n async replayEventsAfter(\n lastEventId: EventId,\n {\n send\n }: { send: (eventId: EventId, message: JSONRPCMessage) => Promise<void> }\n ): Promise<StreamId> {\n const streamId = await this.getStreamIdForEventId(lastEventId);\n if (!streamId) return \"\";\n\n const prefix = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${streamId}:`;\n // `list({ start })` is inclusive, and we want strictly-after\n // semantics. Appending `\\x00` (the smallest byte) to the last\n // event's key produces a key that sorts immediately after it, so\n // the list excludes the boundary event without a post-filter.\n const startKey = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${lastEventId}\\x00`;\n // DO `storage.list()` with no `limit` loads everything into memory.\n // Stream histories are normally small (progress events + result),\n // but cap the batch defensively. Clients can reconnect again to\n // drain past the cap if they ever produce that many events.\n const rows = await this.storage.list<JSONRPCMessage>({\n prefix,\n start: startKey,\n limit: DurableObjectEventStore.REPLAY_LIMIT\n });\n\n for (const [key, message] of rows) {\n const eventId = key.slice(\n DurableObjectEventStore.EVENT_KEY_PREFIX.length\n );\n await send(eventId, message);\n }\n return streamId;\n }\n\n /**\n * Drop the event log for a single stream. Called by the transport\n * immediately after a POST's final response has been written to the\n * wire — no future `Last-Event-ID` for this stream is expected to\n * resolve.\n *\n * Lists and deletes in chunks of {@link DELETE_CHUNK} (128, the DO\n * storage cap) so we never load the entire event log into memory.\n * After deleting, the next `list` call won't see the deleted keys,\n * so passing `start: <prefix>` again is enough — no cursor bookkeeping.\n */\n async clearStream(streamId: StreamId): Promise<void> {\n const prefix = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${streamId}:`;\n for (;;) {\n const rows = await this.storage.list({\n prefix,\n limit: DurableObjectEventStore.DELETE_CHUNK\n });\n if (rows.size === 0) break;\n await this.storage.delete([...rows.keys()]);\n }\n this.seqByStream.delete(streamId);\n this.seqInit.delete(streamId);\n }\n\n private async ensureSeqLoaded(streamId: StreamId): Promise<void> {\n if (this.seqByStream.has(streamId)) return;\n let pending = this.seqInit.get(streamId);\n if (!pending) {\n pending = (async () => {\n const prefix = `${DurableObjectEventStore.EVENT_KEY_PREFIX}${streamId}:`;\n const rows = await this.storage.list({\n prefix,\n reverse: true,\n limit: 1\n });\n let seq = 0;\n for (const key of rows.keys()) {\n const parsed = Number.parseInt(key.slice(prefix.length), 16);\n if (Number.isFinite(parsed)) seq = parsed;\n }\n if (!this.seqByStream.has(streamId)) {\n this.seqByStream.set(streamId, seq);\n }\n })();\n this.seqInit.set(streamId, pending);\n }\n try {\n await pending;\n } finally {\n this.seqInit.delete(streamId);\n }\n }\n}\n","/**\n * Deprecated transport wrappers\n */\n\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { SSEClientTransportOptions } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { StreamableHTTPClientTransportOptions } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n\nlet didWarnAboutSSEEdgeClientTransport = false;\n\n/**\n * @deprecated Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. This alias will be removed in the next major version.\n */\nexport class SSEEdgeClientTransport extends SSEClientTransport {\n constructor(url: URL, options: SSEClientTransportOptions) {\n super(url, options);\n if (!didWarnAboutSSEEdgeClientTransport) {\n didWarnAboutSSEEdgeClientTransport = true;\n console.warn(\n \"SSEEdgeClientTransport is deprecated. Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. SSEEdgeClientTransport will be removed in the next major version.\"\n );\n }\n }\n}\n\nlet didWarnAboutStreamableHTTPEdgeClientTransport = false;\n\n/**\n * @deprecated Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. This alias will be removed in the next major version.\n */\nexport class StreamableHTTPEdgeClientTransport extends StreamableHTTPClientTransport {\n constructor(url: URL, options: StreamableHTTPClientTransportOptions) {\n super(url, options);\n if (!didWarnAboutStreamableHTTPEdgeClientTransport) {\n didWarnAboutStreamableHTTPEdgeClientTransport = true;\n console.warn(\n \"StreamableHTTPEdgeClientTransport is deprecated. Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. StreamableHTTPEdgeClientTransport will be removed in the next major version.\"\n );\n }\n }\n}\n","/**\n * WorkerTransport\n *\n * Thin Cloudflare-Workers wrapper around the official MCP SDK\n * `WebStandardStreamableHTTPServerTransport`. The wrapper layers a couple of\n * Workers-specific concerns on top of the SDK transport without forking it:\n *\n * 1. **CORS** — preflight handling and response-header injection,\n * configurable via `corsOptions`.\n * 2. **Persistent transport state** — when a `storage` adapter\n * (`MCPStorageApi`) is supplied, the wrapper persists\n * `{sessionId, initialized, initializeParams}` so that an MCP session can\n * survive DO hibernation / eviction. On the first request after a cold\n * start, the saved initialize params are replayed through the `Server`\n * so client capabilities are re-established.\n * 3. **SSE keepalive** — SSE responses are wrapped in a TransformStream that\n * injects a `: keepalive\\n\\n` comment frame every 25s so the Cloudflare\n * edge ~5min idle-stream watchdog doesn't kill long-running tool calls.\n * Disabled on the standalone GET stream when an `eventStore` is\n * configured — clients recover idle drops via `Last-Event-ID` instead.\n * POST response streams always keepalive (no resumption path during a\n * mid-flight tool call). See cloudflare/agents#1583.\n *\n * Everything else (session validation, SSE streaming, protocol-version\n * negotiation, event-store resumability, etc.) is delegated to the SDK\n * transport.\n */\n\nimport {\n WebStandardStreamableHTTPServerTransport,\n type WebStandardStreamableHTTPServerTransportOptions\n} from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport type { TransportSendOptions } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n isInitializeRequest,\n isJSONRPCErrorResponse,\n isJSONRPCResultResponse,\n type InitializeRequestParams,\n type JSONRPCMessage,\n type RequestId\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { CORSOptions } from \"./types\";\nimport { KEEPALIVE_FRAME, KEEPALIVE_INTERVAL_MS } from \"./sse-keepalive\";\n\n/** Sentinel id used when replaying the persisted initialize request. */\nconst RESTORE_REQUEST_ID = \"__worker_transport_restore__\";\n\n/**\n * Pluggable storage adapter for persisting `WorkerTransport` state across\n * Durable Object hibernation / restart cycles.\n *\n * A typical implementation reads/writes a single key on `this.ctx.storage`\n * inside a Durable Object or Agent.\n */\nexport interface MCPStorageApi {\n get(): Promise<TransportState | undefined> | TransportState | undefined;\n set(state: TransportState): Promise<void> | void;\n}\n\n/** Shape of the persisted transport state. */\nexport interface TransportState {\n sessionId?: string;\n initialized: boolean;\n initializeParams?: InitializeRequestParams;\n}\n\nconst DEFAULT_CORS_OPTIONS: Required<\n Pick<\n CORSOptions,\n \"origin\" | \"headers\" | \"methods\" | \"exposeHeaders\" | \"maxAge\"\n >\n> = {\n origin: \"*\",\n headers:\n \"Content-Type, Accept, Authorization, mcp-session-id, MCP-Protocol-Version\",\n methods: \"GET, POST, DELETE, OPTIONS\",\n exposeHeaders: \"mcp-session-id\",\n maxAge: 86400\n};\n\nexport interface WorkerTransportOptions extends WebStandardStreamableHTTPServerTransportOptions {\n /**\n * CORS options applied to every response and to OPTIONS preflight.\n * Defaults: `origin: *`, expose `mcp-session-id`, allow the standard MCP\n * methods/headers, max-age 86400.\n */\n corsOptions?: CORSOptions;\n /**\n * Optional storage adapter for persisting transport state across DO\n * hibernation / restart. Use this to keep an MCP session alive across\n * Durable Object wake-ups.\n */\n storage?: MCPStorageApi;\n}\n\nexport class WorkerTransport extends WebStandardStreamableHTTPServerTransport {\n private readonly _corsOptions?: CORSOptions;\n private readonly _storage?: MCPStorageApi;\n private _stateRestored = false;\n private _capturedInitializeParams?: InitializeRequestParams;\n private _userOnSessionInitialized?: (\n sessionId: string\n ) => void | Promise<void>;\n private _bridgeInstalled = false;\n /**\n * Tracks keepalive interval cleanups so we can fire them eagerly when the\n * SDK closes the underlying SSE stream via `closeSSEStream(requestId)` or\n * `closeStandaloneSSEStream()`. Keyed by the JSON-RPC request id that\n * triggered the stream, or the sentinel for the standalone GET stream.\n */\n private readonly _keepaliveCleanups = new Map<\n RequestId | \"_standalone\",\n () => void\n >();\n /**\n * Most recent JSON-RPC request id seen on an incoming POST. Used to key\n * keepalive cleanups when the response is an SSE stream tied to that\n * request (so `closeSSEStream(id)` can find and clear the interval).\n */\n private _pendingRequestId?: RequestId;\n /**\n * Request ids whose SSE stream was deliberately torn down via\n * `closeSSEStream`. The SDK's `send()` throws \"No connection established\"\n * when a request id has no stream — a race that surfaces whenever the\n * server's tool handler resolves *after* the caller closed the stream\n * (e.g. polling-style early-close, or test fixtures closing mid-flight).\n * We swallow `send()` for these ids so the rejection doesn't bubble out\n * of the protocol layer as an unhandled rejection. Mirrors the\n * silent-noop behaviour of the pre-refactor `WorkerTransport`.\n */\n private readonly _closedRequestIds = new Set<RequestId>();\n\n constructor(options: WorkerTransportOptions = {}) {\n const { corsOptions, storage, onsessioninitialized, ...sdkOptions } =\n options;\n\n // `storage` is intentionally orthogonal to statefulness: stateful-vs-\n // stateless behaviour is driven solely by the SDK's `sessionIdGenerator`.\n // `storage` only persists whatever session state exists across DO\n // hibernation, so it's used alongside a `sessionIdGenerator`.\n //\n // We wrap onsessioninitialized so we can persist state to storage as soon\n // as the SDK transport assigns a session ID. The bridge gets installed\n // lazily on the first request so `this` is fully constructed when it fires.\n super({\n ...sdkOptions,\n onsessioninitialized: undefined\n });\n\n this._corsOptions = corsOptions;\n this._storage = storage;\n this._userOnSessionInitialized = onsessioninitialized;\n }\n\n /**\n * Backwards-compatible alias for the SDK's internal `_started` flag.\n * Several callers and tests check `transport.started` directly.\n */\n get started(): boolean {\n return (this as unknown as { _started: boolean })._started;\n }\n\n /**\n * Top-level request entry point. Handles CORS preflight, restores any\n * persisted state on first invocation, then delegates to the SDK transport\n * and finally appends CORS headers + keepalive to whatever response comes\n * back.\n */\n override async handleRequest(\n request: Request,\n options?: { parsedBody?: unknown; authInfo?: AuthInfo }\n ): Promise<Response> {\n if (request.method === \"OPTIONS\") {\n return new Response(null, {\n headers: this.getCorsHeaders({ forPreflight: true })\n });\n }\n\n await this.restoreState();\n this.installOnSessionInitializedBridge();\n\n // Capture the initialize params before delegating, so we can persist\n // them alongside the session id that the SDK assigns inside\n // handleRequest. Also captures the JSON-RPC request id of any POSTed\n // request so we can key the keepalive cleanup to it.\n await this.captureInitializeParams(request, options);\n const requestIdForKeepalive =\n request.method === \"GET\" ? \"_standalone\" : this._pendingRequestId;\n\n const response = await super.handleRequest(request, options);\n\n // State is persisted by the `onsessioninitialized` bridge, which the SDK\n // fires (and awaits) during `super.handleRequest` on the initialize path —\n // the only point session state actually changes. We deliberately do *not*\n // snapshot again here: that would write to storage on every request\n // (notifications, tool calls, GET, DELETE) where nothing changed, matching\n // neither the pre-refactor behaviour (one write at init) nor the intent of\n // the storage adapter.\n\n return this.withCorsHeaders(\n this.withKeepalive(\n this.normalizeAllowHeader(response),\n requestIdForKeepalive\n )\n );\n }\n\n /**\n * The SDK's 405 responses advertise `Allow: GET, POST, DELETE` because\n * OPTIONS is handled outside the SDK. Since our wrapper *does* handle\n * OPTIONS, advertise it in `Allow` so clients can probe accurately.\n */\n private normalizeAllowHeader(response: Response): Response {\n if (response.status !== 405) return response;\n const allow = response.headers.get(\"Allow\");\n if (!allow || allow.includes(\"OPTIONS\")) return response;\n const headers = new Headers(response.headers);\n headers.set(\"Allow\", `${allow}, OPTIONS`);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers\n });\n }\n\n override closeSSEStream(requestId: RequestId): void {\n this._keepaliveCleanups.get(requestId)?.();\n this._keepaliveCleanups.delete(requestId);\n this._closedRequestIds.add(requestId);\n super.closeSSEStream(requestId);\n }\n\n override closeStandaloneSSEStream(): void {\n this._keepaliveCleanups.get(\"_standalone\")?.();\n this._keepaliveCleanups.delete(\"_standalone\");\n super.closeStandaloneSSEStream();\n }\n\n override async close(): Promise<void> {\n for (const cleanup of Array.from(this._keepaliveCleanups.values())) {\n cleanup();\n }\n this._keepaliveCleanups.clear();\n this._closedRequestIds.clear();\n await super.close();\n }\n\n /**\n * Swallow two classes of message that would otherwise surface as\n * unhandled rejections from the SDK transport's `send()`:\n *\n * 1. Replayed initialize responses (the `RESTORE_REQUEST_ID` sentinel)\n * — we synthesise these in `restoreState()` to rebuild server\n * capabilities; there's no real client waiting for the response.\n * 2. Sends for a request id whose SSE stream has been deliberately\n * closed via `closeSSEStream`. The protocol layer's tool-handler\n * promise may settle after the close, and the SDK's `send()` throws\n * \"No connection established\" — a race the pre-refactor transport\n * silently swallowed.\n *\n * Everything else is delegated. We use `await super.send(...)` rather\n * than `return super.send(...)` so any rejection is observed inside this\n * async frame; without the await, the test runner's\n * unhandled-rejection tracker can fire before the caller's own `await`\n * observes it.\n */\n override async send(\n message: JSONRPCMessage,\n options?: TransportSendOptions\n ): Promise<void> {\n let requestId: RequestId | undefined = options?.relatedRequestId;\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n requestId = message.id;\n }\n if (requestId === RESTORE_REQUEST_ID) {\n return;\n }\n if (requestId !== undefined && this._closedRequestIds.has(requestId)) {\n return;\n }\n await super.send(message, options);\n }\n\n // ── SSE keepalive ──────────────────────────────────────────────────────\n\n /**\n * If the response is an SSE stream, tee the body through a TransformStream\n * that injects a `: keepalive\\n\\n` comment frame every 25s. The interval\n * is cleared when the wrapped stream closes — which happens both when the\n * SDK ends the underlying stream naturally and when `closeSSEStream` is\n * called.\n *\n * Keepalive policy:\n * - POST response streams (`key` is a request id): always keepalive.\n * In-progress tool calls have no recovery path — if the stream drops\n * mid-execution the result is lost — so we keep it under the\n * Cloudflare edge ~5min idle watchdog.\n * - Standalone GET stream (`key === \"_standalone\"`): keepalive only\n * when no `eventStore` is configured. When resumability is enabled,\n * clients reconnect with `Last-Event-ID` after an idle drop, so we\n * skip the keepalive and let the DO hibernate.\n *\n * Uses the shared `sse-keepalive` constants so both this wrapper and\n * `McpAgent.serve()` write identical frames at the same cadence.\n * See cloudflare/agents#1583.\n */\n private withKeepalive(\n response: Response,\n key: RequestId | \"_standalone\" | undefined\n ): Response {\n const contentType = response.headers.get(\"Content-Type\") ?? \"\";\n if (!contentType.includes(\"text/event-stream\") || !response.body) {\n return response;\n }\n\n // Skip keepalive on the standalone GET stream when an event store is\n // configured — the recovery path is Last-Event-ID reconnects, not\n // bytes-on-the-wire.\n if (key === \"_standalone\" && this.eventStoreConfigured()) {\n return response;\n }\n\n const encoder = new TextEncoder();\n let intervalId: ReturnType<typeof setInterval> | undefined;\n let controllerRef: TransformStreamDefaultController<Uint8Array> | undefined;\n\n const clear = () => {\n if (intervalId !== undefined) {\n clearInterval(intervalId);\n intervalId = undefined;\n }\n if (key !== undefined) this._keepaliveCleanups.delete(key);\n };\n\n const transform = new TransformStream<Uint8Array, Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n intervalId = setInterval(() => {\n try {\n controllerRef?.enqueue(encoder.encode(KEEPALIVE_FRAME));\n } catch {\n clear();\n }\n }, KEEPALIVE_INTERVAL_MS);\n if (key !== undefined) this._keepaliveCleanups.set(key, clear);\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n flush() {\n clear();\n },\n cancel() {\n clear();\n }\n });\n\n const piped = response.body.pipeThrough(transform);\n return new Response(piped, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers\n });\n }\n\n /**\n * Does the SDK transport have an `eventStore`? Reaches into the SDK's\n * private field because the option isn't surfaced on the public API —\n * we only need a yes/no for keepalive policy.\n */\n private eventStoreConfigured(): boolean {\n return (\n (this as unknown as { _eventStore?: unknown })._eventStore !== undefined\n );\n }\n\n // ── CORS ───────────────────────────────────────────────────────────────\n\n private getCorsHeaders({\n forPreflight\n }: { forPreflight?: boolean } = {}): Record<string, string> {\n const merged = { ...DEFAULT_CORS_OPTIONS, ...this._corsOptions };\n if (forPreflight) {\n return {\n \"Access-Control-Allow-Origin\": merged.origin,\n \"Access-Control-Allow-Headers\": merged.headers,\n \"Access-Control-Allow-Methods\": merged.methods,\n \"Access-Control-Max-Age\": String(merged.maxAge)\n };\n }\n return {\n \"Access-Control-Allow-Origin\": merged.origin,\n \"Access-Control-Expose-Headers\": merged.exposeHeaders\n };\n }\n\n private withCorsHeaders(response: Response): Response {\n const headers = new Headers(response.headers);\n for (const [k, v] of Object.entries(this.getCorsHeaders())) {\n headers.set(k, v);\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers\n });\n }\n\n // ── State persistence ──────────────────────────────────────────────────\n\n private installOnSessionInitializedBridge(): void {\n if (this._bridgeInstalled) return;\n const sdk = this as unknown as {\n _onsessioninitialized?: (id: string) => void | Promise<void>;\n };\n sdk._onsessioninitialized = async (sessionId: string): Promise<void> => {\n if (this._userOnSessionInitialized) {\n await Promise.resolve(this._userOnSessionInitialized(sessionId));\n }\n await this.saveState();\n };\n this._bridgeInstalled = true;\n }\n\n private async captureInitializeParams(\n request: Request,\n handleOptions?: { parsedBody?: unknown }\n ): Promise<void> {\n this._pendingRequestId = undefined;\n if (request.method !== \"POST\") return;\n try {\n const parsed =\n handleOptions?.parsedBody ?? (await request.clone().json());\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n const init = messages.find(\n (m): m is JSONRPCMessage =>\n typeof m === \"object\" && m !== null && isInitializeRequest(m)\n );\n if (init && isInitializeRequest(init)) {\n this._capturedInitializeParams = {\n capabilities: init.params.capabilities,\n clientInfo: init.params.clientInfo,\n protocolVersion: init.params.protocolVersion\n };\n }\n // Record the first JSON-RPC request id so we can key keepalive cleanup\n // to it. Batch requests share a single SSE stream in the SDK, so we\n // pick the first request id we see. Eager cleanup via `closeSSEStream`\n // only matches that first id; closing any other id in the batch tears\n // down the same shared stream, and the keepalive interval is cleared by\n // the TransformStream's flush/cancel when that stream actually closes.\n const firstRequest = messages.find(\n (m): m is JSONRPCMessage & { id: RequestId } =>\n typeof m === \"object\" && m !== null && \"id\" in m && \"method\" in m\n );\n if (firstRequest) {\n this._pendingRequestId = firstRequest.id;\n }\n } catch {\n // Body wasn't JSON or already consumed — the SDK transport will\n // surface a proper error response.\n }\n }\n\n private async restoreState(): Promise<void> {\n if (!this._storage || this._stateRestored) return;\n // Set the guard up-front so a re-entrant call (a second request reaching\n // this `await` before the first resolves) doesn't restore twice. If the\n // storage read throws we reset it so a transient failure can be retried\n // on the next request rather than leaving the session permanently\n // un-restored for this DO instance's lifetime.\n this._stateRestored = true;\n\n let state: TransportState | undefined;\n try {\n state = await Promise.resolve(this._storage.get());\n } catch (error) {\n this._stateRestored = false;\n throw error;\n }\n if (!state) return;\n\n // Restore SDK private state. We intentionally reach in here — the SDK\n // doesn't expose hooks for this, and the alternative (a fresh initialize\n // round-trip per cold start) would defeat the point of session\n // persistence.\n const sdk = this as unknown as {\n sessionId?: string;\n _initialized: boolean;\n };\n sdk.sessionId = state.sessionId;\n sdk._initialized = state.initialized;\n this._capturedInitializeParams = state.initializeParams;\n\n if (state.initializeParams && this.onmessage) {\n // Replay through the Server so `_clientCapabilities` etc. are\n // restored. `send()` filters out the resulting response by request id.\n this.onmessage({\n jsonrpc: \"2.0\",\n id: RESTORE_REQUEST_ID,\n method: \"initialize\",\n params: state.initializeParams\n });\n }\n }\n\n private async saveState(): Promise<void> {\n if (!this._storage) return;\n const sdk = this as unknown as {\n sessionId?: string;\n _initialized: boolean;\n };\n const state: TransportState = {\n sessionId: sdk.sessionId,\n initialized: sdk._initialized,\n initializeParams: this._capturedInitializeParams\n };\n await Promise.resolve(this._storage.set(state));\n }\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport interface McpAuthContext {\n props: Record<string, unknown>;\n}\n\nconst authContextStorage = new AsyncLocalStorage<McpAuthContext>();\n\nexport function getMcpAuthContext(): McpAuthContext | undefined {\n return authContextStorage.getStore();\n}\n\nexport function runWithAuthContext<T>(context: McpAuthContext, fn: () => T): T {\n return authContextStorage.run(context, fn);\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n WorkerTransport,\n type WorkerTransportOptions\n} from \"./worker-transport\";\nimport { runWithAuthContext, type McpAuthContext } from \"./auth-context\";\n\nexport interface CreateMcpHandlerOptions extends WorkerTransportOptions {\n /**\n * The route path that this MCP handler should respond to.\n * If specified, the handler will only process requests that match this route.\n * @default \"/mcp\"\n */\n route?: string;\n /**\n * An optional auth context to use for handling MCP requests.\n * If not provided, the handler will look for props in the execution context.\n */\n authContext?: McpAuthContext;\n /**\n * An optional transport to use for handling MCP requests.\n * If not provided, a WorkerTransport will be created with the provided WorkerTransportOptions.\n */\n transport?: WorkerTransport;\n}\n\nexport function createMcpHandler(\n server: McpServer | Server,\n options: CreateMcpHandlerOptions = {}\n): (\n request: Request,\n env: unknown,\n ctx: ExecutionContext\n) => Promise<Response> {\n const route = options.route ?? \"/mcp\";\n const {\n route: _route,\n authContext,\n transport: providedTransport,\n ...transportOptions\n } = options;\n\n return async (\n request: Request,\n _env: unknown,\n ctx: ExecutionContext\n ): Promise<Response> => {\n const url = new URL(request.url);\n if (route && url.pathname !== route) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const transport =\n providedTransport ?? new WorkerTransport(transportOptions);\n\n const buildAuthContext = () => {\n if (authContext) {\n return authContext;\n }\n\n if (ctx.props && Object.keys(ctx.props).length > 0) {\n return {\n props: ctx.props as Record<string, unknown>\n };\n }\n\n return undefined;\n };\n\n const handleRequest = async () => {\n return await transport.handleRequest(request);\n };\n\n const resolvedAuthContext = buildAuthContext();\n\n // Guard for stateful usage where a pre-connected transport is passed via options.\n // If someone passes a transport that's already connected to this server, skip reconnecting.\n // Note: If a developer incorrectly uses a global server with per-request transports,\n // the MCP SDK 1.26.0+ will throw an error when trying to connect an already-connected server.\n if (!transport.started) {\n // Check if server is already connected (McpServer has isConnected(), Server uses transport getter)\n const isServerConnected =\n server instanceof McpServer\n ? server.isConnected()\n : server.transport !== undefined;\n\n if (isServerConnected) {\n throw new Error(\n \"Server is already connected to a transport. Create a new McpServer instance per request for stateless handlers.\"\n );\n }\n\n await server.connect(transport);\n }\n\n try {\n if (resolvedAuthContext) {\n return await runWithAuthContext(resolvedAuthContext, handleRequest);\n } else {\n return await handleRequest();\n }\n } catch (error) {\n console.error(\"MCP handler error:\", error);\n\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message:\n error instanceof Error ? error.message : \"Internal server error\"\n },\n id: null\n }),\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n };\n}\n\nlet didWarnAboutExperimentalCreateMcpHandler = false;\n\n/**\n * @deprecated This has been renamed to createMcpHandler, and experimental_createMcpHandler will be removed in the next major version\n */\nexport function experimental_createMcpHandler(\n server: McpServer | Server,\n options: CreateMcpHandlerOptions = {}\n): (\n request: Request,\n env: unknown,\n ctx: ExecutionContext\n) => Promise<Response> {\n if (!didWarnAboutExperimentalCreateMcpHandler) {\n didWarnAboutExperimentalCreateMcpHandler = true;\n console.warn(\n \"experimental_createMcpHandler is deprecated, use createMcpHandler instead. experimental_createMcpHandler will be removed in the next major version.\"\n );\n }\n return createMcpHandler(server, options);\n}\n","import type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type {\n JSONRPCMessage,\n MessageExtraInfo,\n RequestId\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n JSONRPCMessageSchema,\n isJSONRPCErrorResponse,\n isJSONRPCResultResponse,\n type ElicitResult\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Connection, ConnectionContext } from \"../\";\nimport { Agent } from \"../index\";\nimport type { BaseTransportType, MaybePromise, ServeOptions } from \"./types\";\nimport {\n createAutoHandler,\n createLegacySseHandler,\n createStreamingHttpHandler,\n handleCORS,\n isDurableObjectNamespace,\n MCP_HTTP_METHOD_HEADER,\n MCP_MESSAGE_HEADER\n} from \"./utils\";\nimport { McpSSETransport, StreamableHTTPServerTransport } from \"./transport\";\nimport type { EventStore } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { DurableObjectEventStore } from \"./event-store\";\nimport { RPCServerTransport, type RPCServerTransportOptions } from \"./rpc\";\n\nexport abstract class McpAgent<\n Env extends Cloudflare.Env = Cloudflare.Env,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>\n> extends Agent<Env, State, Props> {\n private _transport?: Transport;\n private _pendingElicitations = new Map<\n string,\n { resolve: (result: ElicitResult) => void; reject: (err: Error) => void }\n >();\n props?: Props;\n\n // MCP WebSocket connections are transport bridges — they use their own\n // protocol and don't need agent identity, state sync, or other protocol\n // messages. Regular WebSocket connections are left untouched.\n override shouldSendProtocolMessages(\n _connection: Connection,\n ctx: ConnectionContext\n ): boolean {\n return !ctx.request.headers.get(MCP_HTTP_METHOD_HEADER);\n }\n\n abstract server: MaybePromise<McpServer | Server>;\n abstract init(): Promise<void>;\n\n /*\n * Helpers\n */\n\n async setInitializeRequest(initializeRequest: JSONRPCMessage) {\n await this.ctx.storage.put(\"initializeRequest\", initializeRequest);\n }\n\n async getInitializeRequest() {\n return this.ctx.storage.get<JSONRPCMessage>(\"initializeRequest\");\n }\n\n /**\n * Storage key prefix for the `streamId -> requestIds` mapping used\n * to support POST stream resumption across WebSocket reconnects.\n *\n * @internal\n */\n private static readonly STREAM_REQS_KEY_PREFIX = \"__mcp_stream_reqs__:\";\n\n /** Persist the `requestIds` for a POST stream. @internal */\n async setStreamRequestIds(\n streamId: string,\n requestIds: RequestId[]\n ): Promise<void> {\n await this.ctx.storage.put<RequestId[]>(\n `${McpAgent.STREAM_REQS_KEY_PREFIX}${streamId}`,\n requestIds\n );\n }\n\n /** Read the persisted `requestIds` for a POST stream. @internal */\n async getStreamRequestIds(\n streamId: string\n ): Promise<RequestId[] | undefined> {\n return this.ctx.storage.get<RequestId[]>(\n `${McpAgent.STREAM_REQS_KEY_PREFIX}${streamId}`\n );\n }\n\n /** Drop the persisted `requestIds` for a POST stream. @internal */\n async deleteStreamRequestIds(streamId: string): Promise<void> {\n await this.ctx.storage.delete(\n `${McpAgent.STREAM_REQS_KEY_PREFIX}${streamId}`\n );\n }\n\n /**\n * Reverse lookup: find which POST stream a given `requestId` belongs\n * to, and return the stream's full `requestIds` list in the same\n * pass. Used by the transport when the originating WS has dropped,\n * so `send()` can still record events for replay and decide whether\n * the stream is fully responded — mirrors the SDK's\n * `_requestToStreamMapping` which outlives connection loss.\n *\n * Returning `requestIds` alongside `streamId` lets `send()` skip a\n * second `getStreamRequestIds` read on the same key.\n *\n * O(n) in the number of in-flight POST streams — single-digit in\n * practice since each stream is cleaned up on its final response.\n * The `limit` is a defensive ceiling so an abandoned-POST leak can't\n * unbounded-load this scan; if you hit it, something else has gone\n * wrong and `send()` will throw `No active stream found`.\n *\n * @internal\n */\n async getStreamForRequestId(\n requestId: RequestId\n ): Promise<{ streamId: string; requestIds: RequestId[] } | undefined> {\n const STREAM_REQS_SCAN_LIMIT = 1000;\n const rows = await this.ctx.storage.list<RequestId[]>({\n prefix: McpAgent.STREAM_REQS_KEY_PREFIX,\n limit: STREAM_REQS_SCAN_LIMIT\n });\n if (rows.size === STREAM_REQS_SCAN_LIMIT) {\n console.warn(\n `McpAgent: getStreamForRequestId hit the ${STREAM_REQS_SCAN_LIMIT}-key scan cap; ` +\n `stale __mcp_stream_reqs__ entries may be accumulating from abandoned POSTs`\n );\n }\n for (const [key, requestIds] of rows) {\n if (requestIds?.includes(requestId)) {\n return {\n streamId: key.slice(McpAgent.STREAM_REQS_KEY_PREFIX.length),\n requestIds\n };\n }\n }\n return undefined;\n }\n\n /** Read the transport type for this agent.\n * This relies on the naming scheme being `sse:${sessionId}`,\n * `streamable-http:${sessionId}`, or `rpc:${sessionId}`.\n */\n getTransportType(): BaseTransportType {\n const [t, ..._] = this.name.split(\":\");\n switch (t) {\n case \"sse\":\n return \"sse\";\n case \"streamable-http\":\n return \"streamable-http\";\n case \"rpc\":\n return \"rpc\";\n default:\n throw new Error(\n \"Invalid transport type. McpAgent must be addressed with a valid protocol.\"\n );\n }\n }\n\n /** Read the sessionId for this agent.\n * This relies on the naming scheme being `sse:${sessionId}`\n * or `streamable-http:${sessionId}`.\n */\n getSessionId(): string {\n const [_, sessionId] = this.name.split(\":\");\n if (!sessionId) {\n throw new Error(\n \"Invalid session id. McpAgent must be addressed with a valid session id.\"\n );\n }\n return sessionId;\n }\n\n /** Get the unique WebSocket. SSE transport only. */\n getWebSocket() {\n const websockets = Array.from(this.getConnections());\n if (websockets.length === 0) {\n return null;\n }\n return websockets[0];\n }\n\n /**\n * Returns options for configuring the RPC server transport.\n * Override this method to customize RPC transport behavior (e.g., timeout).\n *\n * @example\n * ```typescript\n * class MyMCP extends McpAgent {\n * protected getRpcTransportOptions() {\n * return { timeout: 120000 }; // 2 minutes\n * }\n * }\n * ```\n */\n protected getRpcTransportOptions(): RPCServerTransportOptions {\n return {};\n }\n\n /**\n * Returns the {@link EventStore} for SSE resumability. Defaults to a\n * {@link DurableObjectEventStore} backed by this agent's storage,\n * letting clients reconnect with `Last-Event-ID` after the Cloudflare\n * edge closes an idle SSE stream (~5 minute watchdog) instead of\n * relying on a server-side keepalive that would block hibernation.\n *\n * Per-stream events are cleared by the transport immediately after\n * the final response is written to the wire, so there's no\n * background cleanup — storage cost is bounded by the in-flight\n * streams alone.\n *\n * Override to disable (`return undefined`) or swap implementations.\n */\n protected getEventStore(): EventStore | undefined {\n return new DurableObjectEventStore(this.ctx.storage);\n }\n\n /** Returns a new transport matching the type of the Agent. */\n private initTransport() {\n switch (this.getTransportType()) {\n case \"sse\": {\n return new McpSSETransport();\n }\n case \"streamable-http\": {\n const transport = new StreamableHTTPServerTransport({\n eventStore: this.getEventStore()\n });\n transport.messageInterceptor = (message) => {\n return Promise.resolve(this._handleElicitationResponse(message));\n };\n return transport;\n }\n case \"rpc\": {\n return new RPCServerTransport(this.getRpcTransportOptions());\n }\n }\n }\n\n /** Update and store the props */\n async updateProps(props?: Props) {\n await this.ctx.storage.put(\"props\", props ?? {});\n this.props = props;\n }\n\n async reinitializeServer() {\n // If the agent was previously initialized, we have to populate\n // the server again by sending the initialize request to make\n // client information available to the server.\n const initializeRequest = await this.getInitializeRequest();\n if (initializeRequest) {\n this._transport?.onmessage?.(initializeRequest);\n }\n }\n\n /*\n * Base Agent / Partykit Server overrides\n */\n\n /** Sets up the MCP transport and server every time the Agent is started.*/\n async onStart(props?: Props) {\n if (props) {\n // Fresh start with props — save to storage (also sets this.props)\n await this.updateProps(props);\n } else {\n // Hibernation recovery — restore props from storage\n this.props = await this.ctx.storage.get(\"props\");\n }\n\n await this.init();\n const server = await this.server;\n // Connect to the MCP server\n this._transport = this.initTransport();\n\n if (!this._transport) {\n throw new Error(\"Failed to initialize transport\");\n }\n await server.connect(this._transport);\n\n await this.reinitializeServer();\n }\n\n /** Validates new WebSocket connections. */\n async onConnect(\n conn: Connection,\n { request: req }: ConnectionContext\n ): Promise<void> {\n switch (this.getTransportType()) {\n case \"sse\": {\n // For SSE connections, we can only have one open connection per session\n // If we get an upgrade while already connected, we should error\n const websockets = Array.from(this.getConnections());\n if (websockets.length > 1) {\n conn.close(1008, \"Websocket already connected\");\n return;\n }\n break;\n }\n case \"streamable-http\":\n if (this._transport instanceof StreamableHTTPServerTransport) {\n switch (req.headers.get(MCP_HTTP_METHOD_HEADER)) {\n case \"POST\": {\n // This returns the response directly to the client\n const payloadHeader = req.headers.get(MCP_MESSAGE_HEADER);\n let rawPayload: string;\n\n if (!payloadHeader) {\n rawPayload = \"{}\";\n } else {\n try {\n rawPayload = Buffer.from(payloadHeader, \"base64\").toString(\n \"utf-8\"\n );\n } catch (_error) {\n throw new Error(\n \"Internal Server Error: Failed to decode MCP message header\"\n );\n }\n }\n\n const parsedBody = JSON.parse(rawPayload);\n this._transport?.handlePostRequest(req, parsedBody);\n break;\n }\n case \"GET\":\n this._transport?.handleGetRequest(req);\n break;\n }\n }\n }\n }\n\n /*\n * Transport ingress and routing\n */\n\n /** Handles MCP Messages for the legacy SSE transport. */\n async onSSEMcpMessage(\n _sessionId: string,\n messageBody: unknown,\n extraInfo?: MessageExtraInfo\n ): Promise<Error | null> {\n // Since we address the DO via both the protocol and the session id,\n // this should never happen, but let's enforce it just in case\n if (this.getTransportType() !== \"sse\") {\n return new Error(\"Internal Server Error: Expected SSE transport\");\n }\n\n try {\n let parsedMessage: JSONRPCMessage;\n try {\n parsedMessage = JSONRPCMessageSchema.parse(messageBody);\n } catch (error) {\n this._transport?.onerror?.(error as Error);\n throw error;\n }\n\n // Check if this is an elicitation response before passing to transport\n if (this._handleElicitationResponse(parsedMessage)) {\n return null; // Message was handled by elicitation system\n }\n\n this._transport?.onmessage?.(parsedMessage, extraInfo);\n return null;\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n this._transport?.onerror?.(error as Error);\n return error as Error;\n }\n }\n\n /** Elicit user input with a message and schema */\n async elicitInput(\n params: {\n message: string;\n requestedSchema: unknown;\n },\n options?: { relatedRequestId?: RequestId }\n ): Promise<ElicitResult> {\n const requestId = `elicit_${Math.random().toString(36).substring(2, 11)}`;\n\n const elicitRequest = {\n jsonrpc: \"2.0\" as const,\n id: requestId,\n method: \"elicitation/create\",\n params: {\n message: params.message,\n requestedSchema: params.requestedSchema\n }\n };\n\n // Create a Promise that will be resolved when the response arrives.\n // timeoutId is hoisted so error paths below can clear it and avoid\n // an unhandled rejection on the orphaned responsePromise.\n let timeoutId: ReturnType<typeof setTimeout>;\n const responsePromise = new Promise<ElicitResult>((resolve, reject) => {\n timeoutId = setTimeout(() => {\n this._pendingElicitations.delete(requestId);\n reject(new Error(\"Elicitation request timed out\"));\n }, 60000);\n\n this._pendingElicitations.set(requestId, {\n resolve: (result: ElicitResult) => {\n clearTimeout(timeoutId);\n this._pendingElicitations.delete(requestId);\n resolve(result);\n },\n reject: (err: Error) => {\n clearTimeout(timeoutId);\n this._pendingElicitations.delete(requestId);\n reject(err);\n }\n });\n });\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n this._pendingElicitations.delete(requestId);\n };\n\n // Keep the DO alive while we wait for the user's elicitation response.\n // An unresolved Promise alone isn't enough to prevent hibernation.\n return this.keepAliveWhile(async () => {\n // Send through MCP transport\n if (this._transport) {\n try {\n await this._transport.send(elicitRequest, options);\n } catch (error) {\n cleanup();\n throw error;\n }\n } else {\n const connections = this.getConnections();\n if (!connections || Array.from(connections).length === 0) {\n cleanup();\n throw new Error(\"No active connections available for elicitation\");\n }\n\n const connectionList = Array.from(connections);\n for (const connection of connectionList) {\n try {\n connection.send(JSON.stringify(elicitRequest));\n } catch (error) {\n console.error(\"Failed to send elicitation request:\", error);\n }\n }\n }\n\n return responsePromise;\n });\n }\n\n /** Handle elicitation responses via in-memory resolver */\n private _handleElicitationResponse(message: JSONRPCMessage): boolean {\n if (isJSONRPCResultResponse(message) && message.result) {\n const requestId = message.id?.toString();\n if (!requestId || !requestId.startsWith(\"elicit_\")) return false;\n\n const pending = this._pendingElicitations.get(requestId);\n if (!pending) return false;\n\n pending.resolve(message.result as ElicitResult);\n return true;\n }\n\n if (isJSONRPCErrorResponse(message)) {\n const requestId = message.id?.toString();\n if (!requestId || !requestId.startsWith(\"elicit_\")) return false;\n\n const pending = this._pendingElicitations.get(requestId);\n if (!pending) return false;\n\n pending.resolve({\n action: \"cancel\",\n content: {\n error: message.error.message || \"Elicitation request failed\"\n }\n });\n return true;\n }\n\n return false;\n }\n\n /**\n * Handle an RPC message for MCP\n * This method is called by the RPC stub to process MCP messages\n * @param message The JSON-RPC message(s) to handle\n * @returns The response message(s) or undefined\n */\n async handleMcpMessage(\n message: JSONRPCMessage | JSONRPCMessage[]\n ): Promise<JSONRPCMessage | JSONRPCMessage[] | undefined> {\n await this.__unsafe_ensureInitialized();\n\n if (!(this._transport instanceof RPCServerTransport)) {\n throw new Error(\"Expected RPC transport\");\n }\n\n const transport = this._transport;\n\n // RPC waits are intentionally in-memory. While a request/continuation is\n // pending, keep the object alive so hibernation does not drop the resolver\n // maps or the suspended tool stack. Making this sleep/resume across\n // hibernation would require a durable continuation model instead.\n return await this.keepAliveWhile(async () => {\n // Intercept elicitation responses before they reach the transport.\n // Mirrors what onSSEMcpMessage() and StreamableHTTPServerTransport's\n // messageInterceptor already do for their respective transports.\n if (!Array.isArray(message)) {\n const parseResult = JSONRPCMessageSchema.safeParse(message);\n if (\n parseResult.success &&\n this._handleElicitationResponse(parseResult.data)\n ) {\n // Resolved a pending elicitation — now wait for the tool handler\n // to send its next message (another elicitation request or the\n // final tool result).\n return await transport._awaitPendingResponse();\n }\n }\n\n return await transport.handle(message);\n });\n }\n\n /** Return a handler for the given path for this MCP.\n * Defaults to Streamable HTTP transport.\n */\n static serve(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n transport = \"streamable-http\",\n jurisdiction\n }: ServeOptions = {}\n ) {\n return {\n async fetch<Env>(\n this: void,\n request: Request,\n env: Env,\n ctx: ExecutionContext\n ): Promise<Response> {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) {\n return corsResponse;\n }\n\n const bindingValue = env[binding as keyof typeof env] as unknown;\n\n // Ensure we have a binding of some sort\n if (bindingValue == null || typeof bindingValue !== \"object\") {\n throw new Error(\n `Could not find McpAgent binding for ${binding}. Did you update your wrangler configuration?`\n );\n }\n\n // Ensure that the binding is to a DurableObject\n if (!isDurableObjectNamespace(bindingValue)) {\n throw new Error(\n `Invalid McpAgent binding for ${binding}. Make sure it's a Durable Object binding.`\n );\n }\n\n const namespace =\n bindingValue satisfies DurableObjectNamespace<McpAgent>;\n\n switch (transport) {\n case \"streamable-http\": {\n const handleStreamableHttp = createStreamingHttpHandler(\n path,\n namespace,\n { corsOptions, jurisdiction }\n );\n return handleStreamableHttp(request, ctx);\n }\n case \"sse\": {\n const handleLegacySse = createLegacySseHandler(path, namespace, {\n corsOptions,\n jurisdiction\n });\n return handleLegacySse(request, ctx);\n }\n case \"auto\": {\n const handleAuto = createAutoHandler(path, namespace, {\n corsOptions,\n jurisdiction\n });\n return handleAuto(request, ctx);\n }\n default:\n return new Response(\n \"Invalid MCP transport mode. Only `streamable-http`, `sse`, or `auto` are allowed.\",\n { status: 500 }\n );\n }\n }\n };\n }\n /**\n * Legacy api\n **/\n static mount(path: string, opts: Omit<ServeOptions, \"transport\"> = {}) {\n return McpAgent.serveSSE(path, opts);\n }\n\n static serveSSE(path: string, opts: Omit<ServeOptions, \"transport\"> = {}) {\n return McpAgent.serve(path, { ...opts, transport: \"sse\" });\n }\n}\n\nexport {\n SSEEdgeClientTransport,\n StreamableHTTPEdgeClientTransport\n} from \"./client-transports\";\nexport {\n RPC_DO_PREFIX,\n RPCClientTransport,\n RPCServerTransport,\n type RPCClientTransportOptions,\n type RPCServerTransportOptions\n} from \"./rpc\";\n\nexport {\n ElicitRequestSchema,\n type ElicitRequest,\n type ElicitResult\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nexport type {\n MCPClientOAuthResult,\n MCPClientOAuthCallbackConfig,\n MCPServerOptions,\n MCPConnectionResult,\n MCPDiscoverResult\n} from \"./client\";\n\nexport { normalizeServerId, MCP_SERVER_ID_MAX_LENGTH } from \"./client\";\n\nexport type { McpClientOptions } from \"./types\";\n\nexport {\n createMcpHandler,\n experimental_createMcpHandler,\n type CreateMcpHandlerOptions\n} from \"./handler\";\n\nexport { getMcpAuthContext, type McpAuthContext } from \"./auth-context\";\n\nexport { DurableObjectEventStore } from \"./event-store\";\nexport type { ClearableEventStore } from \"./transport\";\n\nexport {\n WorkerTransport,\n type WorkerTransportOptions,\n type TransportState\n} from \"./worker-transport\";\n"],"mappings":"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"}