mcp-handler
Version:
Framework-agnostic HTTP adapter for Model Context Protocol servers
1 lines • 62 kB
Source Map (JSON)
{"version":3,"sources":["../src/handler/mcp-api-handler.ts","../src/lib/log-helper.ts","../src/webmcp/script-handler.ts","../src/handler/index.ts","../src/events/register.ts","../src/events/schemas.ts","../src/events/json.ts","../src/events/validate-delivery.ts","../src/auth/auth-wrapper.ts","../src/lib/url.ts","../src/auth/auth-metadata.ts"],"names":["name","messages"],"mappings":"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","sourcesContent":["import {\n createMcpHandler as createSdkMcpHandler,\n McpServer,\n type CreateMcpHandlerOptions as SdkMcpHandlerOptions,\n type ServerOptions as McpServerOptions,\n} from \"@modelcontextprotocol/server\";\nimport type {\n McpEvent,\n McpRequestEvent,\n McpErrorEvent,\n} from \"../lib/log-helper\";\nimport { createEvent } from \"../lib/log-helper\";\nimport {\n createWebMcpScriptHandler,\n type WebMcpScriptHandlerOptions,\n} from \"../webmcp/script-handler\";\n\nexport type WebMcpOptions = Omit<WebMcpScriptHandlerOptions, \"endpoint\">;\n\n/**\n * Options for the MCP handler: the SDK's `ServerOptions` (capabilities,\n * instructions, ...) plus handler-level extras.\n */\nexport type McpHandlerOptions = McpServerOptions & {\n /**\n * Maximum number of concurrent `subscriptions/listen` streams.\n * Set to `0` to reject subscriptions without opening an SSE stream.\n * @default 1024\n */\n maxSubscriptions?: SdkMcpHandlerOptions[\"maxSubscriptions\"];\n /**\n * Name and version reported to clients during initialization.\n */\n serverInfo?: {\n name: string;\n version: string;\n };\n /**\n * If true, enables console logging.\n * @default false\n */\n verboseLogs?: boolean;\n /**\n * Callback function that receives MCP events.\n * This can be used to track analytics, debug issues, or implement custom behaviors.\n */\n onEvent?: (event: McpEvent) => void;\n /**\n * Publishes an allowlisted subset of this server's tools to in-page agents\n * through WebMCP. Load the generated bridge from the MCP route with the\n * `?webmcp-script` query parameter.\n *\n * @experimental WebMCP is an early-stage browser API.\n */\n experimental_webMcp?: WebMcpOptions;\n};\n\nexport function initializeMcpApiHandler(\n initializeServer:\n | ((server: McpServer) => Promise<void>)\n | ((server: McpServer) => void),\n options: McpHandlerOptions = {},\n): (req: Request) => Promise<Response> {\n const {\n serverInfo = {\n name: \"mcp-typescript server on vercel\",\n version: \"0.1.0\",\n },\n verboseLogs = false,\n onEvent,\n maxSubscriptions,\n experimental_webMcp,\n ...mcpServerOptions\n } = options;\n\n // Validate WebMCP configuration when the handler is created rather than on\n // the first request. The real endpoint is inferred from the script request.\n if (experimental_webMcp) {\n createWebMcpScriptHandler({\n ...experimental_webMcp,\n endpoint: \"/\",\n });\n }\n\n const emitError = (error: Error) => {\n if (verboseLogs) {\n console.error(\"MCP handler error:\", error);\n }\n onEvent?.(\n createEvent<McpErrorEvent>({\n type: \"ERROR\",\n error,\n source: \"request\",\n severity: \"error\",\n }),\n );\n };\n\n // The SDK handler serves the 2026-07-28 protocol (stateless, per-request\n // envelope, server/discover) and falls back to stateless serving for\n // 2025-era Streamable HTTP clients. A fresh McpServer is constructed per\n // request via the factory.\n const sdkHandler = createSdkMcpHandler(\n async () => {\n const server = new McpServer(serverInfo, mcpServerOptions);\n await initializeServer(server);\n return server;\n },\n {\n legacy: \"stateless\",\n onerror: emitError,\n maxSubscriptions,\n },\n );\n\n return async function mcpApiHandler(req: Request): Promise<Response> {\n if (\n experimental_webMcp &&\n (req.method === \"GET\" || req.method === \"HEAD\")\n ) {\n const scriptUrl = new URL(req.url);\n if (scriptUrl.searchParams.has(\"webmcp-script\")) {\n scriptUrl.searchParams.delete(\"webmcp-script\");\n return createWebMcpScriptHandler({\n ...experimental_webMcp,\n endpoint: scriptUrl.toString(),\n })(req);\n }\n }\n\n let method: string | undefined;\n let parsedBody: unknown;\n const started = Date.now();\n\n if (\n req.method === \"POST\" &&\n (req.headers.get(\"content-type\") || \"\").includes(\"application/json\")\n ) {\n try {\n parsedBody = await req.clone().json();\n if (\n typeof parsedBody === \"object\" &&\n parsedBody !== null &&\n \"method\" in parsedBody\n ) {\n method = String((parsedBody as { method: unknown }).method);\n onEvent?.(\n createEvent<McpRequestEvent>({\n type: \"REQUEST_RECEIVED\",\n method,\n parameters: redactEventSecret(parsedBody),\n status: \"success\",\n }),\n );\n }\n } catch {\n // Malformed JSON is rejected by the SDK handler below.\n }\n }\n\n try {\n // withMcpAuth attaches the verified AuthInfo to this Request. Pass it\n // explicitly so the SDK exposes it as ctx.http?.authInfo.\n const response = await sdkHandler.fetch(req, {\n authInfo: req.auth,\n parsedBody,\n });\n\n if (method) {\n onEvent?.(\n createEvent<McpRequestEvent>({\n type: \"REQUEST_COMPLETED\",\n method,\n duration: Date.now() - started,\n status: response.ok ? \"success\" : \"error\",\n }),\n );\n }\n return response;\n } catch (error) {\n emitError(error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n };\n}\n\nfunction redactEventSecret(body: object): object {\n const request = body as { method?: unknown; params?: unknown };\n if (\n typeof request.method !== \"string\" ||\n !request.method.startsWith(\"events/\") ||\n !request.params ||\n typeof request.params !== \"object\"\n )\n return body;\n const params = request.params as { delivery?: unknown };\n if (\n !params.delivery ||\n typeof params.delivery !== \"object\" ||\n !(\"secret\" in params.delivery)\n )\n return body;\n return {\n ...body,\n params: {\n ...params,\n delivery: { ...params.delivery, secret: \"[REDACTED]\" },\n },\n };\n}\n","export type McpEventType =\n | \"REQUEST_RECEIVED\" // When a request is received from the client\n | \"REQUEST_COMPLETED\" // When a request completes\n | \"ERROR\"; // When an error occurs during any operation\n\nexport interface McpEventBase {\n type: McpEventType;\n timestamp: number;\n}\n\nexport interface McpRequestEvent extends McpEventBase {\n type: \"REQUEST_RECEIVED\" | \"REQUEST_COMPLETED\";\n method: string;\n parameters?: unknown;\n result?: unknown;\n duration?: number; // For REQUEST_COMPLETED events\n status: \"success\" | \"error\";\n}\n\nexport interface McpErrorEvent extends McpEventBase {\n type: \"ERROR\";\n error: Error | string;\n context?: string;\n source: \"request\" | \"system\";\n severity: \"warning\" | \"error\" | \"fatal\";\n}\n\nexport type McpEvent = McpRequestEvent | McpErrorEvent;\n\nexport function createEvent<T extends McpEvent>(\n event: Omit<T, \"timestamp\">,\n): T {\n return {\n ...event,\n timestamp: Date.now(),\n } as T;\n}\n","/**\n * Options for the WebMCP bridge script endpoint.\n */\nexport type WebMcpScriptHandlerOptions = {\n /**\n * URL of the MCP endpoint the script talks to. May be a path relative to\n * the page origin (\"/api/mcp\") or an absolute URL.\n */\n endpoint: string;\n /**\n * Explicit allowlist of tool names exposed to in-page agents. Tools not\n * listed here are never registered with the browser, even though they\n * remain reachable through the MCP endpoint itself.\n */\n tools: string[];\n /**\n * Credentials mode for the tool-call fetches issued from the page.\n * @default \"same-origin\"\n */\n credentials?: \"same-origin\" | \"include\" | \"omit\";\n /**\n * Value served in the script response's Cache-Control header.\n * @default \"public, max-age=300\"\n */\n cacheControl?: string;\n};\n\ntype ScriptConfig = {\n endpoint: string;\n tools: string[];\n credentials: string;\n};\n\n/**\n * Returns a Web-standard handler that serves a small browser script. When\n * loaded in a page, the script lists the tools of the MCP endpoint, filters\n * them down to the configured allowlist, and registers each one with the\n * page's WebMCP provider (`navigator.modelContext` / `document.modelContext`)\n * so in-page agents can call them. Tool calls run through `fetch` and carry\n * the user's session according to the configured credentials mode.\n *\n * WebMCP is an early-stage W3C proposal; in browsers without a provider (or\n * polyfill) the script is a no-op.\n */\nexport function createWebMcpScriptHandler(\n options: WebMcpScriptHandlerOptions,\n): (req: Request) => Response {\n const {\n endpoint,\n tools,\n credentials = \"same-origin\",\n cacheControl = \"public, max-age=300\",\n } = options;\n\n if (typeof endpoint !== \"string\" || endpoint.length === 0) {\n throw new Error(\"createWebMcpScriptHandler: `endpoint` is required\");\n }\n if (\n !Array.isArray(tools) ||\n tools.some((name) => typeof name !== \"string\" || name.length === 0)\n ) {\n throw new Error(\n \"createWebMcpScriptHandler: `tools` must be an array of tool names — only allowlisted tools are exposed to the web\",\n );\n }\n\n const script = buildScript({ endpoint, tools, credentials });\n const headers = {\n \"content-type\": \"text/javascript; charset=utf-8\",\n \"cache-control\": cacheControl,\n };\n\n return function webMcpScriptHandler(req: Request): Response {\n if (req.method === \"HEAD\") {\n return new Response(null, { status: 200, headers });\n }\n if (req.method !== \"GET\") {\n return new Response(\"Method not allowed\", {\n status: 405,\n headers: { allow: \"GET, HEAD\" },\n });\n }\n return new Response(script, { status: 200, headers });\n };\n}\n\nfunction buildScript(config: ScriptConfig): string {\n // \"<\" is escaped so the config can never terminate an inline <script> tag.\n const configJson = JSON.stringify(config).replace(/</g, \"\\\\u003c\");\n\n return `(() => {\n \"use strict\";\n const config = ${configJson};\n const provider = [\n typeof document !== \"undefined\" && document.modelContext,\n typeof navigator !== \"undefined\" && navigator.modelContext,\n ].find((candidate) => candidate && typeof candidate.registerTool === \"function\");\n if (!provider || typeof provider.registerTool !== \"function\") {\n return;\n }\n\n let protocolVersion = null;\n let requestId = 0;\n\n async function rpc(method, params, isNotification, signal) {\n const body = { jsonrpc: \"2.0\", method };\n if (params !== undefined) body.params = params;\n if (!isNotification) body.id = ++requestId;\n const headers = {\n \"content-type\": \"application/json\",\n accept: \"application/json, text/event-stream\",\n };\n if (protocolVersion) headers[\"mcp-protocol-version\"] = protocolVersion;\n const res = await fetch(config.endpoint, {\n method: \"POST\",\n credentials: config.credentials,\n headers,\n body: JSON.stringify(body),\n signal,\n });\n if (isNotification) return null;\n if (!res.ok) {\n throw new Error(\"MCP request failed: HTTP \" + res.status);\n }\n const contentType = res.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"text/event-stream\")) {\n return parseSse(await res.text(), body.id);\n }\n return unwrap(await res.json(), body.id);\n }\n\n function parseSse(text, id) {\n for (const event of text.split(/\\\\r?\\\\n\\\\r?\\\\n/)) {\n const data = event\n .split(/\\\\r?\\\\n/)\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).replace(/^ /, \"\"))\n .join(\"\\\\n\");\n if (!data) continue;\n let message;\n try {\n message = JSON.parse(data);\n } catch {\n continue;\n }\n if (\n message &&\n message.id === id &&\n (\"result\" in message || \"error\" in message)\n ) {\n return unwrap(message, id);\n }\n }\n throw new Error(\"No MCP response found in event stream\");\n }\n\n function unwrap(message, id) {\n if (message && message.error) {\n throw new Error(\n message.error.message || \"MCP error \" + message.error.code,\n );\n }\n if (!message || message.id !== id) {\n throw new Error(\"Unexpected MCP response\");\n }\n return message.result;\n }\n\n (async () => {\n const init = await rpc(\"initialize\", {\n protocolVersion: \"2025-06-18\",\n capabilities: {},\n clientInfo: { name: \"mcp-handler/webmcp\", version: \"1.0.0\" },\n });\n protocolVersion = (init && init.protocolVersion) || \"2025-06-18\";\n await rpc(\"notifications/initialized\", undefined, true).catch(() => null);\n\n let cursor;\n do {\n const listed = await rpc(\"tools/list\", cursor ? { cursor } : {});\n for (const tool of (listed && listed.tools) || []) {\n if (!config.tools.includes(tool.name)) continue;\n try {\n await provider.registerTool({\n name: tool.name,\n title: tool.title || tool.annotations?.title || tool.name,\n description: tool.description || tool.title || tool.annotations?.title || tool.name,\n inputSchema: tool.inputSchema,\n annotations: { readOnlyHint: tool.annotations?.readOnlyHint === true },\n execute: (args, options) =>\n rpc(\"tools/call\", { name: tool.name, arguments: args || {} }, false, options?.signal),\n });\n } catch (error) {\n console.warn(\"[mcp-handler/webmcp] failed to register tool \" + tool.name + \":\", error);\n }\n }\n cursor = listed && listed.nextCursor;\n } while (cursor);\n })().catch((error) => {\n console.warn(\"[mcp-handler/webmcp] failed to register tools:\", error);\n });\n})();\n`;\n}\n","import {\n initializeMcpApiHandler,\n type McpHandlerOptions,\n type WebMcpOptions,\n} from \"./mcp-api-handler\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\n\nexport type { McpHandlerOptions, WebMcpOptions };\n\n/**\n * Creates a MCP handler that can be used to handle MCP requests.\n *\n * The returned handler is a web-standard `(request: Request) => Promise<Response>`\n * function. It serves every request it receives — routing belongs to the host\n * framework, so mount it at the route you want (a Next.js route handler,\n * Hono, Nitro, ...).\n *\n * @param initializeServer - A function that initializes the MCP server. Use this to access the server instance and register tools, prompts, and resources.\n * @param options - The SDK's server options plus handler extras (`serverInfo`, `verboseLogs`, `onEvent`, `maxSubscriptions`, `experimental_webMcp`).\n * @returns A function that can be used to handle MCP requests.\n */\nexport default function createMcpRouteHandler(\n initializeServer:\n | ((server: McpServer) => Promise<void>)\n | ((server: McpServer) => void),\n options?: McpHandlerOptions,\n): (request: Request) => Promise<Response> {\n return initializeMcpApiHandler(initializeServer, options);\n}\n","import {\n ProtocolError,\n type McpServer,\n type ServerCapabilities,\n type ServerContext,\n} from \"@modelcontextprotocol/server\";\nimport {\n listParams,\n subscribeParams,\n subscriptionState,\n unsubscribeParams,\n webhookFailures,\n} from \"./schemas\";\nimport { canonical } from \"./json\";\nimport type {\n ExperimentalMcpEventContext,\n ExperimentalMcpEventDefinition,\n ExperimentalMcpEventsOptions,\n ExperimentalMcpSubscriptionKey,\n} from \"./types\";\n\nconst registered = new WeakSet<McpServer>();\n\n/**\n * Register the draft MCP Events webhook control methods on the existing MCP\n * endpoint. Storage and delivery must live outside the per-request server.\n *\n * @experimental Follows the experimental MCP Events design sketch, not a stable\n * MCP specification. Poll/stream delivery and model-tool generation are excluded.\n */\nexport function experimental_registerMcpEvents(\n server: McpServer,\n options: ExperimentalMcpEventsOptions,\n): void {\n if (registered.has(server)) throw new Error(\"MCP Events already registered\");\n const minMs = options.ttl?.minMs ?? 60_000;\n const defaultMs = options.ttl?.defaultMs ?? 3_600_000;\n const maxMs = options.ttl?.maxMs ?? 86_400_000;\n if (\n ![minMs, defaultMs, maxMs].every(\n (value) => Number.isSafeInteger(value) && value > 0,\n ) ||\n minMs > defaultMs ||\n defaultMs > maxMs ||\n maxMs > 8.64e15 - Date.now()\n )\n throw new Error(\n \"MCP Events TTLs must satisfy 0 < minMs <= defaultMs <= maxMs\",\n );\n registered.add(server);\n\n // The SDK preserves extension capabilities but does not yet type events.\n server.server.registerCapabilities({ events: {} } as ServerCapabilities);\n\n async function context(\n ctx: ServerContext,\n ): Promise<ExperimentalMcpEventContext> {\n const authInfo = ctx.http?.authInfo;\n if (\n !authInfo ||\n (authInfo.expiresAt !== undefined &&\n authInfo.expiresAt <= Date.now() / 1000)\n ) {\n throw new ProtocolError(-32012, \"Authentication required\");\n }\n const principal = await options.getPrincipal(authInfo);\n if (typeof principal !== \"string\" || !principal.trim()) {\n throw new ProtocolError(-32012, \"Authenticated principal required\");\n }\n return { principal, authInfo, signal: ctx.mcpReq.signal };\n }\n\n async function catalog(ctx: ExperimentalMcpEventContext) {\n const events =\n typeof options.events === \"function\"\n ? await options.events(ctx)\n : options.events;\n const names = new Set<string>();\n for (const event of events) {\n if (!event.name || names.has(event.name))\n throw new Error(\"Invalid event catalog\");\n names.add(event.name);\n }\n const visible: ExperimentalMcpEventDefinition[] = [];\n for (const event of events) {\n ctx.signal.throwIfAborted();\n // Filter-specific authorize() cannot run until arguments are supplied.\n if (\n event.authorizeDiscovery === undefined ||\n (await event.authorizeDiscovery(ctx)) === true\n ) {\n visible.push(event);\n }\n }\n ctx.signal.throwIfAborted();\n return visible;\n }\n\n server.server.setRequestHandler(\n \"events/list\",\n { params: listParams },\n async (params, ctx) =>\n safe(async () => {\n const auth = await context(ctx);\n // This adapter returns the complete account-scoped catalog in one page.\n if (params.cursor !== undefined)\n throw new ProtocolError(-32602, \"Invalid catalog cursor\");\n const events = await catalog(auth);\n return {\n events: events.map((event) => ({\n name: event.name,\n ...(event.description !== undefined\n ? { description: event.description }\n : {}),\n delivery: [\"webhook\"],\n inputSchema: event.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n }),\n payloadSchema: event.payloadSchema[\"~standard\"].jsonSchema.output({\n target: \"draft-2020-12\",\n }),\n })),\n };\n }),\n );\n\n server.server.setRequestHandler(\n \"events/subscribe\",\n { params: subscribeParams },\n async (params, ctx) =>\n safe(async () => {\n const auth = await context(ctx);\n if (params.delivery.mode !== \"webhook\") {\n throw new ProtocolError(-32014, \"Unsupported delivery mode\", {\n feature: \"deliveryMode\",\n });\n }\n validateUrl(params.delivery.url);\n validateSecret(params.delivery.secret);\n const event = (await catalog(auth)).find(\n (candidate) => candidate.name === params.name,\n );\n if (!event)\n throw new ProtocolError(-32011, \"Event not found\", { kind: \"event\" });\n const checked = await event.inputSchema[\"~standard\"].validate(\n params.arguments,\n );\n if (\n checked.issues ||\n canonical(checked.value) !== canonical(params.arguments)\n ) {\n throw new ProtocolError(\n -32602,\n \"Arguments must match the event schema without transformation\",\n );\n }\n if (\n (await event.authorize(params.arguments, {\n principal: auth.principal,\n signal: auth.signal,\n })) !== true\n ) {\n throw new ProtocolError(-32012, \"Event arguments are not authorized\");\n }\n const key = await subscriptionKey(\n auth.principal,\n params.name,\n params.arguments,\n params.delivery.url,\n );\n auth.signal.throwIfAborted();\n const operationId = await options.subscriptions.prepare(key, auth);\n if (typeof operationId !== \"string\" || !operationId.trim()) {\n throw new Error(\"Invalid subscription operation\");\n }\n auth.signal.throwIfAborted();\n const verification = await options.delivery.verifyEndpoint(\n { ...key, secret: params.delivery.secret },\n auth,\n );\n if (verification.verified !== true) {\n const reason = webhookFailures.includes(verification.reason)\n ? verification.reason\n : \"challenge_failed\";\n throw new ProtocolError(-32015, \"Callback verification failed\", {\n reason,\n });\n }\n auth.signal.throwIfAborted();\n const ttl =\n params.ttlMs === null && options.ttl?.allowNoExpiry\n ? null\n : Math.max(minMs, Math.min(params.ttlMs ?? defaultMs, maxMs));\n const refreshBefore =\n ttl === null ? null : new Date(Date.now() + ttl).toISOString();\n const state = await options.subscriptions.upsert(\n {\n ...key,\n secret: params.delivery.secret,\n refreshBefore,\n cursor: params.cursor ?? null,\n ...(params.maxAgeMs !== undefined\n ? { maxAgeMs: params.maxAgeMs }\n : {}),\n },\n auth,\n operationId,\n );\n const result = await subscriptionState[\"~standard\"].validate(state);\n if (result.issues) throw new Error(\"Invalid subscription state\");\n return { id: key.id, refreshBefore, ...result.value };\n }),\n );\n\n server.server.setRequestHandler(\n \"events/unsubscribe\",\n { params: unsubscribeParams },\n async (params, ctx) =>\n safe(async () => {\n const auth = await context(ctx);\n validateUrl(params.delivery.url);\n const key = await subscriptionKey(\n auth.principal,\n params.name,\n params.arguments,\n params.delivery.url,\n );\n // Cleanup must remain possible after an event is removed or its schema changes.\n auth.signal.throwIfAborted();\n if (!(await options.subscriptions.remove(key, auth))) {\n throw new ProtocolError(-32011, \"Subscription not found\", {\n kind: \"subscription\",\n });\n }\n return {};\n }),\n );\n}\n\nfunction validateUrl(value: string): void {\n try {\n const url = new URL(value);\n if (\n !value.startsWith(\"https://\") ||\n url.protocol !== \"https:\" ||\n url.username ||\n url.password ||\n url.hash\n )\n throw new Error();\n } catch {\n throw new ProtocolError(\n -32602,\n \"Callback must be an HTTPS URL without credentials or a fragment\",\n );\n }\n}\n\nfunction validateSecret(value: string): void {\n try {\n if (!/^whsec_[A-Za-z0-9+/]+={0,2}$/.test(value)) throw new Error();\n const encoded = value.slice(6);\n const decoded = atob(encoded);\n if (\n decoded.length < 24 ||\n decoded.length > 64 ||\n btoa(decoded).replace(/=+$/, \"\") !== encoded.replace(/=+$/, \"\")\n )\n throw new Error();\n } catch {\n throw new ProtocolError(-32602, \"Invalid Standard Webhooks secret\");\n }\n}\n\nasync function subscriptionKey(\n principal: string,\n name: string,\n args: Record<string, unknown>,\n url: string,\n): Promise<ExperimentalMcpSubscriptionKey> {\n const bytes = new TextEncoder().encode(\n canonical([principal, url, name, args]),\n );\n const hash = await crypto.subtle.digest(\"SHA-256\", bytes);\n const id =\n \"sub_\" +\n Array.from(new Uint8Array(hash), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n return { id, principal, name, arguments: args, url };\n}\n\nasync function safe<T>(run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (error) {\n // Adapter exceptions can contain webhook secrets or endpoint response data.\n if (\n error instanceof ProtocolError &&\n [-32602, -32011, -32012, -32013, -32014, -32015].includes(error.code)\n ) {\n const messages: Record<number, string> = {\n [-32602]: \"Invalid event parameters\",\n [-32011]: \"Event or subscription not found\",\n [-32012]: \"Forbidden\",\n [-32013]: \"Resource exhausted\",\n [-32014]: \"Unsupported event option\",\n [-32015]: \"Callback verification failed\",\n };\n // Only fixed categories cross the wire, including for intentional adapter errors.\n const data = error.data as\n | { reason?: unknown; kind?: unknown; feature?: unknown }\n | undefined;\n const detail =\n error.code === -32015 &&\n webhookFailures.some((reason) => reason === data?.reason)\n ? { reason: data!.reason }\n : error.code === -32011 &&\n (data?.kind === \"event\" || data?.kind === \"subscription\")\n ? { kind: data.kind }\n : error.code === -32014 && data?.feature === \"deliveryMode\"\n ? { feature: \"deliveryMode\" }\n : undefined;\n throw new ProtocolError(error.code, messages[error.code], detail);\n }\n throw new ProtocolError(-32603, \"MCP Events operation failed\");\n }\n}\n","import {\n fromJsonSchema,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\";\nimport type { ExperimentalMcpSubscriptionState } from \"./types\";\n\nconst object = { type: \"object\", additionalProperties: true } as const;\nconst name = { type: \"string\", minLength: 1 } as const;\nconst integer = {\n type: \"integer\",\n minimum: 1,\n maximum: Number.MAX_SAFE_INTEGER,\n} as const;\n\nexport const listParams: StandardSchemaWithJSON<{ cursor?: string }> =\n fromJsonSchema({\n type: \"object\",\n properties: { cursor: { type: \"string\" } },\n });\n\nexport interface SubscriptionParams {\n name: string;\n arguments: Record<string, unknown>;\n delivery: { mode: string; url: string; secret: string };\n cursor?: string | null;\n maxAgeMs?: number;\n ttlMs?: number | null;\n}\n\nexport const subscribeParams: StandardSchemaWithJSON<SubscriptionParams> =\n fromJsonSchema({\n type: \"object\",\n required: [\"name\", \"arguments\", \"delivery\"],\n properties: {\n name,\n arguments: object,\n delivery: {\n type: \"object\",\n required: [\"mode\", \"url\", \"secret\"],\n properties: { mode: name, url: name, secret: name },\n },\n cursor: { type: [\"string\", \"null\"] },\n maxAgeMs: integer,\n ttlMs: { ...integer, type: [\"integer\", \"null\"] },\n },\n });\n\nexport const unsubscribeParams: StandardSchemaWithJSON<\n Pick<SubscriptionParams, \"name\" | \"arguments\"> & { delivery: { url: string } }\n> = fromJsonSchema({\n type: \"object\",\n required: [\"name\", \"arguments\", \"delivery\"],\n properties: {\n name,\n arguments: object,\n delivery: {\n type: \"object\",\n required: [\"url\"],\n properties: { url: name },\n },\n },\n});\n\nexport const webhookFailures = [\n \"connection_refused\",\n \"timeout\",\n \"tls_error\",\n \"http_4xx\",\n \"http_5xx\",\n \"challenge_failed\",\n] as const;\n\nexport const subscriptionState: StandardSchemaWithJSON<ExperimentalMcpSubscriptionState> =\n fromJsonSchema({\n type: \"object\",\n required: [\"cursor\", \"truncated\"],\n additionalProperties: false,\n properties: {\n cursor: { type: [\"string\", \"null\"] },\n truncated: { type: \"boolean\" },\n deliveryStatus: {\n type: \"object\",\n required: [\"active\", \"lastError\"],\n additionalProperties: false,\n properties: {\n active: { type: \"boolean\" },\n lastDeliveryAt: { type: \"string\", format: \"date-time\" },\n lastError: { enum: [...webhookFailures, null] },\n failedSince: { type: \"string\", format: \"date-time\" },\n throttled: { type: \"boolean\" },\n retryAfterMs: { type: \"integer\", minimum: 0 },\n },\n },\n },\n });\n","/** Canonicalize plain JSON only; never silently discard or coerce values. */\nexport function canonical(\n value: unknown,\n ancestors = new Set<object>(),\n): string {\n if (value === null) return \"null\";\n if (typeof value === \"string\" || typeof value === \"boolean\")\n return JSON.stringify(value);\n if (typeof value === \"number\" && Number.isFinite(value))\n return JSON.stringify(value);\n if (\n typeof value !== \"object\" ||\n ancestors.has(value) ||\n (!Array.isArray(value) &&\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null)\n )\n throw new Error(\"Invalid JSON value\");\n\n ancestors.add(value);\n try {\n if (Array.isArray(value))\n return `[${Array.from(value, (item) => canonical(item, ancestors)).join(\n \",\",\n )}]`;\n return `{${Object.keys(value)\n .sort()\n .map(\n (key) =>\n `${JSON.stringify(key)}:${canonical(\n (value as Record<string, unknown>)[key],\n ancestors,\n )}`,\n )\n .join(\",\")}}`;\n } finally {\n ancestors.delete(value);\n }\n}\n","import type { StandardSchemaWithJSON } from \"@modelcontextprotocol/server\";\nimport { canonical } from \"./json\";\nimport type {\n ExperimentalMcpEventDeliveryErrorCode,\n ExperimentalMcpEventDeliveryValidationOptions,\n} from \"./types\";\n\nconst messages: Record<ExperimentalMcpEventDeliveryErrorCode, string> = {\n invalid_subscription: \"A valid stored subscription is required\",\n event_mismatch: \"Event definition does not match the subscription\",\n expired: \"Subscription has expired\",\n invalid_arguments: \"Stored arguments must match the event schema unchanged\",\n invalid_payload: \"Payload must be JSON matching the event schema unchanged\",\n forbidden: \"Subscription principal is no longer authorized\",\n};\n\n/** @experimental A validation failure; contains no payload or credentials. */\nexport class ExperimentalMcpEventDeliveryError extends Error {\n constructor(public readonly code: ExperimentalMcpEventDeliveryErrorCode) {\n super(messages[code]);\n this.name = \"ExperimentalMcpEventDeliveryError\";\n }\n}\n\n/**\n * Validate a candidate event against a current stored subscription. Resolves\n * without modifying/transforming the payload, or throws to prevent delivery.\n *\n * @experimental Does not load state, match source events to filters, sign/send,\n * or coordinate cancellation. The worker must gate dispatch on active state.\n * Authorization/schema backend exceptions and abort reasons propagate unchanged.\n */\nexport async function experimental_validateMcpEventDelivery({\n event,\n subscription,\n payload,\n signal = new AbortController().signal,\n}: ExperimentalMcpEventDeliveryValidationOptions): Promise<void> {\n signal.throwIfAborted();\n if (\n !subscription ||\n typeof subscription.principal !== \"string\" ||\n !subscription.principal.trim() ||\n typeof subscription.name !== \"string\" ||\n !subscription.name ||\n !subscription.arguments ||\n typeof subscription.arguments !== \"object\" ||\n Array.isArray(subscription.arguments)\n )\n throw new ExperimentalMcpEventDeliveryError(\"invalid_subscription\");\n if (event.name !== subscription.name)\n throw new ExperimentalMcpEventDeliveryError(\"event_mismatch\");\n\n const expiry =\n subscription.refreshBefore === null\n ? null\n : typeof subscription.refreshBefore === \"string\"\n ? Date.parse(subscription.refreshBefore)\n : NaN;\n if (expiry !== null && !Number.isFinite(expiry))\n throw new ExperimentalMcpEventDeliveryError(\"invalid_subscription\");\n\n function checkLifetime() {\n signal.throwIfAborted();\n if (expiry !== null && expiry <= Date.now())\n throw new ExperimentalMcpEventDeliveryError(\"expired\");\n }\n checkLifetime();\n await validateUnchanged(\n event.inputSchema,\n subscription.arguments,\n \"invalid_arguments\",\n );\n checkLifetime();\n await validateUnchanged(event.payloadSchema, payload, \"invalid_payload\");\n checkLifetime();\n const authorized = await event.authorize(subscription.arguments, {\n principal: subscription.principal,\n signal,\n });\n // Async schema/policy checks can outlive the grant or worker cancellation.\n checkLifetime();\n if (authorized !== true)\n throw new ExperimentalMcpEventDeliveryError(\"forbidden\");\n}\n\nasync function validateUnchanged(\n schema: StandardSchemaWithJSON,\n value: unknown,\n code: \"invalid_arguments\" | \"invalid_payload\",\n): Promise<void> {\n function json(input: unknown) {\n try {\n return canonical(input);\n } catch {\n throw new ExperimentalMcpEventDeliveryError(code);\n }\n }\n const before = json(value);\n const result = await schema[\"~standard\"].validate(value);\n if (result.issues || json(result.value) !== before || json(value) !== before)\n throw new ExperimentalMcpEventDeliveryError(code);\n}\n","import type { AuthInfo } from \"@modelcontextprotocol/server\";\nimport {\n OAuthError,\n OAuthErrorCode,\n bearerAuthChallengeResponse,\n} from \"@modelcontextprotocol/server\";\nimport { getPublicOrigin } from \"../lib/url\";\n\ndeclare global {\n interface Request {\n auth?: AuthInfo;\n }\n}\n\nexport function withMcpAuth(\n handler: (req: Request) => Response | Promise<Response>,\n verifyToken: (\n req: Request,\n bearerToken?: string,\n ) => AuthInfo | undefined | Promise<AuthInfo | undefined>,\n {\n required = false,\n resourceMetadataPath = \"/.well-known/oauth-protected-resource\",\n requiredScopes,\n resourceUrl,\n }: {\n required?: boolean;\n resourceMetadataPath?: string;\n requiredScopes?: string[];\n /**\n * Explicit resource URL override. When provided, this URL is used as the\n * origin for constructing the resource_metadata URL. Use this when running\n * behind a proxy that doesn't set standard forwarding headers, or when you\n * need to specify a specific public URL.\n *\n * If not provided, the origin is automatically detected from proxy headers\n * (X-Forwarded-Host, X-Forwarded-Proto, Forwarded) or falls back to req.url.\n */\n resourceUrl?: string;\n } = {},\n) {\n return async (req: Request) => {\n const origin = resourceUrl ?? getPublicOrigin(req);\n const resourceMetadataUrl = `${origin}${resourceMetadataPath}`;\n const challengeOptions = { requiredScopes, resourceMetadataUrl };\n\n const authHeader = req.headers.get(\"Authorization\");\n const [type, token] = authHeader?.split(\" \") || [];\n\n // Only support bearer token as per the MCP spec\n // https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization#2-6-1-token-requirements\n const bearerToken = type?.toLowerCase() === \"bearer\" ? token : undefined;\n\n let authInfo: AuthInfo | undefined;\n try {\n authInfo = await verifyToken(req, bearerToken);\n } catch (error) {\n console.error(\"Unexpected error authenticating bearer token:\", error);\n return bearerAuthChallengeResponse(\n new OAuthError(OAuthErrorCode.InvalidToken, \"Invalid token\"),\n challengeOptions,\n );\n }\n\n try {\n if (required && !authInfo) {\n throw new OAuthError(\n OAuthErrorCode.InvalidToken,\n \"No authorization provided\",\n );\n }\n\n if (!authInfo) {\n return handler(req);\n }\n\n // Check if token has the required scopes (if any)\n if (requiredScopes?.length) {\n const hasAllScopes = requiredScopes.every((scope) =>\n authInfo!.scopes.includes(scope),\n );\n\n if (!hasAllScopes) {\n throw new OAuthError(\n OAuthErrorCode.InsufficientScope,\n \"Insufficient scope\",\n );\n }\n }\n\n // Check if the token is expired\n if (authInfo.expiresAt && authInfo.expiresAt < Date.now() / 1000) {\n throw new OAuthError(OAuthErrorCode.InvalidToken, \"Token has expired\");\n }\n\n // Set auth info on the request object after successful verification\n req.auth = authInfo;\n\n return handler(req);\n } catch (error) {\n if (!OAuthError.isInstance(error)) {\n console.error(\"Unexpected error authenticating bearer token:\", error);\n }\n // Maps invalid_token → 401, insufficient_scope → 403 (both with a\n // WWW-Authenticate challenge advertising resource_metadata), anything\n // unexpected → 500 server_error.\n return bearerAuthChallengeResponse(error, challengeOptions);\n }\n };\n}\n","/**\n * Get the public-facing origin from a request, respecting proxy headers.\n *\n * When running behind a reverse proxy (e.g., nginx, Vercel, Cloudflare),\n * the `req.url` typically reflects the internal URL (e.g., http://localhost:3000).\n * This function reconstructs the public-facing origin using standard proxy headers.\n *\n * Header precedence:\n * 1. X-Forwarded-Host + X-Forwarded-Proto (most common)\n * 2. Forwarded header (RFC 7239)\n * 3. Falls back to req.url origin\n *\n * @param req - The incoming request\n * @returns The public-facing origin (e.g., \"https://example.org\")\n */\nexport function getPublicOrigin(req: Request): string {\n const forwardedHost = req.headers.get(\"x-forwarded-host\");\n const forwardedProto = req.headers.get(\"x-forwarded-proto\");\n\n // If we have X-Forwarded-Host, construct origin from forwarded headers\n if (forwardedHost) {\n // X-Forwarded-Host can contain multiple comma-separated values; use the first (leftmost)\n const host = forwardedHost.split(\",\")[0].trim();\n // X-Forwarded-Proto can also be comma-separated\n const proto = forwardedProto?.split(\",\")[0].trim() || \"https\";\n return `${proto}://${host}`;\n }\n\n // Check RFC 7239 Forwarded header (less common but standardized)\n const forwarded = req.headers.get(\"forwarded\");\n if (forwarded) {\n const parsed = parseForwardedHeader(forwarded);\n if (parsed.host) {\n const proto = parsed.proto || \"https\";\n return `${proto}://${parsed.host}`;\n }\n }\n\n // Fallback to req.url origin\n return new URL(req.url).origin;\n}\n\n/**\n * Get the public-facing URL from a request, respecting proxy headers.\n *\n * @param req - The incoming request\n * @returns The public-facing URL with the correct origin\n */\nexport function getPublicUrl(req: Request): URL {\n const url = new URL(req.url);\n const publicOrigin = getPublicOrigin(req);\n\n // Construct a new URL with the public origin but preserve pathname, search, and hash\n const result = new URL(url.pathname + url.search + url.hash, publicOrigin);\n return result;\n}\n\n/**\n * Parse the RFC 7239 Forwarded header.\n * Example: \"for=192.0.2.60;proto=https;host=example.com\"\n */\nfunction parseForwardedHeader(\n forwarded: string\n): { host?: string; proto?: string } {\n const result: { host?: string; proto?: string } = {};\n\n // The header can contain multiple comma-separated forwarded elements; use the first\n const firstElement = forwarded.split(\",\")[0];\n\n // Parse key=value pairs separated by semicolons\n const pairs = firstElement.split(\";\");\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\").map((s) => s.trim().toLowerCase());\n if (key === \"host\" && value) {\n // Remove surrounding quotes if present\n result.host = value.replace(/^\"|\"$/g, \"\");\n } else if (key === \"proto\" && value) {\n result.proto = value.replace(/^\"|\"$/g, \"\");\n }\n }\n\n return result;\n}\n","import type { OAuthProtectedResourceMetadata } from \"@modelcontextprotocol/server\";\nimport { getPublicUrl } from \"../lib/url\";\n\n/**\n * CORS headers for OAuth Protected Resource Metadata endpoint.\n * Configured to allow any origin to make the endpoint accessible to web-based MCP clients.\n */\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"*\",\n \"Access-Control-Max-Age\": \"86400\",\n};\n\n/**\n * OAuth 2.0 Protected Resource Metadata endpoint based on RFC 9728.\n * @see https://datatracker.ietf.org/doc/html/rfc9728\n *\n * @param authServerUrls - Array of issuer URLs of the OAuth 2.0 Authorization Servers.\n * These should match the \"issuer\" field in the authorization servers'\n * OAuth metadata (RFC 8414).\n * @param resourceUrl - Optional explicit resource URL override. When provided, this URL is\n * used instead of deriving it from the request. Use this when running\n * behind a proxy that doesn't set standard forwarding headers.\n * If not provided, the URL is automatically detected from proxy headers\n * (X-Forwarded-Host, X-Forwarded-Proto, Forwarded) or falls back to req.url.\n */\nexport function protectedResourceHandler({\n authServerUrls,\n resourceUrl: explicitResourceUrl,\n}: {\n authServerUrls: string[];\n resourceUrl?: string;\n}) {\n return (req: Request) => {\n let resource: string;\n\n if (explicitResourceUrl) {\n // Use explicit override if provided\n resource = explicitResourceUrl;\n } else {\n // Auto-detect from proxy headers or req.url\n const publicUrl = getPublicUrl(req);\n\n publicUrl.pathname = publicUrl.pathname\n .replace(/^\\/\\.well-known\\/[^\\/]+/, \"\");\n\n // The URL class does not allow for empty `pathname` and will replace it\n // with \"/\". Here, we correct that.\n resource = publicUrl.pathname === '/'\n ? publicUrl.toString().replace(/\\/$/, '')\n : publicUrl.toString();\n }\n\n const metadata = generateProtectedResourceMetadata({\n authServerUrls,\n resourceUrl: resource,\n });\n\n return new Response(JSON.stringify(metadata), {\n headers: {\n ...corsHeaders,\n \"Cache-Control\": \"max-age=3600\",\n \"Content-Type\": \"application/json\",\n },\n });\n };\n}\n\n/**\n * Generates protected resource metadata for the given auth server URLs and\n * protected resource identifier. The protected resource identifier, as defined\n * in RFC 9728, should be a a URL that uses the https scheme and has no fragment\n * component.\n *\n * @param authServerUrls - Array of issuer URLs of the authorization servers. Each URL should \n * match the \"issuer\" field in the respective authorization server's \n * OAuth metadata (RFC 8414).\n * @param resourceUrl - the protected resource identifier\n * @param additionalMetadata - Additional metadata fields to include in the response\n * @returns Protected resource metadata, serializable to JSON\n */\nexport function generateProtectedResourceMetadata({\n authServerUrls,\n resourceUrl,\n additionalMetadata,\n}: {\n authServerUrls: string[];\n resourceUrl: string;\n additionalMetadata?: Partial<OAuthProtectedResourceMetadata>;\n}): OAuthProtectedResourceMetadata {\n return Object.assign(\n {\n resource: resourceUrl,\n authorization_servers: authServerUrls\n },\n additionalMetadata\n );\n}\n\n/**\n * CORS options request handler for OAuth metadata endpoints.\n * Necessary for MCP clients that operate in web browsers.\n */\nexport function metadataCorsOptionsRequestHandler() {\n return () => {\n return new Response(null, {\n status: 200,\n headers: corsHeaders,\n });\n };\n}\n\n"]}