{"version":3,"file":"index.cjs","names":["DEFAULT_SSE_KEEP_ALIVE_MS","SdkError","SdkErrorCode","messageExtra: MessageExtraInfo","isJSONRPCRequest","resolve!: (response: Response) => void","reject!: (error: Error) => void","isJSONRPCResultResponse","isJSONRPCErrorResponse","ProtocolErrorCode","LADDER_ERROR_HTTP_STATUS","controller!: ReadableStreamDefaultController","armSseKeepAlive","SUPPORTED_PROTOCOL_VERSIONS","DEFAULT_SSE_KEEP_ALIVE_MS","armSseKeepAlive","error: { code: number; message: string; data?: string }","streamController: ReadableStreamDefaultController","keepAliveTimer: ReturnType | undefined","headers: Record","streamId: string | undefined","replayedStreamId: string | undefined","isJsonContentType","messages: JSONRPCMessage[]","JSONRPCMessageSchema","isInitializeRequest","isJSONRPCRequest","DEFAULT_NEGOTIATED_PROTOCOL_VERSION","closeSSEStream: (() => void) | undefined","closeStandaloneSSEStream: (() => void) | undefined","isJSONRPCResultResponse","isJSONRPCErrorResponse","eventId: string | undefined","mediaTypeEssence","body: unknown","bodyText: string","classifyInboundRequest","bus: ServerEventBus","InMemoryServerEventBus","createServerNotifier","createListenRouter","DEFAULT_MAX_SUBSCRIPTIONS","DEFAULT_SSE_KEEP_ALIVE_MS","legacyHandler: LegacyHttpHandler | undefined","SUPPORTED_MODERN_PROTOCOL_VERSIONS","UnsupportedProtocolVersionError","validateStandardRequestHeaders","requestMetaOf","CLIENT_CAPABILITIES_META_KEY","requiredClientCapabilitiesForRequest","missingClientCapabilities","MissingRequiredClientCapabilityError","httpStatusForErrorCode","McpServer","serverIdentityOf","scanXMcpHeaderDeclarations","validateMcpParamHeaders","CLIENT_INFO_META_KEY","SdkError","SdkErrorCode","modernOnlyStrictRejection","isJsonContentType","OAuthError","OAuthErrorCode","hostname: string","OAuthError","OAuthErrorCode","hostname: string","cryptoKey: ReturnType | undefined","envelope: { p: T; exp: number; b?: string }","macBytes: Uint8Array","_defaultValidator: jsonSchemaValidator | undefined","coreFromJsonSchema","DefaultJsonSchemaValidator"],"sources":["../src/server/perRequestTransport.ts","../src/server/invoke.ts","../src/server/streamableHttp.ts","../src/server/createMcpHandler.ts","../src/server/middleware/bearerAuth.ts","../src/server/middleware/hostHeaderValidation.ts","../src/server/middleware/oauthMetadata.ts","../src/server/middleware/originValidation.ts","../src/server/requestStateCodec.ts","../src/fromJsonSchema.ts"],"sourcesContent":["/**\n * A single-exchange, per-request HTTP server transport for modern-era\n * (protocol revision 2026-07-28) serving.\n *\n * One transport instance serves exactly one already-classified inbound\n * JSON-RPC message and produces exactly one HTTP `Response`:\n *\n * - a `202` with no body for notifications,\n * - a single JSON body for requests whose handler produces no streamed\n * output, or\n * - a lazily-opened SSE stream when the handler emits related messages\n * (notifications or server-to-client requests) before its result — the\n * stream carries those messages and finally the terminal result, then\n * closes.\n *\n * The transport is constructed already-classified: the entry parses and\n * classifies the request body exactly once and hands the classification in via\n * the constructor; the transport attaches it (together with the original\n * request and any caller-provided auth info) to every message it delivers, and\n * the protocol layer validates it against the serving instance's negotiated\n * era. `authInfo` is strictly pass-through — it is never derived from the\n * inbound request's headers here.\n *\n * Deliberately NOT carried over from the session-oriented streamable HTTP\n * transport: session ids and session headers, resumability (event ids,\n * priming events, `Last-Event-ID` replay, retry hints), the standalone GET\n * stream, and request-header validation (which belongs to middleware). The\n * exchange is single-use; serving another request requires a new transport\n * (and, in the per-request serving model, a fresh server instance).\n *\n * Consumers wiring this transport into a custom entry (instead of\n * `createMcpHandler`) inherit the spec's per-error HTTP mandate with it: a\n * terminal `MissingRequiredClientCapabilityError` (-32021) MUST answer 400,\n * which this transport applies whenever the response is still uncommitted.\n * A custom entry must also reject POSTs whose Content-Type media type is not\n * `application/json` (415) before parsing the body — use `isJsonContentType`\n * from the package root; this transport never sees the HTTP headers, so it\n * cannot perform that validation itself.\n */\nimport type {\n AuthInfo,\n JSONRPCMessage,\n JSONRPCNotification,\n JSONRPCRequest,\n MessageClassification,\n MessageExtraInfo,\n RequestId,\n Transport,\n TransportSendOptions\n} from '@modelcontextprotocol/core-internal';\nimport {\n isJSONRPCErrorResponse,\n isJSONRPCRequest,\n isJSONRPCResultResponse,\n LADDER_ERROR_HTTP_STATUS,\n ProtocolErrorCode,\n SdkError,\n SdkErrorCode\n} from '@modelcontextprotocol/core-internal';\n\nimport { armSseKeepAlive, DEFAULT_SSE_KEEP_ALIVE_MS } from './sseKeepAlive';\n\n/**\n * How the transport shapes its HTTP response for a request:\n *\n * - `auto` (default): answer with a single JSON body unless the handler emits\n * a related message before its result, in which case the response upgrades\n * to an SSE stream.\n * - `sse`: always answer handler output over an SSE stream. The stream opens\n * once the request has passed the pre-dispatch validation gates, so ladder\n * rejections keep their mapped HTTP status instead of being framed onto a\n * 200 stream.\n * - `json`: never stream; related messages other than the terminal response\n * are dropped.\n */\nexport type PerRequestResponseMode = 'auto' | 'sse' | 'json';\n\n/** Constructor options for {@linkcode PerRequestHTTPServerTransport}. */\nexport interface PerRequestHTTPServerTransportOptions {\n /** The edge classification of the message this transport will serve. */\n classification: MessageClassification;\n /** Response shaping for the exchange; defaults to `auto`. */\n responseMode?: PerRequestResponseMode;\n /** SSE keep-alive interval in milliseconds; defaults to `15000`, `0` disables. */\n keepAliveMs?: number;\n}\n\n/** Per-exchange context handed to {@linkcode PerRequestHTTPServerTransport.handleMessage}. */\nexport interface PerRequestMessageExtra {\n /**\n * The original HTTP request. Used for handler context and, when the\n * runtime provides an abort signal on it, to cancel the exchange when the\n * client disconnects.\n */\n request?: globalThis.Request;\n /**\n * Validated authentication information supplied by the caller. Strictly\n * pass-through: the transport never populates this from request headers.\n */\n authInfo?: AuthInfo;\n}\n\ninterface DeferredResponse {\n promise: Promise;\n resolve: (response: Response) => void;\n reject: (error: Error) => void;\n settled: boolean;\n}\n\ninterface SseSink {\n controller: ReadableStreamDefaultController;\n encoder: InstanceType;\n closed: boolean;\n keepAliveTimer?: ReturnType;\n}\n\n/**\n * The per-request micro-transport: a real, connected `Transport` whose whole\n * lifetime is one HTTP exchange. See the module documentation for the\n * response shapes it produces.\n */\nexport class PerRequestHTTPServerTransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: T, extra?: MessageExtraInfo) => void;\n\n private readonly _classification: MessageClassification;\n private readonly _responseMode: PerRequestResponseMode;\n\n private _started = false;\n private _used = false;\n private _closed = false;\n private _terminalDelivered = false;\n /**\n * `true` only while the inbound message is being delivered synchronously\n * to the connected protocol layer. The pre-handler gates (the era\n * registry gate, the edge→instance handoff check, the missing-handler\n * rejection) answer inside this window; request handlers always run\n * after it (the protocol layer defers them to a microtask). An error\n * sent inside the window is therefore ladder-originated, and an error\n * sent after it is handler-produced.\n */\n private _dispatchWindowOpen = false;\n private _requestId?: RequestId;\n private _deferredResponse?: DeferredResponse;\n private _sse?: SseSink;\n private _abortCleanup?: () => void;\n private readonly _keepAliveMs: number;\n\n constructor(options: PerRequestHTTPServerTransportOptions) {\n this._classification = options.classification;\n this._responseMode = options.responseMode ?? 'auto';\n this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;\n }\n\n async start(): Promise {\n if (this._started) {\n throw new Error('PerRequestHTTPServerTransport is already started');\n }\n this._started = true;\n }\n\n /**\n * Serves the single exchange: delivers the classified message to the\n * connected server instance and resolves with the HTTP response.\n *\n * Throws when called a second time (the transport is strictly\n * single-use), or before a server has been connected to the transport.\n * The returned promise rejects with a connection-closed error when the\n * transport is closed before a response was produced (for example because\n * the client disconnected).\n */\n async handleMessage(message: JSONRPCRequest | JSONRPCNotification, extra?: PerRequestMessageExtra): Promise {\n if (this._used) {\n throw new Error('PerRequestHTTPServerTransport serves exactly one exchange; construct a new transport per request');\n }\n if (!this._started || this.onmessage === undefined) {\n throw new Error('PerRequestHTTPServerTransport is not connected: connect a server to this transport before handling a message');\n }\n if (this._closed) {\n throw new Error('PerRequestHTTPServerTransport is closed');\n }\n this._used = true;\n\n const signal = extra?.request?.signal;\n if (signal?.aborted) {\n await this.close();\n throw new SdkError(SdkErrorCode.ConnectionClosed, 'The request was aborted before it could be handled');\n }\n\n // authInfo is strictly pass-through from the caller; it is never\n // derived from the inbound request's headers.\n const messageExtra: MessageExtraInfo = {\n classification: this._classification,\n ...(extra?.request !== undefined && { request: extra.request }),\n ...(extra?.authInfo !== undefined && { authInfo: extra.authInfo })\n };\n\n if (isJSONRPCRequest(message)) {\n this._requestId = message.id;\n\n let resolve!: (response: Response) => void;\n let reject!: (error: Error) => void;\n const promise = new Promise((promiseResolve, promiseReject) => {\n resolve = promiseResolve;\n reject = promiseReject;\n });\n this._deferredResponse = { promise, resolve, reject, settled: false };\n\n if (signal !== undefined) {\n const onAbort = () => void this.close();\n signal.addEventListener('abort', onAbort, { once: true });\n this._abortCleanup = () => signal.removeEventListener('abort', onAbort);\n }\n\n this._dispatchWindowOpen = true;\n try {\n this.onmessage(message, messageExtra);\n } finally {\n this._dispatchWindowOpen = false;\n }\n\n if (this._responseMode === 'sse' && !this._closed && !this._deferredResponse.settled) {\n // Forced-SSE exchanges open their stream as soon as the\n // request has passed the pre-dispatch gates: a ladder\n // rejection settles inside the dispatch window with its\n // mapped HTTP status, while handler output — including\n // comment frames written before the first message — streams\n // as before.\n this.upgradeToSse();\n }\n return promise;\n }\n\n // Notifications never get a JSON-RPC response: deliver the message and\n // acknowledge the POST with 202 and no body.\n this.onmessage(message, messageExtra);\n return new Response(null, { status: 202 });\n }\n\n async send(message: JSONRPCMessage, options?: TransportSendOptions): Promise {\n if (this._closed) {\n // The exchange is over; late writes are dropped.\n return;\n }\n\n const isResponse = isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);\n const relatedId = isResponse ? (message as { id: RequestId }).id : options?.relatedRequestId;\n\n if (this._requestId === undefined || relatedId === undefined || relatedId !== this._requestId) {\n if (isResponse) {\n this.onerror?.(new Error(`Received a response for an unknown request id: ${String((message as { id?: unknown }).id)}`));\n }\n // Messages unrelated to the single in-flight request have nowhere\n // to go on a per-request exchange (there is no session-wide\n // stream); they are dropped.\n return;\n }\n\n if (isResponse) {\n if (this._terminalDelivered) {\n return;\n }\n this._terminalDelivered = true;\n\n // The HTTP status is keyed on the error's origin, not on its bare\n // code: only errors produced inside the dispatch window — the\n // validation ladder, the era registry gate and handoff check, a\n // missing handler — are answered with the mapped HTTP status from\n // the ladder table. Handler-produced errors, whatever their code,\n // stay in-band on HTTP 200 — except\n // MissingRequiredClientCapability (-32021), whose 400 the spec\n // mandates per-error with no origin condition and whose only\n // SDK-produced post-window source (the input_required capability\n // gate) cannot fire any earlier — a handler-minted -32021 gets\n // the same 400; a handler RELAYING a downstream peer's\n // -32020/-32022 is not that peer's spec error and stays in-band.\n // Must agree with httpStatusForErrorCode (core-internal), which\n // is deliberately NOT called here: its `?? 400` ladder fallback\n // would wrongly map window codes outside the table.\n // The mapping applies only while no response has been committed:\n // once the stream is open — the handler streamed first, or the\n // exchange is forced-`sse` (which settles its 200 at dispatch\n // end) — the status is on the wire and the error rides the\n // stream. Pre-dispatch ladder rejections always precede the\n // forced-`sse` upgrade, so they keep their mapped status in every\n // response mode.\n const errorCode = isJSONRPCErrorResponse(message) ? message.error.code : undefined;\n const ladderStatus =\n errorCode !== undefined && (this._dispatchWindowOpen || errorCode === ProtocolErrorCode.MissingRequiredClientCapability)\n ? LADDER_ERROR_HTTP_STATUS[errorCode]\n : undefined;\n if (ladderStatus !== undefined && this._sse === undefined) {\n this.settleResponse(Response.json(message, { status: ladderStatus, headers: { 'Content-Type': 'application/json' } }));\n queueMicrotask(() => void this.close());\n return;\n }\n\n if (this._sse !== undefined || this._responseMode === 'sse') {\n // Finalize the stream: serialize the terminal result onto it\n // after everything already enqueued, then close.\n if (this._sse === undefined) {\n this.upgradeToSse();\n }\n this.writeMessageFrame(message);\n this.finalizeStream();\n return;\n }\n\n // Single JSON body.\n this.settleResponse(Response.json(message, { status: 200, headers: { 'Content-Type': 'application/json' } }));\n queueMicrotask(() => void this.close());\n return;\n }\n\n // A message related to the in-flight request that is not its terminal\n // response: a mid-call notification or a server-to-client request\n // emitted by the handler.\n if (this._responseMode === 'json') {\n // JSON responses cannot carry mid-call messages; they are dropped.\n return;\n }\n if (this._sse === undefined) {\n this.upgradeToSse();\n }\n this.writeMessageFrame(message);\n }\n\n /**\n * Writes an SSE comment frame (a keep-alive heartbeat). Dropped when the\n * exchange is not currently streaming.\n */\n writeCommentFrame(comment: string): void {\n if (this._closed || this._sse === undefined || this._sse.closed) {\n return;\n }\n const frame = comment\n .split('\\n')\n .map(line => `: ${line}`)\n .join('\\n');\n this.writeFrame(`${frame}\\n\\n`);\n }\n\n async close(): Promise {\n if (this._closed) {\n return;\n }\n this._closed = true;\n\n this._abortCleanup?.();\n this._abortCleanup = undefined;\n\n if (this._sse?.keepAliveTimer !== undefined) {\n clearInterval(this._sse.keepAliveTimer);\n }\n if (this._sse !== undefined && !this._sse.closed) {\n this._sse.closed = true;\n try {\n this._sse.controller.close();\n } catch {\n // The stream was already closed or cancelled by the consumer.\n }\n }\n\n if (this._deferredResponse !== undefined && !this._deferredResponse.settled) {\n this._deferredResponse.settled = true;\n this._deferredResponse.reject(new SdkError(SdkErrorCode.ConnectionClosed, 'Connection closed before a response was produced'));\n }\n\n this.onclose?.();\n }\n\n private settleResponse(response: Response): void {\n if (this._deferredResponse === undefined || this._deferredResponse.settled) {\n return;\n }\n this._deferredResponse.settled = true;\n this._deferredResponse.resolve(response);\n }\n\n private upgradeToSse(): void {\n let controller!: ReadableStreamDefaultController;\n const readable = new ReadableStream({\n start: streamController => {\n controller = streamController;\n },\n cancel: () => {\n // The client went away mid-stream: tear the exchange down,\n // which aborts the in-flight handler through the connected\n // server's close chain.\n void this.close();\n }\n });\n this._sse = { controller, encoder: new TextEncoder(), closed: false };\n this._sse.keepAliveTimer = armSseKeepAlive(this._keepAliveMs, () => this.writeCommentFrame('keepalive'));\n\n this.settleResponse(\n new Response(readable, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n // Disable proxy buffering so streamed messages are\n // delivered as they are written.\n 'X-Accel-Buffering': 'no'\n }\n })\n );\n }\n\n private finalizeStream(): void {\n if (this._sse?.keepAliveTimer !== undefined) {\n clearInterval(this._sse.keepAliveTimer);\n }\n if (this._sse !== undefined && !this._sse.closed) {\n this._sse.closed = true;\n try {\n this._sse.controller.close();\n } catch {\n // The stream was already cancelled by the consumer.\n }\n }\n queueMicrotask(() => void this.close());\n }\n\n private writeMessageFrame(message: JSONRPCMessage): void {\n this.writeFrame(`event: message\\ndata: ${JSON.stringify(message)}\\n\\n`);\n }\n\n private writeFrame(frame: string): void {\n if (this._sse === undefined || this._sse.closed) {\n return;\n }\n try {\n this._sse.controller.enqueue(this._sse.encoder.encode(frame));\n } catch (error) {\n this.onerror?.(new Error(`Failed to write to the response stream: ${error}`));\n }\n }\n}\n","/**\n * The internal per-request invoke seam for modern-era HTTP serving.\n *\n * One classified inbound message is served by composing existing pieces, with\n * no changes to the protocol dispatch layer:\n *\n * server instance (from the consumer's factory)\n * → `connect(per-request transport)`\n * → inject the classified message through the transport's message callback\n * → capture the value (a single JSON body or an SSE stream) via the\n * transport's send path.\n *\n * The seam is value-returning and independently testable: it resolves with the\n * HTTP `Response` for the exchange. Marking factory instances as modern-era\n * (and installing modern-only handlers) is the calling entry's responsibility\n * and happens before this seam runs; the seam itself never writes era state.\n */\nimport type { AuthInfo, JSONRPCNotification, JSONRPCRequest, MessageClassification } from '@modelcontextprotocol/core-internal';\n\nimport type { McpServer } from './mcp';\nimport type { PerRequestResponseMode } from './perRequestTransport';\nimport { PerRequestHTTPServerTransport } from './perRequestTransport';\nimport type { Server } from './server';\n\n/** Per-exchange context for {@linkcode invoke}. */\nexport interface InvokeContext {\n /** The edge classification of the message (computed once, at the entry boundary). */\n classification: MessageClassification;\n /** The original HTTP request, when serving HTTP traffic. */\n request?: globalThis.Request;\n /**\n * Validated authentication information supplied by the caller. Strictly\n * pass-through — never derived from request headers by this seam.\n */\n authInfo?: AuthInfo;\n /** Response shaping for the exchange; defaults to `auto` (lazy SSE upgrade). */\n responseMode?: PerRequestResponseMode;\n /** SSE keep-alive interval for the exchange. */\n keepAliveMs?: number;\n}\n\n/**\n * Serves one classified inbound message on the given server instance and\n * returns the HTTP response for the exchange.\n *\n * The instance is connected to a fresh single-exchange transport, the message\n * is injected through the normal transport message path, and whatever the\n * dispatch layer produces (the handler result, a protocol-level rejection, or\n * streamed related messages followed by the result) is captured as the\n * returned `Response`. For request exchanges, teardown rides the transport's\n * close chain once the terminal response has been delivered; notification\n * exchanges resolve with the 202 response immediately and do NOT run the\n * close chain — the transport stays connected until the caller closes it or\n * drops the per-request instance, which is the caller's choice either way.\n */\nexport async function invoke(\n server: Server | McpServer,\n message: JSONRPCRequest | JSONRPCNotification,\n ctx: InvokeContext\n): Promise {\n const transport = new PerRequestHTTPServerTransport({\n classification: ctx.classification,\n ...(ctx.responseMode !== undefined && { responseMode: ctx.responseMode }),\n ...(ctx.keepAliveMs !== undefined && { keepAliveMs: ctx.keepAliveMs })\n });\n await server.connect(transport);\n return transport.handleMessage(message, {\n ...(ctx.request !== undefined && { request: ctx.request }),\n ...(ctx.authInfo !== undefined && { authInfo: ctx.authInfo })\n });\n}\n","/**\n * Web Standards Streamable HTTP Server Transport\n *\n * This is the core transport implementation using Web Standard APIs (`Request`, `Response`, `ReadableStream`).\n * It can run on any runtime that supports Web Standards: Node.js 18+, Cloudflare Workers, Deno, Bun, etc.\n *\n * For Node.js Express/HTTP compatibility, use {@linkcode @modelcontextprotocol/node!NodeStreamableHTTPServerTransport | NodeStreamableHTTPServerTransport} which wraps this transport.\n */\n\nimport type { AuthInfo, JSONRPCMessage, MessageExtraInfo, RequestId, Transport } from '@modelcontextprotocol/core-internal';\nimport {\n DEFAULT_NEGOTIATED_PROTOCOL_VERSION,\n isInitializeRequest,\n isJsonContentType,\n isJSONRPCErrorResponse,\n isJSONRPCRequest,\n isJSONRPCResultResponse,\n JSONRPCMessageSchema,\n SUPPORTED_PROTOCOL_VERSIONS\n} from '@modelcontextprotocol/core-internal';\n\nimport { armSseKeepAlive, DEFAULT_SSE_KEEP_ALIVE_MS } from './sseKeepAlive';\n\nexport type StreamId = string;\nexport type EventId = string;\n\n/**\n * Interface for resumability support via event storage\n */\nexport interface EventStore {\n /**\n * Stores an event for later retrieval\n * @param streamId ID of the stream the event belongs to\n * @param message The JSON-RPC message to store\n * @returns The generated event ID for the stored event\n */\n storeEvent(streamId: StreamId, message: JSONRPCMessage): Promise;\n\n /**\n * Get the stream ID associated with a given event ID.\n * @param eventId The event ID to look up\n * @returns The stream ID, or `undefined` if not found\n *\n * Optional: If not provided, the SDK will use the `streamId` returned by\n * {@linkcode replayEventsAfter} for stream mapping.\n */\n getStreamIdForEventId?(eventId: EventId): Promise;\n\n replayEventsAfter(\n lastEventId: EventId,\n {\n send\n }: {\n send: (eventId: EventId, message: JSONRPCMessage) => Promise;\n }\n ): Promise;\n}\n\n/**\n * Internal stream mapping for managing SSE connections\n */\ninterface StreamMapping {\n /** Stream controller for pushing SSE data - only used with `ReadableStream` approach */\n controller?: ReadableStreamDefaultController;\n /** Text encoder for SSE formatting */\n encoder?: InstanceType;\n /** Promise resolver for JSON response mode */\n resolveJson?: (response: Response) => void;\n /**\n * Event ids already written to this stream by `replayEventsAfter` — lets\n * `send()` skip a duplicate write when the resumed stream registered\n * during the `storeEvent()` await and replay already delivered the event.\n */\n replayedEventIds?: Set;\n /** Cleanup function to close stream and remove mapping */\n cleanup: () => void;\n}\n\n/**\n * Configuration options for {@linkcode WebStandardStreamableHTTPServerTransport}\n */\nexport interface WebStandardStreamableHTTPServerTransportOptions {\n /**\n * Function that generates a session ID for the transport.\n * The session ID SHOULD be globally unique and cryptographically secure (e.g., a securely generated UUID, a JWT, or a cryptographic hash)\n *\n * If not provided, session management is disabled (stateless mode).\n */\n sessionIdGenerator?: (() => string) | undefined;\n\n /**\n * A callback for session initialization events\n * This is called when the server initializes a new session.\n * Useful in cases when you need to register multiple mcp sessions\n * and need to keep track of them.\n * @param sessionId The generated session ID\n */\n onsessioninitialized?: ((sessionId: string) => void | Promise) | undefined;\n\n /**\n * A callback for session close events\n * This is called when the server closes a session due to a `DELETE` request.\n * Useful in cases when you need to clean up resources associated with the session.\n * Note that this is different from the transport closing, if you are handling\n * HTTP requests from multiple nodes you might want to close each\n * {@linkcode WebStandardStreamableHTTPServerTransport} after a request is completed while still keeping the\n * session open/running.\n * @param sessionId The session ID that was closed\n */\n onsessionclosed?: ((sessionId: string) => void | Promise) | undefined;\n\n /**\n * If `true`, the server will return JSON responses instead of starting an SSE stream.\n * This can be useful for simple request/response scenarios without streaming.\n * Default is `false` (SSE streams are preferred).\n */\n enableJsonResponse?: boolean;\n\n /**\n * Event store for resumability support\n * If provided, resumability will be enabled, allowing clients to reconnect and resume messages\n */\n eventStore?: EventStore;\n\n /**\n * List of allowed `Host` header values for DNS rebinding protection.\n * If not specified, host validation is disabled.\n * @deprecated Use external middleware for host validation instead.\n */\n allowedHosts?: string[];\n\n /**\n * List of allowed `Origin` header values for DNS rebinding protection.\n * If not specified, origin validation is disabled.\n * @deprecated Use external middleware for origin validation instead.\n */\n allowedOrigins?: string[];\n\n /**\n * Enable DNS rebinding protection (requires `allowedHosts` and/or `allowedOrigins` to be configured).\n * Default is `false` for backwards compatibility.\n * @deprecated Use external middleware for DNS rebinding protection instead.\n */\n enableDnsRebindingProtection?: boolean;\n\n /**\n * Retry interval in milliseconds to suggest to clients in SSE `retry` field.\n * When set, the server will send a `retry` field in SSE priming events to control\n * client reconnection timing for polling behavior.\n */\n retryInterval?: number;\n\n /**\n * Interval in milliseconds between SSE keep-alive comment frames.\n * Defaults to `15000`; set to `0` to disable.\n */\n keepAliveMs?: number;\n\n /**\n * List of protocol versions that this transport will accept.\n * Used to validate the `mcp-protocol-version` header in incoming requests.\n *\n * Note: When using {@linkcode server/server.Server.connect | Server.connect()}, the server automatically passes its\n * `supportedProtocolVersions` to the transport, so you typically don't need\n * to set this option directly.\n *\n * @default {@linkcode SUPPORTED_PROTOCOL_VERSIONS}\n */\n supportedProtocolVersions?: string[];\n}\n\n/**\n * Options for handling a request\n */\nexport interface HandleRequestOptions {\n /**\n * Pre-parsed request body. If provided, the transport will use this instead of parsing `req.json()`.\n * Useful when using body-parser middleware that has already parsed the body.\n */\n parsedBody?: unknown;\n\n /**\n * Authentication info from middleware. If provided, will be passed to message handlers.\n */\n authInfo?: AuthInfo;\n}\n\n/**\n * Server transport for Web Standards Streamable HTTP: this implements the MCP Streamable HTTP transport specification\n * using Web Standard APIs (`Request`, `Response`, `ReadableStream`).\n *\n * This transport works on any runtime that supports Web Standards: Node.js 18+, Cloudflare Workers, Deno, Bun, etc.\n *\n * In stateful mode:\n * - Session ID is generated and included in response headers\n * - Session ID is always included in initialization responses\n * - Requests with invalid session IDs are rejected with `404 Not Found`\n * - Non-initialization requests without a session ID are rejected with `400 Bad Request`\n * - State is maintained in-memory (connections, message history)\n *\n * In stateless mode:\n * - No Session ID is included in any responses\n * - No session validation is performed\n *\n * @example Stateful setup\n * ```ts source=\"./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_stateful\"\n * const server = new McpServer({ name: 'my-server', version: '1.0.0' });\n *\n * const transport = new WebStandardStreamableHTTPServerTransport({\n * sessionIdGenerator: () => crypto.randomUUID()\n * });\n *\n * await server.connect(transport);\n * ```\n *\n * @example Stateless setup\n * ```ts source=\"./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_stateless\"\n * const transport = new WebStandardStreamableHTTPServerTransport({\n * sessionIdGenerator: undefined\n * });\n * ```\n *\n * @example Hono.js\n * ```ts source=\"./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_hono\"\n * app.all('/mcp', async c => {\n * return transport.handleRequest(c.req.raw);\n * });\n * ```\n *\n * @example Cloudflare Workers\n * ```ts source=\"./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_workers\"\n * const worker = {\n * async fetch(request: Request): Promise {\n * return transport.handleRequest(request);\n * }\n * };\n * ```\n */\nexport class WebStandardStreamableHTTPServerTransport implements Transport {\n // when sessionId is not set (undefined), it means the transport is in stateless mode\n private sessionIdGenerator: (() => string) | undefined;\n private _started: boolean = false;\n private _closed: boolean = false;\n private _streamMapping: Map = new Map();\n private _requestToStreamMapping: Map = new Map();\n private _requestResponseMap: Map = new Map();\n private _initialized: boolean = false;\n private _enableJsonResponse: boolean = false;\n private _standaloneSseStreamId: string = '_GET_stream';\n private _eventStore?: EventStore;\n private _onsessioninitialized?: ((sessionId: string) => void | Promise) | undefined;\n private _onsessionclosed?: ((sessionId: string) => void | Promise) | undefined;\n private _allowedHosts?: string[];\n private _allowedOrigins?: string[];\n private _enableDnsRebindingProtection: boolean;\n private _retryInterval?: number;\n private _supportedProtocolVersions: string[];\n private _keepAliveMs: number;\n\n sessionId?: string;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n constructor(options: WebStandardStreamableHTTPServerTransportOptions = {}) {\n this.sessionIdGenerator = options.sessionIdGenerator;\n this._enableJsonResponse = options.enableJsonResponse ?? false;\n this._eventStore = options.eventStore;\n this._onsessioninitialized = options.onsessioninitialized;\n this._onsessionclosed = options.onsessionclosed;\n this._allowedHosts = options.allowedHosts;\n this._allowedOrigins = options.allowedOrigins;\n this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;\n this._retryInterval = options.retryInterval;\n this._supportedProtocolVersions = options.supportedProtocolVersions ?? SUPPORTED_PROTOCOL_VERSIONS;\n this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;\n }\n\n private startKeepAlive(\n controller: ReadableStreamDefaultController,\n encoder: InstanceType\n ): ReturnType | undefined {\n if (this._closed) return undefined;\n\n const timer = armSseKeepAlive(this._keepAliveMs, () => {\n try {\n controller.enqueue(encoder.encode(': keepalive\\n\\n'));\n } catch {\n if (timer !== undefined) clearInterval(timer);\n }\n });\n return timer;\n }\n\n /**\n * Starts the transport. This is required by the {@linkcode Transport} interface but is a no-op\n * for the Streamable HTTP transport as connections are managed per-request.\n */\n async start(): Promise {\n if (this._started) {\n throw new Error('Transport already started');\n }\n this._started = true;\n }\n\n /**\n * Sets the supported protocol versions for header validation.\n * Called by the server during {@linkcode server/server.Server.connect | connect()} to pass its supported versions.\n */\n setSupportedProtocolVersions(versions: string[]): void {\n this._supportedProtocolVersions = versions;\n }\n\n /**\n * Helper to create a JSON error response\n */\n private createJsonErrorResponse(\n status: number,\n code: number,\n message: string,\n options?: { headers?: Record; data?: string }\n ): Response {\n const error: { code: number; message: string; data?: string } = { code, message };\n if (options?.data !== undefined) {\n error.data = options.data;\n }\n return Response.json(\n {\n jsonrpc: '2.0',\n error,\n id: null\n },\n {\n status,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n }\n );\n }\n\n /**\n * Validates request headers for DNS rebinding protection.\n * @returns Error response if validation fails, `undefined` if validation passes.\n */\n private validateRequestHeaders(req: Request): Response | undefined {\n // Skip validation if protection is not enabled\n if (!this._enableDnsRebindingProtection) {\n return undefined;\n }\n\n // Validate Host header if allowedHosts is configured\n if (this._allowedHosts && this._allowedHosts.length > 0) {\n const hostHeader = req.headers.get('host');\n if (!hostHeader || !this._allowedHosts.includes(hostHeader)) {\n const error = `Invalid Host header: ${hostHeader}`;\n this.onerror?.(new Error(error));\n return this.createJsonErrorResponse(403, -32_000, error);\n }\n }\n\n // Validate Origin header if allowedOrigins is configured\n if (this._allowedOrigins && this._allowedOrigins.length > 0) {\n const originHeader = req.headers.get('origin');\n if (originHeader && !this._allowedOrigins.includes(originHeader)) {\n const error = `Invalid Origin header: ${originHeader}`;\n this.onerror?.(new Error(error));\n return this.createJsonErrorResponse(403, -32_000, error);\n }\n }\n\n return undefined;\n }\n\n /**\n * Handles an incoming HTTP request, whether `GET`, `POST`, or `DELETE`\n * Returns a `Response` object (Web Standard)\n */\n async handleRequest(req: Request, options?: HandleRequestOptions): Promise {\n if (this._closed) {\n return this.createJsonErrorResponse(404, -32_001, 'Session not found');\n }\n\n // Validate request headers for DNS rebinding protection\n const validationError = this.validateRequestHeaders(req);\n if (validationError) {\n return validationError;\n }\n\n switch (req.method) {\n case 'POST': {\n return this.handlePostRequest(req, options);\n }\n case 'GET': {\n return this.handleGetRequest(req);\n }\n case 'DELETE': {\n return this.handleDeleteRequest(req);\n }\n default: {\n return this.handleUnsupportedRequest();\n }\n }\n }\n\n /**\n * Returns true if the client's protocol version supports empty SSE data in\n * priming events (the fix shipped with protocol version `2025-11-25`).\n *\n * The version is checked for membership in this transport instance's\n * supported protocol versions rather than with an open-ended\n * `>= '2025-11-25'` comparison: the value may come from an `initialize`\n * request body, which (unlike the `MCP-Protocol-Version` header) is not\n * validated against `supportedProtocolVersions` before reaching this\n * check. An unknown future version string must not silently enable\n * behavior reserved for versions this transport actually supports.\n */\n private supportsEmptySSEData(protocolVersion: string): boolean {\n return this._supportedProtocolVersions.includes(protocolVersion) && protocolVersion >= '2025-11-25';\n }\n\n /**\n * Writes a priming event to establish resumption capability.\n * Only sends if `eventStore` is configured (opt-in for resumability) and\n * the client's protocol version supports empty SSE data (a supported\n * version that is >= `2025-11-25`).\n */\n private async writePrimingEvent(\n controller: ReadableStreamDefaultController,\n encoder: InstanceType,\n streamId: string,\n protocolVersion: string\n ): Promise {\n if (!this._eventStore) {\n return;\n }\n\n // Priming events have empty data which older clients cannot handle.\n // Only send priming events to clients whose protocol version includes\n // the fix for handling empty SSE data.\n if (!this.supportsEmptySSEData(protocolVersion)) {\n return;\n }\n\n const primingEventId = await this._eventStore.storeEvent(streamId, {} as JSONRPCMessage);\n\n let primingEvent = `id: ${primingEventId}\\ndata: \\n\\n`;\n if (this._retryInterval !== undefined) {\n primingEvent = `id: ${primingEventId}\\nretry: ${this._retryInterval}\\ndata: \\n\\n`;\n }\n controller.enqueue(encoder.encode(primingEvent));\n }\n\n /**\n * Handles `GET` requests for SSE stream\n */\n private async handleGetRequest(req: Request): Promise {\n // The client MUST include an Accept header, listing text/event-stream as a supported content type.\n const acceptHeader = req.headers.get('accept');\n if (!acceptHeader?.includes('text/event-stream')) {\n this.onerror?.(new Error('Not Acceptable: Client must accept text/event-stream'));\n return this.createJsonErrorResponse(406, -32_000, 'Not Acceptable: Client must accept text/event-stream');\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 const sessionError = this.validateSession(req);\n if (sessionError) {\n return sessionError;\n }\n const protocolError = this.validateProtocolVersion(req);\n if (protocolError) {\n return protocolError;\n }\n\n // Handle resumability: check for Last-Event-ID header\n if (this._eventStore) {\n const lastEventId = req.headers.get('last-event-id');\n if (lastEventId) {\n return this.replayEvents(lastEventId);\n }\n }\n\n // Check if there's already an active standalone SSE stream for this session\n if (this._streamMapping.get(this._standaloneSseStreamId) !== undefined) {\n // Only one GET SSE stream is allowed per session\n this.onerror?.(new Error('Conflict: Only one SSE stream is allowed per session'));\n return this.createJsonErrorResponse(409, -32_000, 'Conflict: Only one SSE stream is allowed per session');\n }\n\n const encoder = new TextEncoder();\n let streamController: ReadableStreamDefaultController;\n // Captured by cancel/cleanup before it is assigned after stream setup.\n // eslint-disable-next-line prefer-const\n let keepAliveTimer: ReturnType | undefined;\n\n // Create a ReadableStream with a controller we can use to push SSE events\n const readable = new ReadableStream({\n start: controller => {\n streamController = controller;\n },\n cancel: () => {\n if (keepAliveTimer !== undefined) clearInterval(keepAliveTimer);\n // Stream was cancelled by client. Only drop the mapping when\n // it still points at THIS controller — a stale cancel must not\n // delete a successor stream registered by a later GET/resume.\n if (this._streamMapping.get(this._standaloneSseStreamId)?.controller === streamController) {\n this._streamMapping.delete(this._standaloneSseStreamId);\n }\n }\n });\n\n const headers: Record = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no'\n };\n\n // After initialization, always include the session ID if we have one\n if (this.sessionId !== undefined) {\n headers['mcp-session-id'] = this.sessionId;\n }\n\n // Store the stream mapping with the controller for pushing data\n this._streamMapping.set(this._standaloneSseStreamId, {\n controller: streamController!,\n encoder,\n cleanup: () => {\n if (keepAliveTimer !== undefined) clearInterval(keepAliveTimer);\n this._streamMapping.delete(this._standaloneSseStreamId);\n try {\n streamController!.close();\n } catch {\n // Controller might already be closed\n }\n }\n });\n\n keepAliveTimer = this.startKeepAlive(streamController!, encoder);\n return new Response(readable, { headers });\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 {\n if (!this._eventStore) {\n this.onerror?.(new Error('Event store not configured'));\n return this.createJsonErrorResponse(400, -32_000, 'Event store not configured');\n }\n\n try {\n // If getStreamIdForEventId is available, use it for conflict checking\n let streamId: string | undefined;\n if (this._eventStore.getStreamIdForEventId) {\n streamId = await this._eventStore.getStreamIdForEventId(lastEventId);\n\n if (!streamId) {\n this.onerror?.(new Error('Invalid event ID format'));\n return this.createJsonErrorResponse(400, -32_000, 'Invalid event ID format');\n }\n\n // Check conflict with the SAME streamId we'll use for mapping\n if (this._streamMapping.get(streamId) !== undefined) {\n this.onerror?.(new Error('Conflict: Stream already has an active connection'));\n return this.createJsonErrorResponse(409, -32_000, 'Conflict: Stream already has an active connection');\n }\n }\n\n const headers: Record = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no'\n };\n\n if (this.sessionId !== undefined) {\n headers['mcp-session-id'] = this.sessionId;\n }\n\n // Create a ReadableStream with controller for SSE\n const encoder = new TextEncoder();\n let streamController: ReadableStreamDefaultController;\n let keepAliveTimer: ReturnType | undefined;\n let cancelled = false;\n // Captured by the cancel closure below before it's assigned (after\n // replayEventsAfter resolves) — must be `let`.\n // eslint-disable-next-line prefer-const\n let replayedStreamId: string | undefined;\n\n const readable = new ReadableStream({\n start: controller => {\n streamController = controller;\n },\n cancel: () => {\n cancelled = true;\n if (keepAliveTimer !== undefined) clearInterval(keepAliveTimer);\n // Stream was cancelled by client — drop the mapping so a\n // subsequent reconnect with the same Last-Event-ID is not\n // refused with 409 by the conflict check above. Only delete\n // when the mapped entry is still THIS closure's controller:\n // a stale cancel from an earlier resume must not delete a\n // successor resumed stream a re-poll has since registered.\n if (replayedStreamId !== undefined && this._streamMapping.get(replayedStreamId)?.controller === streamController) {\n this._streamMapping.delete(replayedStreamId);\n }\n }\n });\n\n // Replay events - returns the streamId for backwards compatibility\n const replayedEventIds = new Set();\n replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, {\n send: async (eventId: string, message: JSONRPCMessage) => {\n replayedEventIds.add(eventId);\n const success = this.writeSSEEvent(streamController!, encoder, message, eventId);\n if (!success) {\n try {\n streamController!.close();\n } catch {\n // Controller might already be closed\n }\n }\n }\n });\n\n if (this._closed || cancelled) {\n try {\n streamController!.close();\n } catch {\n // Controller already closed/cancelled.\n }\n return this.createJsonErrorResponse(404, -32_001, 'Session not found');\n }\n\n this._streamMapping.get(replayedStreamId)?.cleanup();\n this._streamMapping.set(replayedStreamId, {\n controller: streamController!,\n encoder,\n replayedEventIds,\n cleanup: () => {\n if (keepAliveTimer !== undefined) clearInterval(keepAliveTimer);\n this._streamMapping.delete(replayedStreamId!);\n try {\n streamController!.close();\n } catch {\n // Controller might already be closed\n }\n }\n });\n\n // If this is a per-request stream and no in-flight request still\n // targets this streamId, the request was already retired by the\n // clean-return path while disconnected and the replay above just\n // delivered the final response. Per the spec the server SHOULD\n // close the SSE stream after the JSON-RPC response — close and\n // unregister so a later reconnect isn't refused with 409. The\n // standalone GET stream is never request-scoped and stays open.\n if (replayedStreamId !== this._standaloneSseStreamId) {\n const hasInFlightRequest = [...this._requestToStreamMapping.values()].includes(replayedStreamId);\n if (!hasInFlightRequest) {\n this._streamMapping.delete(replayedStreamId);\n try {\n streamController!.close();\n } catch {\n // Controller might already be closed\n }\n }\n }\n\n if (this._streamMapping.get(replayedStreamId)?.controller === streamController!) {\n keepAliveTimer = this.startKeepAlive(streamController!, encoder);\n }\n return new Response(readable, { headers });\n } catch (error) {\n this.onerror?.(error as Error);\n return this.createJsonErrorResponse(500, -32_000, 'Error replaying events');\n }\n }\n\n /**\n * Writes an event to an SSE stream via controller with proper formatting\n */\n private writeSSEEvent(\n controller: ReadableStreamDefaultController,\n encoder: InstanceType,\n message: JSONRPCMessage,\n eventId?: string\n ): boolean {\n try {\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 controller.enqueue(encoder.encode(eventData));\n return true;\n } catch (error) {\n this.onerror?.(error as Error);\n return false;\n }\n }\n\n /**\n * Handles unsupported requests (`PUT`, `PATCH`, etc.)\n */\n private handleUnsupportedRequest(): Response {\n this.onerror?.(new Error('Method not allowed.'));\n return Response.json(\n {\n jsonrpc: '2.0',\n error: {\n code: -32_000,\n message: 'Method not allowed.'\n },\n id: null\n },\n {\n status: 405,\n headers: {\n Allow: 'GET, POST, DELETE',\n 'Content-Type': 'application/json'\n }\n }\n );\n }\n\n /**\n * Handles `POST` requests containing JSON-RPC messages\n */\n private async handlePostRequest(req: Request, options?: HandleRequestOptions): Promise {\n try {\n // Validate the Accept header\n const acceptHeader = req.headers.get('accept');\n // The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.\n // Accept is a comma-separated list, so a substring check is the intended semantics here (unlike Content-Type below).\n // eslint-disable-next-line no-restricted-syntax\n if (!acceptHeader?.includes('application/json') || !acceptHeader.includes('text/event-stream')) {\n this.onerror?.(new Error('Not Acceptable: Client must accept both application/json and text/event-stream'));\n return this.createJsonErrorResponse(\n 406,\n -32_000,\n 'Not Acceptable: Client must accept both application/json and text/event-stream'\n );\n }\n\n // Parsed media type, never a substring match — see\n // isJsonContentType. This check stays here for hand-wired\n // transports; via createMcpHandler the entry's own check answers\n // first.\n const ct = req.headers.get('content-type');\n if (!isJsonContentType(ct)) {\n this.onerror?.(new Error('Unsupported Media Type: Content-Type must be application/json'));\n return this.createJsonErrorResponse(415, -32_000, 'Unsupported Media Type: Content-Type must be application/json');\n }\n\n const request = req;\n\n let rawMessage;\n if (options?.parsedBody === undefined) {\n try {\n rawMessage = await req.json();\n } catch (error) {\n this.onerror?.(error as Error);\n return this.createJsonErrorResponse(400, -32_700, 'Parse error: Invalid JSON');\n }\n } else {\n rawMessage = options.parsedBody;\n }\n\n let messages: JSONRPCMessage[];\n\n // handle batch and single messages\n try {\n messages = Array.isArray(rawMessage)\n ? rawMessage.map(msg => JSONRPCMessageSchema.parse(msg))\n : [JSONRPCMessageSchema.parse(rawMessage)];\n } catch (error) {\n this.onerror?.(error as Error);\n return this.createJsonErrorResponse(400, -32_700, 'Parse error: Invalid JSON-RPC message');\n }\n\n if (this._closed) {\n return this.createJsonErrorResponse(404, -32_001, 'Session not found');\n }\n\n // Check if this is an initialization request\n // https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle/\n // The schema-validated guard (types/guards.ts → types/schemas.ts —\n // NOT a wire/rev* import) gates a transport state mutation: a\n // malformed `initialize` must NOT set `_initialized = true` before\n // the protocol layer rejects it.\n const isInitializationRequest = messages.some(element => isInitializeRequest(element));\n if (isInitializationRequest) {\n // If it's a server with session management and the session ID is already set we should reject the request\n // to avoid re-initialization.\n if (this._initialized && this.sessionId !== undefined) {\n this.onerror?.(new Error('Invalid Request: Server already initialized'));\n return this.createJsonErrorResponse(400, -32_600, 'Invalid Request: Server already initialized');\n }\n if (messages.length > 1) {\n this.onerror?.(new Error('Invalid Request: Only one initialization request is allowed'));\n return this.createJsonErrorResponse(400, -32_600, 'Invalid Request: Only one initialization request is allowed');\n }\n this.sessionId = this.sessionIdGenerator?.();\n this._initialized = true;\n\n // If we have a session ID and an onsessioninitialized handler, call it immediately\n // This is needed in cases where the server needs to keep track of multiple sessions\n if (this.sessionId && this._onsessioninitialized) {\n await Promise.resolve(this._onsessioninitialized(this.sessionId));\n }\n }\n if (!isInitializationRequest) {\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 const sessionError = this.validateSession(req);\n if (sessionError) {\n return sessionError;\n }\n // Mcp-Protocol-Version header is required for all requests after initialization.\n const protocolError = this.validateProtocolVersion(req);\n if (protocolError) {\n return protocolError;\n }\n }\n\n if (this._closed) {\n return this.createJsonErrorResponse(404, -32_001, 'Session not found');\n }\n\n // check if it contains requests\n const hasRequests = messages.some(element => isJSONRPCRequest(element));\n\n if (!hasRequests) {\n // if it only contains notifications or responses, return 202\n for (const message of messages) {\n this.onmessage?.(message, { authInfo: options?.authInfo, request });\n }\n return new Response(null, { status: 202 });\n }\n\n // The default behavior is to use SSE streaming\n // but in some cases server will return JSON responses\n const streamId = crypto.randomUUID();\n\n // Extract protocol version for priming event decision.\n // For initialize requests, get from request params.\n // For other requests, get from header (already validated).\n const initRequest = messages.find(m => isInitializeRequest(m));\n const clientProtocolVersion = initRequest\n ? initRequest.params.protocolVersion\n : (req.headers.get('mcp-protocol-version') ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION);\n\n if (this._enableJsonResponse) {\n // For JSON response mode, return a Promise that resolves when all responses are ready\n return new Promise(resolve => {\n this._streamMapping.set(streamId, {\n resolveJson: resolve,\n cleanup: () => {\n this._streamMapping.delete(streamId);\n }\n });\n\n for (const message of messages) {\n if (isJSONRPCRequest(message)) {\n this._requestToStreamMapping.set(message.id, streamId);\n }\n }\n\n for (const message of messages) {\n this.onmessage?.(message, { authInfo: options?.authInfo, request });\n }\n });\n }\n\n // SSE streaming mode - use ReadableStream with controller for more reliable data pushing\n const encoder = new TextEncoder();\n let streamController: ReadableStreamDefaultController;\n let keepAliveTimer: ReturnType | undefined;\n\n const readable = new ReadableStream({\n start: controller => {\n streamController = controller;\n },\n cancel: () => {\n if (keepAliveTimer !== undefined) clearInterval(keepAliveTimer);\n // Stream was cancelled by client. Only drop the mapping\n // when it still points at THIS controller — a stale cancel\n // (firing after a Last-Event-ID reconnect registered a\n // resumed stream under the same streamId) must not delete\n // the successor.\n if (this._streamMapping.get(streamId)?.controller === streamController) {\n this._streamMapping.delete(streamId);\n }\n }\n });\n\n const headers: Record = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no'\n };\n\n // After initialization, always include the session ID if we have one\n if (this.sessionId !== undefined) {\n headers['mcp-session-id'] = this.sessionId;\n }\n\n // Store the response for this request to send messages back through this connection\n // We need to track by request ID to maintain the connection\n for (const message of messages) {\n if (isJSONRPCRequest(message)) {\n this._streamMapping.set(streamId, {\n controller: streamController!,\n encoder,\n cleanup: () => {\n if (keepAliveTimer !== undefined) clearInterval(keepAliveTimer);\n this._streamMapping.delete(streamId);\n try {\n streamController!.close();\n } catch {\n // Controller might already be closed\n }\n }\n });\n this._requestToStreamMapping.set(message.id, streamId);\n }\n }\n\n // Write priming event if event store is configured (after mapping is set up)\n await this.writePrimingEvent(streamController!, encoder, streamId, clientProtocolVersion);\n\n // handle each message\n for (const message of messages) {\n // Build closeSSEStream callback for requests when eventStore is configured\n // AND client supports resumability (a supported protocol version >= 2025-11-25).\n // Old clients can't resume if the stream is closed early because they\n // didn't receive a priming event with an event ID.\n let closeSSEStream: (() => void) | undefined;\n let closeStandaloneSSEStream: (() => void) | undefined;\n if (isJSONRPCRequest(message) && this._eventStore && this.supportsEmptySSEData(clientProtocolVersion)) {\n closeSSEStream = () => {\n this.closeSSEStream(message.id);\n };\n closeStandaloneSSEStream = () => {\n this.closeStandaloneSSEStream();\n };\n }\n\n this.onmessage?.(message, { authInfo: options?.authInfo, request, closeSSEStream, closeStandaloneSSEStream });\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 if (this._streamMapping.get(streamId)?.controller === streamController!) {\n keepAliveTimer = this.startKeepAlive(streamController!, encoder);\n }\n return new Response(readable, { status: 200, headers });\n } catch (error) {\n // return JSON-RPC formatted error\n this.onerror?.(error as Error);\n return this.createJsonErrorResponse(400, -32_700, 'Parse error', { data: String(error) });\n }\n }\n\n /**\n * Handles `DELETE` requests to terminate sessions\n */\n private async handleDeleteRequest(req: Request): Promise {\n const sessionError = this.validateSession(req);\n if (sessionError) {\n return sessionError;\n }\n const protocolError = this.validateProtocolVersion(req);\n if (protocolError) {\n return protocolError;\n }\n\n try {\n await Promise.resolve(this._onsessionclosed?.(this.sessionId!));\n return new Response(null, { status: 200 });\n } finally {\n await this.close();\n }\n }\n\n /**\n * Validates session ID for non-initialization requests.\n * Returns `Response` error if invalid, `undefined` otherwise\n */\n private validateSession(req: Request): Response | undefined {\n if (this.sessionIdGenerator === undefined) {\n // If the sessionIdGenerator ID is not set, the session management is disabled\n // and we don't need to validate the session ID\n return undefined;\n }\n if (!this._initialized) {\n // If the server has not been initialized yet, reject all requests\n this.onerror?.(new Error('Bad Request: Server not initialized'));\n return this.createJsonErrorResponse(400, -32_000, 'Bad Request: Server not initialized');\n }\n\n const sessionId = req.headers.get('mcp-session-id');\n\n if (!sessionId) {\n // Non-initialization requests without a session ID should return 400 Bad Request\n this.onerror?.(new Error('Bad Request: Mcp-Session-Id header is required'));\n return this.createJsonErrorResponse(400, -32_000, 'Bad Request: Mcp-Session-Id header is required');\n }\n\n if (sessionId !== this.sessionId) {\n // Reject requests with invalid session ID with 404 Not Found\n this.onerror?.(new Error('Session not found'));\n return this.createJsonErrorResponse(404, -32_001, 'Session not found');\n }\n\n return undefined;\n }\n\n /**\n * Validates the `MCP-Protocol-Version` header on incoming requests.\n *\n * For initialization: Version negotiation handles unknown versions gracefully\n * (server responds with its supported version).\n *\n * For subsequent requests with `MCP-Protocol-Version` header:\n * - Accept if in supported list\n * - 400 if unsupported\n *\n * For HTTP requests without the `MCP-Protocol-Version` header:\n * - Accept and default to the version negotiated at initialization\n */\n private validateProtocolVersion(req: Request): Response | undefined {\n const protocolVersion = req.headers.get('mcp-protocol-version');\n\n if (protocolVersion !== null && !this._supportedProtocolVersions.includes(protocolVersion)) {\n const error = `Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${this._supportedProtocolVersions.join(', ')})`;\n this.onerror?.(new Error(error));\n return this.createJsonErrorResponse(400, -32_000, error);\n }\n return undefined;\n }\n\n async close(): Promise {\n if (this._closed) {\n return;\n }\n this._closed = true;\n\n // Close all SSE connections\n for (const { cleanup } of this._streamMapping.values()) {\n cleanup();\n }\n this._streamMapping.clear();\n\n // Clear any pending responses\n this._requestResponseMap.clear();\n this.onclose?.();\n }\n\n /**\n * Close an SSE stream for a specific request, triggering client reconnection.\n * Use this to implement polling behavior during long-running operations -\n * client will reconnect after the retry interval specified in the priming event.\n */\n closeSSEStream(requestId: RequestId): void {\n const streamId = this._requestToStreamMapping.get(requestId);\n if (!streamId) return;\n\n const stream = this._streamMapping.get(streamId);\n if (stream) {\n stream.cleanup();\n }\n }\n\n /**\n * Close the standalone `GET` SSE stream, triggering client reconnection.\n * Use this to implement polling behavior for server-initiated notifications.\n */\n closeStandaloneSSEStream(): void {\n const stream = this._streamMapping.get(this._standaloneSseStreamId);\n if (stream) {\n stream.cleanup();\n }\n }\n\n async send(message: JSONRPCMessage, options?: { relatedRequestId?: RequestId }): Promise {\n let requestId = options?.relatedRequestId;\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n // If the message is a response, use the request ID from the message\n requestId = message.id;\n }\n\n // Check if this message should be sent on the standalone SSE stream (no request ID)\n // Ignore notifications from tools (which have relatedRequestId set)\n // Those will be sent via dedicated response SSE streams\n if (requestId === undefined) {\n // For standalone SSE streams, we can only send requests and notifications\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n throw new Error('Cannot send a response on a standalone SSE stream unless resuming a previous client request');\n }\n\n // Generate and store event ID if event store is provided\n // Store even if stream is disconnected so events can be replayed on reconnect\n let eventId: string | undefined;\n if (this._eventStore) {\n // Stores the event and gets the generated event ID\n eventId = await this._eventStore.storeEvent(this._standaloneSseStreamId, message);\n }\n\n const standaloneSse = this._streamMapping.get(this._standaloneSseStreamId);\n if (standaloneSse === undefined) {\n // Stream is disconnected - event is stored for replay, nothing more to do\n return;\n }\n\n // Send the message to the standalone SSE stream — unless the\n // resumed stream's replay already delivered this exact eventId\n // (identity dedup; mirrors the per-request path below).\n if (\n standaloneSse.controller &&\n standaloneSse.encoder &&\n (eventId === undefined || !standaloneSse.replayedEventIds?.has(eventId))\n ) {\n this.writeSSEEvent(standaloneSse.controller, standaloneSse.encoder, message, eventId);\n }\n return;\n }\n\n // Get the response for this request\n const streamId = this._requestToStreamMapping.get(requestId);\n if (!streamId) {\n throw new Error(`No connection established for request ID: ${String(requestId)}`);\n }\n\n let stream = this._streamMapping.get(streamId);\n\n if (!this._enableJsonResponse) {\n // Store FIRST so request-related events emitted while the per-request\n // stream is disconnected (e.g. after `closeSSE()` or a transient\n // client drop) are replayed on Last-Event-ID reconnect — same\n // store-first semantics as the standalone path above. Storage is\n // keyed on request-in-flight (`_requestToStreamMapping` resolved\n // `streamId` above), not on whether a live SSE writer currently\n // exists: `_streamMapping` tracks the delivery target only. Per\n // 2025-11-25 transports.mdx, disconnection SHOULD NOT be\n // interpreted as the client cancelling its request.\n let eventId: string | undefined;\n if (this._eventStore) {\n eventId = await this._eventStore.storeEvent(streamId, message);\n // Re-read after the await: a Last-Event-ID reconnect during\n // storeEvent() may have registered a resumed stream under this\n // streamId (mirrors the standalone path's post-await read).\n stream = this._streamMapping.get(streamId);\n }\n // Write the event to the response stream — unless the resumed\n // stream's replay already delivered this exact eventId (the store\n // committed before replay scanned, so replay wrote it; identity\n // dedup only, no ordering assumption).\n if (stream?.controller && stream?.encoder && (eventId === undefined || !stream.replayedEventIds?.has(eventId))) {\n this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);\n }\n }\n\n if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n this._requestResponseMap.set(requestId, message);\n const relatedIds = [...this._requestToStreamMapping.entries()].filter(([_, sid]) => sid === streamId).map(([id]) => id);\n\n // Check if we have responses for all requests using this connection\n const allResponsesReady = relatedIds.every(id => this._requestResponseMap.has(id));\n\n if (allResponsesReady) {\n if (!stream) {\n if (this._enableJsonResponse) {\n // JSON-mode requires a resolveJson sink; with no stream entry the\n // response is undeliverable.\n throw new Error(`No connection established for request ID: ${String(requestId)}`);\n }\n if (!this._eventStore) {\n // SSE-mode with no live writer and no event store: the\n // response is undeliverable AND not stored. Surface via\n // onerror so the drop is observable (matching pre-PR\n // behaviour), then run the bookkeeping cleanup so the\n // request id is retired.\n this.onerror?.(\n new Error(\n `Response for request ID ${String(requestId)} is undeliverable: per-request stream is disconnected and no eventStore is configured`\n )\n );\n for (const id of relatedIds) {\n this._requestResponseMap.delete(id);\n this._requestToStreamMapping.delete(id);\n }\n return;\n }\n // SSE-mode with no live writer and an event store configured:\n // the response was stored above for replay on Last-Event-ID\n // reconnect. Return cleanly after running the bookkeeping\n // cleanup so the request id is retired.\n for (const id of relatedIds) {\n this._requestResponseMap.delete(id);\n this._requestToStreamMapping.delete(id);\n }\n return;\n }\n if (this._enableJsonResponse && stream.resolveJson) {\n // All responses ready, send as JSON\n const headers: Record = {\n 'Content-Type': 'application/json'\n };\n if (this.sessionId !== undefined) {\n headers['mcp-session-id'] = this.sessionId;\n }\n\n const responses = relatedIds.map(id => this._requestResponseMap.get(id)!);\n\n if (responses.length === 1) {\n stream.resolveJson(Response.json(responses[0], { status: 200, headers }));\n } else {\n stream.resolveJson(Response.json(responses, { status: 200, headers }));\n }\n stream.cleanup();\n } else {\n // End the SSE stream\n stream.cleanup();\n }\n // Clean up\n for (const id of relatedIds) {\n this._requestResponseMap.delete(id);\n this._requestToStreamMapping.delete(id);\n }\n }\n }\n }\n}\n","/**\n * `createMcpHandler` — the HTTP entry point for serving the 2026-07-28 protocol\n * revision, with old-school stateless 2025-era serving as the default fallback.\n *\n * The entry classifies every inbound HTTP request exactly once (body-primary,\n * via {@linkcode classifyInboundRequest}) and routes it:\n *\n * - Requests carrying the per-request `_meta` envelope are served on the modern\n * path: a fresh server instance from the consumer's factory, marked as\n * serving the claimed revision, connected to a single-exchange per-request\n * transport.\n * - Requests without an envelope claim (including `initialize`, GET/DELETE\n * session operations, and 2025-era notification POSTs) are legacy traffic.\n * By default they are served per request through the stateless idiom from\n * the same factory (`legacy: 'stateless'`); with `legacy: 'reject'` the\n * endpoint is modern-only strict and answers the documented rejection cells\n * instead — there is no 2025 serving in that mode.\n *\n * There is no handler-valued `legacy` option: an existing legacy deployment\n * (for example a sessionful streamable HTTP wiring) keeps serving 2025 traffic\n * by routing in user land with {@linkcode isLegacyRequest} — the entry's own\n * classification step, exported as a predicate — in front of a strict\n * (`legacy: 'reject'`) handler.\n *\n * The entry performs no Origin/Host validation (mount the origin/host\n * validation middleware in front of it) and no token verification — `authInfo`\n * is pass-through from the caller and is never derived from request headers.\n */\nimport type {\n AuthInfo,\n ClientCapabilities,\n Implementation,\n InboundClassificationOutcome,\n InboundLadderRejection,\n InboundLegacyRoute,\n InboundModernRoute,\n RequestId\n} from '@modelcontextprotocol/core-internal';\nimport {\n classifyInboundRequest,\n CLIENT_CAPABILITIES_META_KEY,\n CLIENT_INFO_META_KEY,\n httpStatusForErrorCode,\n isJsonContentType,\n mediaTypeEssence,\n missingClientCapabilities,\n MissingRequiredClientCapabilityError,\n modernOnlyStrictRejection,\n requestMetaOf,\n requiredClientCapabilitiesForRequest,\n scanXMcpHeaderDeclarations,\n SdkError,\n SdkErrorCode,\n setNegotiatedProtocolVersion,\n SUPPORTED_MODERN_PROTOCOL_VERSIONS,\n UnsupportedProtocolVersionError,\n validateMcpParamHeaders,\n validateStandardRequestHeaders\n} from '@modelcontextprotocol/core-internal';\n\nimport { invoke } from './invoke';\nimport { createListenRouter, DEFAULT_MAX_SUBSCRIPTIONS } from './listenRouter';\nimport { McpServer } from './mcp';\nimport type { PerRequestResponseMode } from './perRequestTransport';\nimport type { Server } from './server';\nimport { installModernOnlyHandlers, seedClientIdentityFromEnvelope, serverIdentityOf } from './server';\nimport type { ServerEventBus, ServerNotifier } from './serverEventBus';\nimport { createServerNotifier, InMemoryServerEventBus } from './serverEventBus';\nimport { DEFAULT_SSE_KEEP_ALIVE_MS } from './sseKeepAlive';\nimport { WebStandardStreamableHTTPServerTransport } from './streamableHttp';\n\n/* ------------------------------------------------------------------------ *\n * Factory and handler types\n * ------------------------------------------------------------------------ */\n\n/**\n * Construction context handed to an {@linkcode McpServerFactory}.\n *\n * Both serving entries call the factory with this context whenever they need\n * a fresh instance: {@linkcode createMcpHandler} once per HTTP request, and\n * `serveStdio` (from `@modelcontextprotocol/server/stdio`) once per\n * connection — plus once for a `server/discover` probe instance that is\n * discarded again if the client falls back to `initialize`.\n *\n * Zero-argument factories remain assignable unchanged; the context exists for\n * factories that vary by principal or era (for example multi-tenant servers\n * keyed off `authInfo`, or a factory that registers extra surface only for one\n * era).\n */\nexport interface McpRequestContext {\n /**\n * The protocol era the constructed instance will serve: `modern` for\n * 2026-07-28 (per-request envelope) traffic, `legacy` for 2025-era\n * traffic. Under {@linkcode createMcpHandler} a `legacy` instance serves\n * one request through the stateless legacy fallback (the default —\n * `legacy: 'reject'` endpoints are strict and never construct one); under\n * `serveStdio` it serves a connection that opened with the 2025 handshake\n * and stays pinned to that era for its lifetime.\n */\n era: 'legacy' | 'modern';\n /**\n * Validated authentication information passed by the caller of the\n * handler face (pass-through; HTTP only — `serveStdio` never sets it).\n */\n authInfo?: AuthInfo;\n /** The original HTTP request being served, when available (HTTP only — `serveStdio` never sets it). */\n requestInfo?: Request;\n}\n\n/**\n * A factory producing a fresh {@linkcode McpServer} (or low-level\n * {@linkcode Server}) instance for one serving unit: one HTTP request under\n * {@linkcode createMcpHandler}, or one connection (or one discarded\n * `server/discover` probe) under `serveStdio`. The same factory backs every\n * era either entry serves — define your tools, resources and prompts once and\n * serve them to both eras.\n */\nexport type McpServerFactory = (ctx: McpRequestContext) => McpServer | Server | Promise;\n\n/** Caller-provided per-request inputs for {@linkcode McpHttpHandler.fetch} and fetch-shaped legacy handlers ({@linkcode LegacyHttpHandler}). */\nexport interface McpHandlerRequestOptions {\n /**\n * Validated authentication information for the request. Strictly\n * pass-through: the handler never populates this from request headers and\n * performs no token verification of its own.\n */\n authInfo?: AuthInfo;\n /** A pre-parsed JSON request body (e.g. `req.body` from `express.json()`). */\n parsedBody?: unknown;\n}\n\n/**\n * A fetch-shaped handler serving 2025-era traffic: the shape produced by\n * {@linkcode legacyStatelessFallback}, and the shape a hand-wired composition\n * routes legacy requests to (see {@linkcode isLegacyRequest}). It is not a\n * `legacy` option value — the entry's own legacy serving is selected by the\n * `'stateless' | 'reject'` posture only.\n */\nexport type LegacyHttpHandler = (request: Request, options?: McpHandlerRequestOptions) => Promise;\n\n/** Options for {@linkcode createMcpHandler}. */\nexport interface CreateMcpHandlerOptions {\n /**\n * How 2025-era (non-envelope) traffic is served:\n *\n * - `'stateless'` (the default, also when the option is omitted) —\n * old-school stateless serving: each legacy request is answered by a\n * fresh instance from the same factory over a streamable HTTP transport\n * constructed with only `sessionIdGenerator: undefined` (the established\n * stateless idiom). Because serving is per-request and stateless, GET and\n * DELETE (2025 session operations) are answered with `405` /\n * `Method not allowed.`.\n * - `'reject'` — modern-only strict: legacy-classified requests are\n * rejected with the unsupported-protocol-version error naming the\n * endpoint's supported revisions (legacy-classified notifications are\n * acknowledged with `202` and dropped). **There is no 2025 serving in\n * this mode.**\n *\n * There is no handler-valued option: to keep an existing legacy deployment\n * (for example a sessionful streamable HTTP wiring) serving 2025 traffic\n * next to this entry, route in user land with {@linkcode isLegacyRequest}\n * in front of a `legacy: 'reject'` handler — see that predicate's\n * documentation for the pattern.\n */\n legacy?: 'stateless' | 'reject';\n /** Callback for out-of-band errors and rejected requests (reporting only; it never alters the response). */\n onerror?: (error: Error) => void;\n /**\n * Response shaping for modern (2026-07-28) request exchanges:\n *\n * - `'auto'` (default) — a single JSON body unless the handler emits a\n * related message before its result, in which case the response upgrades\n * to an SSE stream.\n * - `'sse'` — always stream.\n * - `'json'` — never stream. **Mid-call notifications (progress, logging,\n * any related message emitted before the result) are dropped** — only the\n * terminal result is delivered. Listen-class subscription streams are\n * always served over SSE regardless of this setting.\n */\n responseMode?: PerRequestResponseMode;\n /**\n * The change-event bus `subscriptions/listen` streams subscribe to.\n *\n * When omitted, an in-process {@link InMemoryServerEventBus} is created\n * and the returned handler's `notify` sugar publishes onto it.\n * Multi-process deployments supply their own implementation over their\n * pub/sub backend; the same instance can be shared across handlers.\n */\n bus?: ServerEventBus;\n /**\n * Reject a new `subscriptions/listen` with `-32603` 'Subscription limit\n * reached' (in-band, HTTP 200, before the ack) when this many subscription\n * streams are already open on this handler.\n * @default 1024\n */\n maxSubscriptions?: number;\n /**\n * SSE comment-frame keepalive interval for every SSE stream this handler\n * serves. In modern `auto` mode it starts after SSE upgrade. Set to `0` to disable.\n * @default 15000\n */\n keepAliveMs?: number;\n}\n\n/**\n * The handler returned by {@linkcode createMcpHandler}: a web-standard\n * `{ fetch, close, notify, bus }` object — the shape Workers/Bun/Deno expect\n * from `export default`. `fetch` is an arrow-assigned bound property: it can be\n * detached and passed around (`const { fetch } = handler`) without losing its\n * binding.\n *\n * Node frameworks (Express, Fastify, plain `node:http`) wrap the handler once\n * with `toNodeHandler(handler)` from `@modelcontextprotocol/node`.\n */\nexport interface McpHttpHandler {\n /** Web-standard face: serve one HTTP request and resolve with the response. */\n fetch: (request: Request, options?: McpHandlerRequestOptions) => Promise;\n /**\n * Tears down the modern leg: aborts in-flight modern exchanges and closes\n * their per-request instances. Legacy serving is unaffected — the\n * stateless fallback is per-request by construction and holds nothing\n * between exchanges.\n */\n close: () => Promise;\n /**\n * Typed publish-side facade over the handler's `subscriptions/listen` bus:\n * each method publishes the corresponding change event to every open\n * subscription stream that opted in to that notification type.\n *\n * Safe to call when no subscription is open (no-op).\n */\n notify: ServerNotifier;\n /**\n * The change-event bus this handler's `subscriptions/listen` streams\n * subscribe to (the supplied `bus` option, or the auto-created in-process\n * default).\n */\n bus: ServerEventBus;\n}\n\n/* ------------------------------------------------------------------------ *\n * Shared response helpers\n * ------------------------------------------------------------------------ */\n\n/**\n * The JSON-RPC id to echo on an entry-built error response: the body's `id`\n * when the body is a single JSON-RPC request whose id is a string or number,\n * `null` otherwise. Error responses must carry the id of the request they\n * correspond to whenever it could be read; `null` is reserved for the cases\n * where no single request id is determinable — unparseable bodies, body-less\n * methods, notifications, posted responses and batch arrays.\n */\nfunction echoableRequestId(body: unknown): RequestId | null {\n if (body === null || typeof body !== 'object' || Array.isArray(body)) {\n return null;\n }\n const { method, id } = body as { method?: unknown; id?: unknown };\n if (typeof method !== 'string') {\n return null;\n }\n return typeof id === 'string' || typeof id === 'number' ? id : null;\n}\n\nfunction jsonRpcErrorResponse(httpStatus: number, code: number, message: string, data?: unknown, id: RequestId | null = null): Response {\n return Response.json(\n {\n jsonrpc: '2.0',\n error: { code, message, ...(data !== undefined && { data }) },\n id\n },\n { status: httpStatus }\n );\n}\n\nfunction rejectionResponse(rejection: InboundLadderRejection, id: RequestId | null = null): Response {\n return jsonRpcErrorResponse(rejection.httpStatus, rejection.code, rejection.message, rejection.data, id);\n}\n\nfunction toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\nfunction internalServerErrorResponse(id: RequestId | null = null): Response {\n return jsonRpcErrorResponse(500, -32_603, 'Internal server error', undefined, id);\n}\n\n/* ------------------------------------------------------------------------ *\n * The default legacy fallback\n * ------------------------------------------------------------------------ */\n\n/**\n * The entry's default legacy serving (`legacy: 'stateless'`): per-request\n * stateless serving of 2025-era traffic using the same factory as the modern\n * path. Exported as a standalone building block for hand-wired compositions\n * (for example mounting legacy stateless serving on its own route next to a\n * strict modern endpoint).\n *\n * Each POST is served by a fresh instance from the factory connected to a\n * fresh streamable HTTP transport constructed with only\n * `sessionIdGenerator: undefined` — the established stateless idiom, unchanged.\n * Because serving is per-request and stateless, GET and DELETE (2025 session\n * operations) are answered with `405` / `Method not allowed.`, exactly like the\n * canonical stateless example.\n *\n * The optional `onerror` callback receives factory and serving failures on\n * this leg (reporting only — the response stays the 500 internal-error body).\n * The entry passes its own `onerror` here when expanding the default, so\n * legacy-leg failures are never silently swallowed.\n */\nfunction createLegacyStatelessFallback(\n factory: McpServerFactory,\n onerror?: (error: Error) => void,\n keepAliveMs?: number\n): LegacyHttpHandler {\n return async (request, options) => {\n if (request.method.toUpperCase() !== 'POST') {\n return jsonRpcErrorResponse(405, -32_000, 'Method not allowed.');\n }\n try {\n const product = await factory({\n era: 'legacy',\n ...(options?.authInfo !== undefined && { authInfo: options.authInfo }),\n requestInfo: request\n });\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n ...(keepAliveMs !== undefined && { keepAliveMs })\n });\n await product.connect(transport);\n\n const teardown = () => {\n void transport.close().catch(() => {});\n void product.close().catch(() => {});\n };\n // Tear the per-request pair down when the client goes away before\n // the exchange completes.\n request.signal?.addEventListener('abort', teardown, { once: true });\n\n const response = await transport.handleRequest(request, {\n ...(options?.authInfo !== undefined && { authInfo: options.authInfo }),\n ...(options?.parsedBody !== undefined && { parsedBody: options.parsedBody })\n });\n if (response.body === null || mediaTypeEssence(response.headers.get('content-type')) !== 'text/event-stream') {\n // Non-streaming exchange (a buffered JSON body or a body-less\n // ack): the response is complete, release the pair now.\n teardown();\n return response;\n }\n // Streaming exchange: the legacy transport answers request-bearing\n // POSTs over SSE, so the exchange is only over once the stream has\n // been fully delivered. Wrap the body so the pair is torn down on\n // completion, on a producer error, or when the consumer abandons\n // the stream — the fetch-world analog of the canonical stateless\n // example's close-on-response-end.\n const reader = response.body.getReader();\n let toreDown = false;\n const completeExchange = () => {\n if (!toreDown) {\n toreDown = true;\n teardown();\n }\n };\n const monitoredBody = new ReadableStream({\n pull: async controller => {\n try {\n const { done, value } = await reader.read();\n if (done) {\n completeExchange();\n controller.close();\n return;\n }\n if (value !== undefined) {\n controller.enqueue(value);\n }\n } catch (error) {\n completeExchange();\n controller.error(error);\n }\n },\n cancel: reason => {\n completeExchange();\n return reader.cancel(reason).catch(() => {});\n }\n });\n return new Response(monitoredBody, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers\n });\n } catch (error) {\n try {\n onerror?.(toError(error));\n } catch {\n // Reporting must never alter the response.\n }\n return internalServerErrorResponse(echoableRequestId(options?.parsedBody));\n }\n };\n}\n\nexport function legacyStatelessFallback(factory: McpServerFactory, onerror?: (error: Error) => void): LegacyHttpHandler {\n return createLegacyStatelessFallback(factory, onerror);\n}\n\n/* ------------------------------------------------------------------------ *\n * The entry's classification step (shared with isLegacyRequest)\n * ------------------------------------------------------------------------ */\n\n/** The outcome of the entry's classification step for one inbound HTTP request. */\ntype EntryClassification =\n /** The body bytes could not be read at all (a failing stream, not malformed JSON). */\n | { step: 'unreadable-body' }\n /** A POST with an empty or non-JSON body: nothing to classify, so there is no envelope claim. */\n | { step: 'no-json-body'; forwardRequest: Request }\n /** A classifiable request, with the classifier's routing outcome. */\n | { step: 'classified'; outcome: InboundClassificationOutcome; body: unknown; parsedBody: unknown; forwardRequest: Request };\n\n/**\n * The entry's classification step: read the request body exactly once (unless\n * a pre-parsed body is supplied) and classify the request with\n * {@linkcode classifyInboundRequest}. This is the single code path behind both\n * {@linkcode createMcpHandler}'s routing and the exported\n * {@linkcode isLegacyRequest} predicate, so the two can never disagree.\n *\n * Pass `needsForward: false` when the caller never reads `forwardRequest` —\n * the body-preserving clone is then skipped and `forwardRequest` is the\n * (consumed) input request.\n */\nasync function classifyEntryRequest(request: Request, providedParsedBody?: unknown, needsForward = true): Promise {\n const httpMethod = request.method.toUpperCase();\n\n let body: unknown;\n let parsedBody = providedParsedBody;\n let forwardRequest = request;\n let unparseable = false;\n\n if (httpMethod === 'POST') {\n if (parsedBody === undefined) {\n // Read the body exactly once for classification, keeping an unread\n // copy of the original bytes for the legacy leg (web-standard\n // request bodies are single-use) when the caller needs it.\n if (needsForward) {\n forwardRequest = request.clone();\n }\n let bodyText: string;\n try {\n bodyText = await request.text();\n } catch {\n return { step: 'unreadable-body' };\n }\n try {\n body = bodyText.length === 0 ? undefined : JSON.parse(bodyText);\n } catch {\n unparseable = true;\n }\n if (!unparseable && body !== undefined) {\n parsedBody = body;\n }\n } else {\n body = parsedBody;\n }\n\n if (unparseable || body === undefined) {\n return { step: 'no-json-body', forwardRequest };\n }\n }\n\n const outcome = classifyInboundRequest({\n httpMethod,\n protocolVersionHeader: request.headers.get('mcp-protocol-version') ?? undefined,\n mcpMethodHeader: request.headers.get('mcp-method') ?? undefined,\n mcpNameHeader: request.headers.get('mcp-name') ?? undefined,\n ...(body !== undefined && { body })\n });\n return { step: 'classified', outcome, body, parsedBody, forwardRequest };\n}\n\n/**\n * Whether {@linkcode createMcpHandler} would route this request to its legacy\n * (2025-era) serving rather than the modern (2026-07-28) path.\n *\n * Call it with just the request: `await isLegacyRequest(request)`. For a\n * `POST` the body is read from an internal clone, so the request you pass\n * stays fully readable for whichever handler you route it to — no second\n * argument is needed. (In a Node `(req, res)` handler, build that `Request`\n * with `toWebRequest(req)` from `@modelcontextprotocol/node`; behind a body\n * parser, which has already drained the Node stream, build it as\n * `toWebRequest(req, req.body)` so the bytes come from the parsed body —\n * either way the predicate still takes just the request.) The optional\n * `parsedBody` is a perf escape hatch for a body you already hold parsed:\n * pass it and the predicate classifies from the value directly, reading and\n * cloning nothing. It is needed, not just faster, when the request's own\n * body was already read — the internal clone is then impossible (cloning a\n * used body throws a `TypeError`), so such a single-argument call rejects\n * instead of guessing.\n *\n * This is the entry's own classification step exported as a predicate — it\n * runs exactly the code `createMcpHandler` runs to make the routing decision,\n * not a re-implementation — so a hand-wired composition that branches on it\n * can never disagree with the entry. It is classification only: hand-wired\n * compositions must validate Content-Type themselves (415 for POSTs whose\n * media type is not `application/json`, via {@linkcode isJsonContentType})\n * before dispatching either leg — routing the legacy leg into the SDK\n * transports gets their built-in check, but a custom modern leg has none. Use it to keep an existing legacy\n * deployment (for example a sessionful streamable HTTP wiring) serving 2025\n * traffic next to a strict modern endpoint, now that the entry has no\n * handler-valued `legacy` option:\n *\n * ```ts\n * import { createMcpHandler, isLegacyRequest } from '@modelcontextprotocol/server';\n *\n * const modern = createMcpHandler(factory, { legacy: 'reject' });\n *\n * export default {\n * async fetch(request: Request): Promise {\n * if (await isLegacyRequest(request)) {\n * // e.g. an existing sessionful WebStandardStreamableHTTPServerTransport wiring\n * return myExistingLegacyHandler(request);\n * }\n * return modern.fetch(request);\n * }\n * };\n * ```\n *\n * Semantics (identical to the entry's routing):\n *\n * - Returns `true` only for requests with no per-request `_meta` envelope\n * claim: claim-less POSTs (including the `initialize` handshake and 2025-era\n * notification POSTs without a modern protocol-version header), body-less\n * GET/DELETE session operations, all-legacy JSON-RPC batch arrays, posted\n * JSON-RPC responses, and POSTs whose body is empty or not valid JSON.\n * - Returns `false` for everything the modern path answers, including its\n * validation-ladder rejections: a request carrying the envelope claim (even\n * one naming a revision the endpoint does not serve — the modern path\n * answers it with the unsupported-protocol-version error), a malformed\n * envelope behind a present claim (answered `-32602`), a request whose\n * `MCP-Protocol-Version` header names a modern revision but that lacks the\n * envelope (`-32602`), and header/body mismatches (`-32020`). Consumers\n * routing on the predicate must send `false` traffic to the modern handler,\n * never to a legacy handler — the modern path owns those error answers.\n * - `server/discover` probes sent by negotiating clients always carry the\n * envelope claim, so they are never legacy; a hand-built claim-less POST to\n * a method named `server/discover` has no claim and classifies legacy,\n * exactly as the entry itself routes it.\n */\nexport async function isLegacyRequest(request: Request, parsedBody?: unknown): Promise {\n // Classify a clone so the caller's request body stays readable; with a\n // pre-parsed body (or a body-less method) nothing is read and no clone is\n // needed. The predicate never reads forwardRequest, so the classification\n // step's own forwarding clone is skipped.\n const probe = parsedBody === undefined && request.method.toUpperCase() === 'POST' ? request.clone() : request;\n const classified = await classifyEntryRequest(probe, parsedBody, false);\n return classified.step === 'no-json-body' || (classified.step === 'classified' && classified.outcome.kind === 'legacy');\n}\n\n/* ------------------------------------------------------------------------ *\n * The entry\n * ------------------------------------------------------------------------ */\n\n/**\n * Creates an HTTP handler that serves the 2026-07-28 protocol revision from a\n * per-request server factory and, by default, falls back to old-school\n * stateless serving for 2025-era traffic. Pass `legacy: 'reject'` for a\n * modern-only strict endpoint.\n *\n * Mounting: `handler.fetch` is the web-standard face (Cloudflare Workers,\n * Deno, Bun, Hono's `c.req.raw`); for Express/Fastify/plain `node:http`, wrap\n * the handler once with `toNodeHandler(handler)` from\n * `@modelcontextprotocol/node`. When mounting bare on a fetch-native runtime,\n * put Origin/Host validation in front of the handler — the entry itself is\n * deliberately validation-free:\n *\n * ```ts\n * import { hostHeaderValidationResponse, originValidationResponse, localhostAllowedHostnames, localhostAllowedOrigins } from '@modelcontextprotocol/server';\n *\n * export default {\n * async fetch(request: Request): Promise {\n * const rejected =\n * hostHeaderValidationResponse(request, localhostAllowedHostnames()) ??\n * originValidationResponse(request, localhostAllowedOrigins());\n * return rejected ?? handler.fetch(request);\n * }\n * };\n * ```\n *\n * Use ONE factory for both legs: the same tools/resources/prompts definition\n * backs the modern path and the stateless legacy fallback, so the two eras can\n * never drift apart. To keep an existing legacy deployment (for example a\n * sessionful streamable HTTP wiring) serving 2025 traffic instead of the\n * stateless fallback, route in user land with {@linkcode isLegacyRequest} in\n * front of a strict handler — see that predicate's documentation for the\n * pattern. Power users composing transport-neutral routing can also use the\n * exported building blocks directly: {@linkcode classifyInboundRequest} for\n * the era decision and `PerRequestHTTPServerTransport` for single-exchange\n * serving — such compositions must reject POSTs whose Content-Type media type\n * is not `application/json` (415) before parsing the body, using\n * {@linkcode isJsonContentType}; neither building block performs this\n * validation itself.\n *\n * The entry performs no token verification: `authInfo` given to `fetch` is\n * passed through to handlers and the factory as-is and is never derived from\n * request headers.\n */\nexport function createMcpHandler(factory: McpServerFactory, options: CreateMcpHandlerOptions = {}): McpHttpHandler {\n const { legacy, onerror, responseMode } = options;\n\n // Construction-time guard for JavaScript callers passing a handler as the\n // legacy value: the option only selects a posture ('stateless' | 'reject').\n // Failing loudly here beats silently treating the handler as the default.\n if (typeof legacy === 'function') {\n throw new TypeError(\n \"The 'legacy' option only accepts 'stateless' or 'reject', not a handler function. To serve 2025-era traffic with your own \" +\n \"handler, route in user land with the exported isLegacyRequest(request) predicate in front of a strict (legacy: 'reject') handler.\"\n );\n }\n\n /** Modern per-request instances with an exchange still in flight (close() tears these down). */\n const inflight = new Set();\n let closed = false;\n\n const reportError = (error: Error) => {\n try {\n onerror?.(error);\n } catch {\n // Reporting must never alter the response.\n }\n };\n\n const bus: ServerEventBus = options.bus ?? new InMemoryServerEventBus(reportError);\n const notify = createServerNotifier(bus);\n const listenRouter = createListenRouter({\n bus,\n maxSubscriptions: options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS,\n keepAliveMs: options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS,\n onerror: reportError\n });\n if (responseMode === 'json') {\n // eslint-disable-next-line no-console\n console.warn(\n \"responseMode: 'json' drops mid-call notifications. subscriptions/listen streams are always served over SSE regardless; \" +\n 'other notifications emitted before a result are dropped.'\n );\n }\n\n // The default posture is the stateless fallback; 'reject' is the only way\n // to turn legacy serving off (modern-only strict).\n const legacyHandler: LegacyHttpHandler | undefined =\n legacy === 'reject' ? undefined : createLegacyStatelessFallback(factory, reportError, options.keepAliveMs);\n\n async function serveModern(route: InboundModernRoute, request: Request, authInfo: AuthInfo | undefined): Promise {\n const claimedRevision = route.classification.revision;\n if (claimedRevision === undefined || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedRevision)) {\n // The claim names a revision this endpoint does not serve (an\n // unknown future revision, or a 2025-era revision delivered via the\n // envelope mechanism).\n const error = new UnsupportedProtocolVersionError({\n supported: [...SUPPORTED_MODERN_PROTOCOL_VERSIONS],\n requested: claimedRevision ?? 'unknown'\n });\n reportError(error);\n return jsonRpcErrorResponse(400, error.code, error.message, error.data, echoableRequestId(route.message));\n }\n\n // SEP-2243 standard-header presence and `Mcp-Name` cross-check\n // (`standard-header-validation` rung; the `MCP-Protocol-Version` and\n // `Mcp-Method` *mismatch* cells are already answered inside\n // `classifyInboundRequest` on the edge `era-classification` rung).\n // Evaluated after the supported-revision\n // gate so an envelope naming a revision this endpoint does not serve\n // is still answered with `-32022` (the supported list is the more\n // useful answer to a client speaking the wrong revision); evaluated\n // before the capability gate, the factory call, and the\n // `Mcp-Param-*` rung so a request that fails several rungs is\n // answered by the standard-header rung first.\n const stdHeaderRejection = validateStandardRequestHeaders(\n {\n httpMethod: request.method,\n mcpMethodHeader: request.headers.get('mcp-method') ?? undefined,\n mcpNameHeader: request.headers.get('mcp-name') ?? undefined\n },\n route\n );\n if (stdHeaderRejection !== undefined) {\n reportError(new Error(`Rejected inbound request (${stdHeaderRejection.cell}): ${stdHeaderRejection.message}`));\n return rejectionResponse(stdHeaderRejection, echoableRequestId(route.message));\n }\n\n const meta = route.messageKind === 'request' ? requestMetaOf(route.message.params) : undefined;\n const declaredClientCapabilities = meta?.[CLIENT_CAPABILITIES_META_KEY] as ClientCapabilities | undefined;\n\n // Pre-dispatch capability gate: a request to a method whose processing\n // structurally requires a client capability the request's validated\n // envelope did not declare is refused here, before any instance is\n // constructed or dispatched. Answering at the entry short-circuits\n // before factory construction and pins the spec-mandated HTTP 400 for\n // this error unconditionally; a handler-time -32021 (the\n // input_required gate) also maps to 400 at the per-request transport,\n // but only while no response has been committed.\n if (route.messageKind === 'request') {\n const required = requiredClientCapabilitiesForRequest(route.message.method);\n if (required !== undefined) {\n const missing = missingClientCapabilities(required, declaredClientCapabilities);\n if (missing !== undefined) {\n const error = new MissingRequiredClientCapabilityError({ requiredCapabilities: missing });\n reportError(error);\n return jsonRpcErrorResponse(\n httpStatusForErrorCode(error.code, 'ladder'),\n error.code,\n error.message,\n error.data,\n route.message.id\n );\n }\n }\n }\n\n const product = await factory({\n era: 'modern',\n ...(authInfo !== undefined && { authInfo }),\n requestInfo: request\n });\n const server = product instanceof McpServer ? product.server : product;\n\n // Entry-handled `subscriptions/listen`: the router owns ack-first /\n // per-stream filtering / subscription-id stamping / keepalive /\n // capacity / teardown. The factory IS constructed for listen — to read\n // the instance's declared capabilities only, so the acknowledged\n // filter reflects what the server can actually deliver. Unlike the\n // discover path (which connects via the per-request transport and tears\n // down with it), the probe instance is never connected: capabilities\n // are read off the unconnected instance and it is closed immediately.\n // Authorization the consumer performs inside the factory therefore DOES\n // see listen requests, although token verification still belongs at the\n // middleware layer mounted in front of this entry.\n if (route.messageKind === 'request' && route.message.method === 'subscriptions/listen') {\n const capabilities = server.getCapabilities();\n const serverInfo = serverIdentityOf(server);\n void product.close().catch(reportError);\n return listenRouter.serve(route.message, request.signal, capabilities, serverInfo);\n }\n\n // SEP-2243 `Mcp-Param-*` server-side validation (pre-dispatch ladder\n // rung): for a `tools/call`, look up the named tool's JSON inputSchema\n // on the just-produced instance and compare every `x-mcp-header`\n // declaration against the request's `Mcp-Param-{Name}` headers and the\n // body `arguments`. A mismatch (or a missing header for a present body\n // value, or an invalid Base64 sentinel) emits the same `400` /\n // `-32020` (`HeaderMismatch`) shape the edge cross-checks use. Only\n // applied when the factory returns an `McpServer` (the registry is the\n // schema source); a low-level `Server` factory has no registry, so\n // there is nothing to validate against.\n if (route.messageKind === 'request' && route.message.method === 'tools/call' && product instanceof McpServer) {\n const callParams = route.message.params as { name?: string; arguments?: Record } | undefined;\n const toolName = typeof callParams?.name === 'string' ? callParams.name : undefined;\n const inputSchema = toolName === undefined ? undefined : product.toolInputSchemaJson(toolName);\n if (inputSchema !== undefined) {\n const scan = scanXMcpHeaderDeclarations(inputSchema);\n if (scan.valid && scan.declarations.length > 0) {\n const rejection = validateMcpParamHeaders(scan.declarations, callParams?.arguments, request.headers);\n if (rejection !== undefined) {\n void product.close().catch(reportError);\n reportError(new Error(`Rejected inbound request (${rejection.cell}): ${rejection.message}`));\n return rejectionResponse(rejection, route.message.id);\n }\n }\n }\n }\n\n // Era-write at instance binding, then modern-only handler installation —\n // both before the instance is connected to the per-request transport.\n setNegotiatedProtocolVersion(server, claimedRevision);\n installModernOnlyHandlers(server, SUPPORTED_MODERN_PROTOCOL_VERSIONS);\n\n if (meta !== undefined) {\n seedClientIdentityFromEnvelope(server, {\n clientInfo: meta[CLIENT_INFO_META_KEY] as Implementation | undefined,\n clientCapabilities: declaredClientCapabilities\n });\n }\n\n // Track the instance until its exchange tears down so close() can abort it.\n const previousOnClose = server.onclose;\n inflight.add(server);\n server.onclose = () => {\n inflight.delete(server);\n previousOnClose?.();\n };\n\n try {\n const response = await invoke(product, route.message, {\n classification: route.classification,\n request,\n ...(authInfo !== undefined && { authInfo }),\n ...(responseMode !== undefined && { responseMode }),\n ...(options.keepAliveMs !== undefined && { keepAliveMs: options.keepAliveMs })\n });\n if (route.messageKind === 'notification') {\n // Notification exchanges have no terminal response to ride the\n // transport's auto-close, so release the per-request instance here.\n queueMicrotask(() => void server.close().catch(() => {}));\n }\n return response;\n } catch (error) {\n if (error instanceof SdkError && error.code === SdkErrorCode.ConnectionClosed) {\n // The client went away before a response existed; there is\n // nobody left to answer.\n return new Response(null, { status: 499 });\n }\n // No terminal response will ride the transport's close chain after a\n // failure here: close the per-request instance explicitly and drop it\n // from the in-flight set so repeated failures cannot accumulate\n // connected instances until handler.close().\n await server.close().catch(() => {});\n inflight.delete(server);\n reportError(toError(error));\n return internalServerErrorResponse(echoableRequestId(route.message));\n }\n }\n\n async function serveLegacyRoute(\n route: InboundLegacyRoute,\n forwardRequest: Request,\n authInfo: AuthInfo | undefined,\n parsedBody: unknown\n ): Promise {\n if (legacyHandler !== undefined) {\n return legacyHandler(forwardRequest, {\n ...(authInfo !== undefined && { authInfo }),\n ...(parsedBody !== undefined && { parsedBody })\n });\n }\n const strict = modernOnlyStrictRejection(route, SUPPORTED_MODERN_PROTOCOL_VERSIONS);\n if (strict === undefined) {\n // Legacy-classified notification on a modern-only endpoint:\n // acknowledged and dropped, never dispatched.\n return new Response(null, { status: 202 });\n }\n reportError(new Error(`Rejected 2025-era request on a modern-only endpoint (${strict.cell}): ${strict.message}`));\n return rejectionResponse(strict, echoableRequestId(parsedBody));\n }\n\n async function handle(request: Request, requestOptions?: McpHandlerRequestOptions): Promise {\n const authInfo = requestOptions?.authInfo;\n\n // Content-Type check, answered before the body is read (parsed media\n // type — see isJsonContentType). Load-bearing for the modern leg,\n // whose ladder does not inspect Content-Type; the legacy transport\n // keeps its own check for hand-wired use, so via this entry a\n // doubly-invalid request answers 415 here before the transport's 406\n // Accept check would.\n if (request.method.toUpperCase() === 'POST' && !isJsonContentType(request.headers.get('content-type'))) {\n reportError(new Error('Unsupported Media Type: Content-Type must be application/json'));\n return jsonRpcErrorResponse(415, -32_000, 'Unsupported Media Type: Content-Type must be application/json');\n }\n\n const classified = await classifyEntryRequest(request, requestOptions?.parsedBody);\n\n if (classified.step === 'unreadable-body') {\n return jsonRpcErrorResponse(400, -32_700, 'Parse error: the request body could not be read');\n }\n if (classified.step === 'no-json-body') {\n // No JSON body to classify: there is no envelope claim, so this is\n // legacy traffic when legacy serving is configured (the legacy leg\n // answers its own parse error, unchanged), and a parse error\n // otherwise.\n if (legacyHandler !== undefined) {\n return legacyHandler(classified.forwardRequest, { ...(authInfo !== undefined && { authInfo }) });\n }\n return jsonRpcErrorResponse(400, -32_700, 'Parse error: the request body is not valid JSON');\n }\n\n const { outcome, body, parsedBody, forwardRequest } = classified;\n try {\n switch (outcome.kind) {\n case 'reject': {\n reportError(new Error(`Rejected inbound request (${outcome.cell}): ${outcome.message}`));\n return rejectionResponse(outcome, echoableRequestId(body));\n }\n case 'modern': {\n return await serveModern(outcome, request, authInfo);\n }\n case 'legacy': {\n return await serveLegacyRoute(outcome, forwardRequest, authInfo, parsedBody);\n }\n }\n } catch (error) {\n // Entry-internal failure while serving a classified request (a\n // throwing factory or a failed connect, on either leg): the parsed\n // body is in scope here, so the 500 body echoes the request id when\n // it could be read.\n reportError(toError(error));\n return internalServerErrorResponse(echoableRequestId(body));\n }\n }\n\n const fetchFace = async (request: Request, requestOptions?: McpHandlerRequestOptions): Promise => {\n if (closed) {\n throw new Error('This MCP handler has been closed');\n }\n try {\n return await handle(request, requestOptions);\n } catch (error) {\n reportError(toError(error));\n return internalServerErrorResponse(echoableRequestId(requestOptions?.parsedBody));\n }\n };\n\n return {\n fetch: fetchFace,\n notify,\n bus,\n close: async () => {\n closed = true;\n listenRouter.closeAll();\n const closing = [...inflight].map(server => server.close().catch(() => {}));\n inflight.clear();\n await Promise.all(closing);\n }\n };\n}\n","import type { AuthInfo } from '@modelcontextprotocol/core-internal';\nimport { OAuthError, OAuthErrorCode } from '@modelcontextprotocol/core-internal';\n\n/**\n * Minimal token-verifier interface for MCP servers acting as an OAuth 2.0\n * Resource Server. Implementations introspect or locally validate an access\n * token and return the resulting {@link AuthInfo}, which the serving entry\n * surfaces to MCP request handlers via `ctx.http.authInfo`.\n *\n * This is intentionally narrower than a full OAuth Authorization Server\n * provider — it only covers the verification step a Resource Server needs.\n */\nexport interface OAuthTokenVerifier {\n /**\n * Verifies an access token and returns information about it.\n *\n * Implementations should throw an {@link OAuthError} with\n * `OAuthErrorCode.InvalidToken` when the token is unknown, revoked, or\n * otherwise invalid; the bearer-auth helpers map that to a `401` with a\n * `WWW-Authenticate` challenge.\n *\n * Note: bearer-auth verification rejects tokens whose\n * `AuthInfo.expiresAt` is unset (matches v1 behavior). Ensure your\n * verifier populates it (e.g. from RFC 7662 introspection `exp` or the\n * JWT `exp` claim).\n */\n verifyAccessToken(token: string): Promise;\n}\n\n/**\n * Options for {@link verifyBearerToken}, {@link requireBearerAuth}, and\n * {@link bearerAuthChallengeResponse}.\n */\nexport interface BearerAuthOptions {\n /**\n * A verifier used to validate access tokens.\n */\n verifier: OAuthTokenVerifier;\n\n /**\n * Optional scopes that the token must have. When any are missing the\n * request is refused with `403 insufficient_scope`.\n */\n requiredScopes?: string[];\n\n /**\n * Optional Protected Resource Metadata URL to advertise in the\n * `WWW-Authenticate` header on 401/403 responses, per\n * {@link https://datatracker.ietf.org/doc/html/rfc9728 | RFC 9728}.\n *\n * Typically built with `getOAuthProtectedResourceMetadataUrl`, exported\n * from this package.\n */\n resourceMetadataUrl?: string;\n}\n\nfunction headerQuotedValue(value: string): string {\n // HTTP quoted-string per RFC 7235: escape backslash and double quote, and\n // replace characters a header cannot carry (controls, anything beyond\n // printable ASCII) so a verifier-authored message can never make the\n // challenge Response constructor throw.\n return value.replaceAll(/[\\\\\"]/g, String.raw`\\$&`).replaceAll(/[^\\u0020-\\u007E]/g, ' ');\n}\n\nfunction buildWwwAuthenticateHeader(\n errorCode: string,\n description: string,\n requiredScopes: string[],\n resourceMetadataUrl: string | undefined\n): string {\n let header = `Bearer error=\"${headerQuotedValue(errorCode)}\", error_description=\"${headerQuotedValue(description)}\"`;\n if (requiredScopes.length > 0) {\n header += `, scope=\"${requiredScopes.join(' ')}\"`;\n }\n if (resourceMetadataUrl) {\n header += `, resource_metadata=\"${resourceMetadataUrl}\"`;\n }\n return header;\n}\n\n/**\n * Validate a raw `Authorization` header value as a Bearer token and return\n * the verified {@link AuthInfo}.\n *\n * The runtime-neutral core of Bearer authentication: it parses the header,\n * runs the verifier, enforces `requiredScopes`, and rejects tokens without an\n * expiration or past it. On any failure it throws an {@link OAuthError} —\n * pass that to {@link bearerAuthChallengeResponse} for the matching HTTP\n * answer, or use {@link requireBearerAuth} to get both steps as one call.\n *\n * Framework adapters build on this: `requireBearerAuth` from\n * `@modelcontextprotocol/express` feeds it `req.headers.authorization`.\n */\nexport async function verifyBearerToken(authorizationHeader: string | null | undefined, options: BearerAuthOptions): Promise {\n const { verifier, requiredScopes = [] } = options;\n\n if (!authorizationHeader) {\n throw new OAuthError(OAuthErrorCode.InvalidToken, 'Missing Authorization header');\n }\n\n const [type, token] = authorizationHeader.split(' ');\n if (type?.toLowerCase() !== 'bearer' || !token) {\n throw new OAuthError(OAuthErrorCode.InvalidToken, \"Invalid Authorization header format, expected 'Bearer TOKEN'\");\n }\n\n const authInfo = await verifier.verifyAccessToken(token);\n\n // Check if token has the required scopes (if any)\n if (requiredScopes.length > 0) {\n const hasAllScopes = requiredScopes.every(scope => authInfo.scopes.includes(scope));\n if (!hasAllScopes) {\n throw new OAuthError(OAuthErrorCode.InsufficientScope, 'Insufficient scope');\n }\n }\n\n // Check if the token is set to expire or if it is expired\n if (typeof authInfo.expiresAt !== 'number' || Number.isNaN(authInfo.expiresAt)) {\n throw new OAuthError(OAuthErrorCode.InvalidToken, 'Token has no expiration time');\n } else if (authInfo.expiresAt < Date.now() / 1000) {\n throw new OAuthError(OAuthErrorCode.InvalidToken, 'Token has expired');\n }\n\n return authInfo;\n}\n\n/**\n * Build the HTTP answer for a Bearer authentication failure.\n *\n * Maps an {@link OAuthError} to its status — `401` for `invalid_token` and\n * `403` for `insufficient_scope` (both carrying the `WWW-Authenticate: Bearer …`\n * challenge, with `resource_metadata` when configured so clients can discover\n * the Authorization Server), `500` for `server_error`, `400` for anything\n * else. A non-`OAuthError` value answers `500 server_error`. The body is the\n * OAuth error JSON.\n */\nexport function bearerAuthChallengeResponse(\n error: unknown,\n options?: Pick\n): Response {\n const { requiredScopes = [], resourceMetadataUrl } = options ?? {};\n\n if (!(error instanceof OAuthError)) {\n const serverError = new OAuthError(OAuthErrorCode.ServerError, 'Internal Server Error');\n return Response.json(serverError.toResponseObject(), { status: 500 });\n }\n\n switch (error.code) {\n case OAuthErrorCode.InvalidToken: {\n const challenge = buildWwwAuthenticateHeader(error.code, error.message, requiredScopes, resourceMetadataUrl);\n return Response.json(error.toResponseObject(), { status: 401, headers: { 'WWW-Authenticate': challenge } });\n }\n case OAuthErrorCode.InsufficientScope: {\n const challenge = buildWwwAuthenticateHeader(error.code, error.message, requiredScopes, resourceMetadataUrl);\n return Response.json(error.toResponseObject(), { status: 403, headers: { 'WWW-Authenticate': challenge } });\n }\n case OAuthErrorCode.ServerError: {\n return Response.json(error.toResponseObject(), { status: 500 });\n }\n default: {\n return Response.json(error.toResponseObject(), { status: 400 });\n }\n }\n}\n\n/**\n * Require a valid Bearer token on web-standard requests.\n *\n * The framework-free counterpart of `requireBearerAuth` from\n * `@modelcontextprotocol/express`, for hosts whose HTTP surface is a\n * `fetch(request)` handler — Cloudflare Workers, Deno, Bun, Hono. The\n * returned gate resolves to the verified {@link AuthInfo}, or to the\n * ready-to-return challenge `Response` when the request must be refused.\n *\n * @example\n * ```ts source=\"./bearerAuth.examples.ts#requireBearerAuth_fetchGate\"\n * const gate = requireBearerAuth({ verifier, requiredScopes: ['mcp'] });\n *\n * async function fetchHandler(request: Request): Promise {\n * const auth: AuthInfo | Response = await gate(request);\n * if (auth instanceof Response) return auth;\n * return handler.fetch(request, { authInfo: auth });\n * }\n * ```\n */\nexport function requireBearerAuth(options: BearerAuthOptions): (request: Request) => Promise {\n // Destructure at creation so a plain-JS caller passing undefined or\n // malformed options crashes at startup, not on the first request.\n const { verifier, requiredScopes = [], resourceMetadataUrl } = options;\n const resolved = { verifier, requiredScopes, resourceMetadataUrl };\n return async request => {\n // Outside the try: a wrong-framework misuse (no web-standard Request)\n // should throw loudly, not surface as a 500 challenge. Fetch's\n // Headers.get comma-joins repeated headers where Node keeps the\n // first; take the first segment so both adapters agree (the token68\n // alphabet has no comma, so this is lossless).\n const [authorizationHeader] = (request.headers.get('authorization') ?? '').split(',');\n try {\n return await verifyBearerToken(authorizationHeader || undefined, resolved);\n } catch (error) {\n return bearerAuthChallengeResponse(error, resolved);\n }\n };\n}\n","export type HostHeaderValidationResult =\n | { ok: true; hostname: string }\n | {\n ok: false;\n errorCode: 'missing_host' | 'invalid_host_header' | 'invalid_host';\n message: string;\n hostHeader?: string;\n hostname?: string;\n };\n\n/**\n * Parse and validate a `Host` header against an allowlist of hostnames (port-agnostic).\n *\n * - Input host header may include a port (e.g. `localhost:3000`) or IPv6 brackets (e.g. `[::1]:3000`).\n * - Allowlist items should be hostnames only (no ports). For IPv6, include brackets (e.g. `[::1]`).\n */\nexport function validateHostHeader(hostHeader: string | null | undefined, allowedHostnames: string[]): HostHeaderValidationResult {\n if (!hostHeader) {\n return { ok: false, errorCode: 'missing_host', message: 'Missing Host header' };\n }\n\n // Use URL API to parse hostname (handles IPv4, IPv6, and regular hostnames)\n let hostname: string;\n try {\n hostname = new URL(`http://${hostHeader}`).hostname;\n } catch {\n return { ok: false, errorCode: 'invalid_host_header', message: `Invalid Host header: ${hostHeader}`, hostHeader };\n }\n\n if (!allowedHostnames.includes(hostname)) {\n return { ok: false, errorCode: 'invalid_host', message: `Invalid Host: ${hostname}`, hostHeader, hostname };\n }\n\n return { ok: true, hostname };\n}\n\n/**\n * Convenience allowlist for `localhost` DNS rebinding protection.\n */\nexport function localhostAllowedHostnames(): string[] {\n return ['localhost', '127.0.0.1', '[::1]'];\n}\n\n/**\n * Web-standard `Request` helper for DNS rebinding protection.\n * @example\n * ```ts source=\"./hostHeaderValidation.examples.ts#hostHeaderValidationResponse_basicUsage\"\n * const result = validateHostHeader(req.headers.get('host'), ['localhost']);\n * ```\n */\nexport function hostHeaderValidationResponse(req: Request, allowedHostnames: string[]): Response | undefined {\n const result = validateHostHeader(req.headers.get('host'), allowedHostnames);\n if (result.ok) return undefined;\n\n return Response.json(\n {\n jsonrpc: '2.0',\n error: {\n code: -32_000,\n message: result.message\n },\n id: null\n },\n {\n status: 403,\n headers: { 'Content-Type': 'application/json' }\n }\n );\n}\n","import type { OAuthMetadata, OAuthProtectedResourceMetadata } from '@modelcontextprotocol/core-internal';\nimport { OAuthError, OAuthErrorCode } from '@modelcontextprotocol/core-internal';\n\n/**\n * Options for {@link oauthMetadataResponse} and\n * {@link buildOAuthProtectedResourceMetadata}.\n */\nexport interface AuthMetadataOptions {\n /**\n * Authorization Server metadata (RFC 8414) for the AS this MCP server\n * relies on. Served at `/.well-known/oauth-authorization-server` so\n * legacy clients that probe the resource origin still discover the AS.\n */\n oauthMetadata: OAuthMetadata;\n\n /**\n * The public URL of this MCP server, used as the `resource` value in the\n * Protected Resource Metadata document. Any path component is reflected\n * in the well-known route per RFC 9728.\n */\n resourceServerUrl: URL;\n\n /**\n * Optional documentation URL advertised as `resource_documentation`.\n */\n serviceDocumentationUrl?: URL;\n\n /**\n * Optional list of scopes this MCP server understands, advertised as\n * `scopes_supported`.\n */\n scopesSupported?: string[];\n\n /**\n * Optional human-readable name advertised as `resource_name`.\n */\n resourceName?: string;\n\n /**\n * Allow a non-HTTPS issuer URL. Local testing only — never enable in\n * production. The Express adapter maps its\n * `MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL` environment variable here.\n */\n dangerouslyAllowInsecureIssuerUrl?: boolean;\n}\n\nfunction checkIssuerUrl(issuer: URL, allowInsecure: boolean | undefined): void {\n // RFC 8414 technically does not permit a localhost HTTPS exemption, but it is necessary for local testing.\n if (issuer.protocol !== 'https:' && issuer.hostname !== 'localhost' && issuer.hostname !== '127.0.0.1' && !allowInsecure) {\n throw new Error('Issuer URL must be HTTPS');\n }\n if (issuer.hash) {\n throw new Error(`Issuer URL must not have a fragment: ${issuer}`);\n }\n if (issuer.search) {\n throw new Error(`Issuer URL must not have a query string: ${issuer}`);\n }\n}\n\n/**\n * Derive the RFC 9728 Protected Resource Metadata document from\n * {@link AuthMetadataOptions}, validating the Authorization Server issuer URL\n * (HTTPS required outside localhost) in the process.\n *\n * `oauthMetadataResponse` and the Express `mcpAuthMetadataRouter` both build\n * on this; use it directly when serving the document through your own\n * routing — or call it once at startup to fail fast on a misconfigured\n * issuer before any request arrives.\n */\nexport function buildOAuthProtectedResourceMetadata(options: AuthMetadataOptions): OAuthProtectedResourceMetadata {\n checkIssuerUrl(new URL(options.oauthMetadata.issuer), options.dangerouslyAllowInsecureIssuerUrl);\n return {\n resource: options.resourceServerUrl.href,\n authorization_servers: [options.oauthMetadata.issuer],\n scopes_supported: options.scopesSupported,\n resource_name: options.resourceName,\n resource_documentation: options.serviceDocumentationUrl?.href\n };\n}\n\n/**\n * Builds the RFC 9728 Protected Resource Metadata URL for a given MCP server\n * URL by inserting `/.well-known/oauth-protected-resource` ahead of the path.\n *\n * @example\n * ```ts\n * getOAuthProtectedResourceMetadataUrl(new URL('https://api.example.com/mcp'))\n * // → 'https://api.example.com/.well-known/oauth-protected-resource/mcp'\n * ```\n */\nexport function getOAuthProtectedResourceMetadataUrl(serverUrl: URL): string {\n return new URL(protectedResourceMetadataPath(serverUrl), serverUrl).href;\n}\n\n/** The RFC 9728 path-aware well-known path for a resource URL. */\nfunction protectedResourceMetadataPath(resourceServerUrl: URL): string {\n // Normalized like the request path in `oauthMetadataResponse`: a resource\n // URL with a trailing slash must not make its own PRM route unreachable.\n const rsPath = stripTrailingSlash(resourceServerUrl.pathname);\n return `/.well-known/oauth-protected-resource${rsPath === '/' ? '' : rsPath}`;\n}\n\nfunction stripTrailingSlash(path: string): string {\n return path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;\n}\n\nconst ALLOWED_METHODS = 'GET, HEAD, OPTIONS';\n\nfunction metadataDocumentResponse(request: Request, metadata: OAuthMetadata | OAuthProtectedResourceMetadata): Response {\n // Metadata documents must be fetchable from web-based MCP clients on any\n // origin, so every response carries permissive CORS headers.\n if (request.method === 'OPTIONS') {\n const requestedHeaders = request.headers.get('access-control-request-headers');\n return new Response(null, {\n status: 204,\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': ALLOWED_METHODS,\n // The reflected allow-list makes the response vary by request:\n // without this a shared cache would replay one preflight's\n // allow-list against another's headers.\n ...(requestedHeaders === null\n ? {}\n : { 'Access-Control-Allow-Headers': requestedHeaders, Vary: 'Access-Control-Request-Headers' })\n }\n });\n }\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n const error = new OAuthError(OAuthErrorCode.MethodNotAllowed, `The method ${request.method} is not allowed for this endpoint`);\n return Response.json(error.toResponseObject(), {\n status: 405,\n headers: { Allow: ALLOWED_METHODS, 'Access-Control-Allow-Origin': '*' }\n });\n }\n const response = Response.json(metadata, { headers: { 'Access-Control-Allow-Origin': '*' } });\n // RFC 9110: HEAD is GET without the body, same headers.\n return request.method === 'HEAD' ? new Response(null, { status: response.status, headers: response.headers }) : response;\n}\n\n/**\n * Serve the two OAuth discovery documents an MCP server acting as a Resource\n * Server exposes, from a web-standard `fetch(request)` handler:\n *\n * - `/.well-known/oauth-protected-resource[/]` — RFC 9728 Protected\n * Resource Metadata, derived from the supplied options (path-aware: the\n * resource URL's path is reflected in the route).\n * - `/.well-known/oauth-authorization-server` — RFC 8414 Authorization\n * Server Metadata, passed through verbatim.\n *\n * Returns the matched document `Response` (JSON with permissive CORS, `405`\n * with an `Allow` header for non-GET methods, `204` for CORS preflight), or\n * `undefined` when the request path is neither well-known route — fall\n * through to your own routing. The framework-free counterpart of\n * `mcpAuthMetadataRouter` from `@modelcontextprotocol/express`; pair it with\n * `requireBearerAuth` and `getOAuthProtectedResourceMetadataUrl` so\n * unauthenticated clients can discover the AS from the `401` challenge.\n *\n * @example\n * ```ts source=\"./oauthMetadata.examples.ts#oauthMetadataResponse_fetchHandler\"\n * async function fetchHandler(request: Request): Promise {\n * return oauthMetadataResponse(request, options) ?? serveMcp(request);\n * }\n * ```\n */\nexport function oauthMetadataResponse(request: Request, options: AuthMetadataOptions): Response | undefined {\n // Match before building: unmatched traffic must fall through untouched,\n // even when the options are misconfigured — a bad issuer surfaces on the\n // discovery routes (or at startup via buildOAuthProtectedResourceMetadata),\n // never on the host's own traffic.\n // Tolerate a single trailing slash, as path-mounted routers do.\n const requestPath = stripTrailingSlash(new URL(request.url).pathname);\n if (requestPath === protectedResourceMetadataPath(options.resourceServerUrl)) {\n return metadataDocumentResponse(request, buildOAuthProtectedResourceMetadata(options));\n }\n if (requestPath === '/.well-known/oauth-authorization-server') {\n buildOAuthProtectedResourceMetadata(options); // issuer validation\n return metadataDocumentResponse(request, options.oauthMetadata);\n }\n return undefined;\n}\n","/**\n * Framework-agnostic Origin header validation helpers.\n *\n * Browsers attach an `Origin` header to cross-origin requests; validating it\n * against an allowlist (alongside Host header validation) protects local and\n * development MCP servers against DNS rebinding and cross-site request\n * forgery. The framework middleware packages (`@modelcontextprotocol/express`,\n * `@modelcontextprotocol/hono`, `@modelcontextprotocol/fastify`,\n * `@modelcontextprotocol/node`) wrap these helpers; use them directly when\n * mounting a handler bare on a fetch-native runtime.\n *\n * Validation is deny-on-failure: a present `Origin` value that cannot be\n * parsed (including the opaque `null` origin) is rejected, never passed\n * through. Requests without an `Origin` header pass — non-browser MCP clients\n * do not send one.\n */\n\nexport type OriginValidationResult =\n | { ok: true; origin?: string; hostname?: string }\n | {\n ok: false;\n errorCode: 'invalid_origin_header' | 'invalid_origin';\n message: string;\n originHeader?: string;\n hostname?: string;\n };\n\n/**\n * Validate an `Origin` header against an allowlist of hostnames (port-agnostic).\n *\n * - A missing/empty `Origin` header passes: non-browser clients do not send one,\n * and only browser-originated requests carry the header this check defends against.\n * - Allowlist items are hostnames only (no scheme, no port), the same convention as\n * `validateHostHeader`. For IPv6, include brackets (e.g. `[::1]`).\n * - Any present value that cannot be parsed as an origin URL — including the literal\n * `null` origin browsers send for opaque contexts — is rejected (deny on failure).\n */\nexport function validateOriginHeader(originHeader: string | null | undefined, allowedOriginHostnames: string[]): OriginValidationResult {\n if (originHeader === null || originHeader === undefined || originHeader === '') {\n return { ok: true };\n }\n\n let hostname: string;\n try {\n hostname = new URL(originHeader).hostname;\n } catch {\n return { ok: false, errorCode: 'invalid_origin_header', message: `Invalid Origin header: ${originHeader}`, originHeader };\n }\n if (hostname === '') {\n // Opaque origins (\"null\") and other non-hierarchical values parse without a\n // hostname; they can never be allowlisted.\n return { ok: false, errorCode: 'invalid_origin_header', message: `Invalid Origin header: ${originHeader}`, originHeader };\n }\n\n if (!allowedOriginHostnames.includes(hostname)) {\n return { ok: false, errorCode: 'invalid_origin', message: `Invalid Origin: ${hostname}`, originHeader, hostname };\n }\n\n return { ok: true, origin: originHeader, hostname };\n}\n\n/**\n * Convenience allowlist of localhost-class origin hostnames, mirroring\n * `localhostAllowedHostnames`.\n */\nexport function localhostAllowedOrigins(): string[] {\n return ['localhost', '127.0.0.1', '[::1]'];\n}\n\n/**\n * Web-standard `Request` helper for Origin validation: returns a `403` JSON-RPC\n * error response when the request's `Origin` header is not allowed, and\n * `undefined` when the request may proceed.\n *\n * ```ts\n * const rejected = originValidationResponse(request, localhostAllowedOrigins());\n * if (rejected) return rejected;\n * ```\n */\nexport function originValidationResponse(req: Request, allowedOriginHostnames: string[]): Response | undefined {\n const result = validateOriginHeader(req.headers.get('origin'), allowedOriginHostnames);\n if (result.ok) return undefined;\n\n return Response.json(\n {\n jsonrpc: '2.0',\n error: {\n code: -32_000,\n message: result.message\n },\n id: null\n },\n {\n status: 403,\n headers: { 'Content-Type': 'application/json' }\n }\n );\n}\n","import type { ServerContext } from '@modelcontextprotocol/core-internal';\n\n/**\n * Options for {@linkcode createRequestStateCodec}.\n */\nexport interface RequestStateCodecOptions {\n /**\n * The HMAC secret. A `string` value is UTF-8-encoded. MUST be at least\n * 32 bytes (256 bits) long; a `RangeError` is thrown at\n * construction otherwise. The same key must be available to every server\n * instance that may receive an echoed `requestState` (so a per-process\n * random key only works when one process serves every round of a flow).\n */\n key: Uint8Array | string;\n\n /**\n * How long a minted `requestState` stays valid, in seconds. An echoed\n * value past its expiry is rejected by {@linkcode RequestStateCodec.verify}.\n * Defaults to `600` (ten minutes).\n */\n ttlSeconds?: number;\n\n /**\n * Optional context binding. Called at mint time and again at verify time;\n * a `requestState` minted under one binding value is rejected when echoed\n * under a different one. Use this to bind state to the authenticated\n * principal and/or the originating method (the spec's user-binding MUST\n * for state that influences authorization), for example:\n *\n * ```ts\n * bind: ctx => `${ctx.mcpReq.method}\\0${ctx.http?.authInfo?.clientId ?? ''}`\n * ```\n *\n * The returned value is stored in the envelope as a domain-separated HMAC\n * tag (keyed by the codec's `key`), not the raw string — so a principal\n * identifier in the binding does not appear in the wire value the client\n * holds.\n *\n * When configured, {@linkcode RequestStateCodec.mint} requires its `ctx`\n * argument.\n */\n bind?: (ctx: ServerContext) => string;\n}\n\n/**\n * The codec returned by {@linkcode createRequestStateCodec}: `mint` seals a\n * JSON-serializable payload into the wire string a handler returns from\n * `inputRequired({ requestState })`; `verify` is the function to drop into\n * {@linkcode server/server.ServerOptions | ServerOptions}`.requestState.verify`\n * (it throws on any failure, which the seam answers as the frozen `-32602`).\n * The decoded payload `verify` resolves with is handed to the handler by the\n * seam via the typed `ctx.mcpReq.requestState()` accessor — `mint` and\n * `requestState()` are the typed encode/read pair.\n */\nexport interface RequestStateCodec {\n /**\n * Seal `payload` into an opaque wire string. The result is what the\n * handler returns from `inputRequired({ requestState })`.\n *\n * @param ctx The handler's context. Required when the codec was created\n * with a {@linkcode RequestStateCodecOptions.bind | bind}\n * callback; ignored otherwise.\n */\n mint(payload: T, ctx?: ServerContext): Promise;\n\n /**\n * Verify an echoed `requestState` and return the original payload. Throws\n * on any failure (bad MAC, expired, bind mismatch, malformed). The thrown\n * message is a fixed opaque reason code (`'malformed'` / `'mac'` /\n * `'expired'` / `'bind'`) — never the decoded payload, the binding value,\n * or any other context-derived field.\n *\n * Pass this directly as `ServerOptions.requestState.verify`.\n */\n verify(state: string, ctx: ServerContext): Promise;\n}\n\nconst PREFIX = 'v1.';\n\n// Runtime-neutral base64url (no padding) over raw bytes. `btoa`/`atob` are\n// available in browsers, Cloudflare Workers, and Node 16+.\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let bin = '';\n for (const b of bytes) bin += String.fromCodePoint(b);\n return btoa(bin).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');\n}\n\n// Constant-time equality on the fixed-length base64url bind-tag string. Same\n// guarantee as the body-MAC check (which uses `subtle.verify`): the\n// per-character XOR accumulator does not vary its trip count with the position\n// of the first mismatch. Length is fixed (22 chars for the 128-bit truncated\n// tag), so the early length-check leaks nothing.\nfunction constantTimeTagEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n let r = 0;\n for (let i = 0; i < a.length; i++) {\n r |= a.codePointAt(i)! ^ b.codePointAt(i)!;\n }\n return r === 0;\n}\n\nfunction base64UrlToBytes(s: string): Uint8Array {\n const b64 = s.replaceAll('-', '+').replaceAll('_', '/');\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.codePointAt(i)!;\n return bytes;\n}\n\n/**\n * Create an opt-in HMAC-SHA256 codec for the multi-round-trip `requestState`\n * (protocol revision 2026-07-28).\n *\n * `requestState` round-trips through the client and is attacker-controlled\n * input on re-entry. The SDK applies no protection of its own; this helper is\n * the convenience implementation of the spec's integrity MUST so authors don't\n * hand-roll HMAC. Wire shape:\n *\n * \"v1.\" b64url({\"p\":,\"exp\":,\"b\":?}) \".\" b64url(mac)\n *\n * where `bindTag` is `b64url(HMAC(key, \"mcp.requestState.bind:\" + bind(ctx))[:16])`\n * — the binding value is never embedded raw.\n *\n * The codec is **signed, not encrypted**: the body is integrity-protected but\n * the client can base64url-decode it and read the payload (`p`) in clear. Do\n * not put secrets in the payload; use an AEAD construction if confidentiality\n * is required. The handler reads its payload back via the typed\n * `ctx.mcpReq.requestState()` accessor — the seam has already run `verify`\n * (integrity proven, payload decoded) by the time the handler is entered.\n *\n * Verification is fail-closed and constant-time (WebCrypto `subtle.verify` for\n * the body MAC; a fixed-length XOR-accumulator compare for the bind tag).\n * See `examples/mrtr/server.ts` for a worked end-to-end example.\n *\n * Design comparison (mcp.d `secureRequestState`, the peer SDK's reference\n * implementation): mcp.d additionally offers an AES-256-GCM encrypted mode and\n * derives independent cipher / bind-HMAC sub-keys from the operator secret via\n * HKDF-SHA256, with an auto-generated per-process ephemeral key when none is\n * supplied. This codec deliberately ships only the signed mode and a single\n * keyed HMAC (domain-separated by input prefix) — HKDF sub-key derivation and\n * an encrypted mode are intentionally out of scope for the initial release.\n */\nexport function createRequestStateCodec(options: RequestStateCodecOptions): RequestStateCodec {\n const subtle = globalThis.crypto?.subtle;\n if (subtle === undefined) {\n throw new TypeError(\n 'createRequestStateCodec requires the Web Crypto API (globalThis.crypto.subtle); ' +\n 'see https://ts.sdk.modelcontextprotocol.io/v2/troubleshooting for the Node.js polyfill instructions'\n );\n }\n\n // Snapshot the key bytes at construction. When `options.key` is a\n // Uint8Array, holding the caller's reference would let a post-construction\n // mutation (e.g. zeroing the secret for hygiene) silently change the key\n // the lazy `importedKey()` reads on first mint/verify — a TOCTOU on the\n // secret. The owned copy here is what every later read sees.\n const keyBytes = typeof options.key === 'string' ? new TextEncoder().encode(options.key) : Uint8Array.from(options.key);\n if (keyBytes.byteLength < 32) {\n throw new RangeError(`createRequestStateCodec: key must be at least 32 bytes (got ${keyBytes.byteLength})`);\n }\n const ttlSeconds = options.ttlSeconds ?? 600;\n if (!Number.isFinite(ttlSeconds)) {\n // Infinity/NaN would serialize `exp` as JSON `null`, which then fails\n // verify() with the misleading reason 'expired' on every round-trip —\n // fail loudly at construction instead.\n throw new RangeError('createRequestStateCodec: ttlSeconds must be a finite number');\n }\n const bind = options.bind;\n\n // The CryptoKey is imported once (lazily) and reused for every mint/verify.\n // `keyBytes` is already an owned standalone Uint8Array (snapshot above), so\n // it satisfies WebCrypto's BufferSource requirement on every runtime.\n let cryptoKey: ReturnType | undefined;\n const importedKey = (): ReturnType =>\n (cryptoKey ??= subtle.importKey('raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']));\n\n const utf8 = new TextEncoder();\n\n // Domain-separated HMAC tag of the binding value, truncated to 128 bits and\n // base64url'd. The label keeps the bind-tag computation separate from the\n // body MAC even though both use the same key (the body MAC's input is the\n // versioned wire string `\"v1.\" + base64url(...)` and can never start with\n // this label).\n const BIND_LABEL = 'mcp.requestState.bind:';\n const bindTag = async (value: string): Promise => {\n const mac = new Uint8Array(await subtle.sign('HMAC', await importedKey(), utf8.encode(BIND_LABEL + value)));\n return bytesToBase64Url(mac.slice(0, 16));\n };\n\n return {\n async mint(payload, ctx) {\n const envelope: { p: T; exp: number; b?: string } = {\n p: payload,\n exp: Math.floor(Date.now() / 1000) + ttlSeconds\n };\n if (bind !== undefined) {\n if (ctx === undefined) {\n throw new TypeError('createRequestStateCodec: mint() requires ctx when a bind callback is configured');\n }\n envelope.b = await bindTag(bind(ctx));\n }\n const body = bytesToBase64Url(utf8.encode(JSON.stringify(envelope)));\n // The MAC covers `PREFIX + body` so the version tag is bound: a\n // valid `body.mac` pair under `v1.` cannot be transplanted to a\n // future `v2.` codec under the same key.\n const mac = new Uint8Array(await subtle.sign('HMAC', await importedKey(), utf8.encode(PREFIX + body)));\n return `${PREFIX}${body}.${bytesToBase64Url(mac)}`;\n },\n\n async verify(state, ctx) {\n // Envelope shape: \"v1.\" body \".\" mac. The MAC is checked FIRST so\n // every other rejection reason is only reachable for a value we\n // minted (or a peer with the key did).\n const dot = state.lastIndexOf('.');\n if (!state.startsWith(PREFIX) || dot <= PREFIX.length) {\n throw new Error('malformed');\n }\n const body = state.slice(PREFIX.length, dot);\n let macBytes: Uint8Array;\n try {\n macBytes = base64UrlToBytes(state.slice(dot + 1));\n } catch {\n throw new Error('malformed');\n }\n // SubtleCrypto.verify is constant-time by spec — no manual byte\n // compare, no `timingSafeEqual` dependency. The MAC input mirrors\n // mint: `PREFIX + body`, so the version tag is authenticated.\n const ok = await subtle.verify('HMAC', await importedKey(), macBytes, utf8.encode(PREFIX + body));\n if (!ok) {\n throw new Error('mac');\n }\n // The body decoded after a good MAC is by construction the JSON we\n // wrote; a parse failure here would indicate key compromise rather\n // than tampering, but stays fail-closed regardless.\n let envelope: { p: T; exp: number; b?: string };\n try {\n envelope = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(base64UrlToBytes(body))) as {\n p: T;\n exp: number;\n b?: string;\n };\n } catch {\n throw new Error('malformed');\n }\n if (typeof envelope.exp !== 'number' || envelope.exp < Math.floor(Date.now() / 1000)) {\n throw new Error('expired');\n }\n if (bind !== undefined) {\n const expected = await bindTag(bind(ctx));\n if (envelope.b === undefined || !constantTimeTagEqual(envelope.b, expected)) {\n // Opaque reason only — never interpolate the expected/actual\n // binding values (they may carry principal identifiers).\n throw new Error('bind');\n }\n } else if (envelope.b !== undefined) {\n // Fail-closed on configuration drift: a token minted with a\n // bind callback is rejected when verified by an instance that\n // has none configured. Accepting it would silently drop the\n // principal-binding guarantee.\n throw new Error('bind');\n }\n return envelope.p;\n }\n };\n}\n","import type { JsonSchemaType, jsonSchemaValidator, StandardSchemaWithJSON } from '@modelcontextprotocol/core-internal';\nimport { fromJsonSchema as coreFromJsonSchema } from '@modelcontextprotocol/core-internal';\nimport { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';\n\nlet _defaultValidator: jsonSchemaValidator | undefined;\n\nexport function fromJsonSchema(schema: JsonSchemaType, validator?: jsonSchemaValidator): StandardSchemaWithJSON {\n return coreFromJsonSchema(schema, validator ?? (_defaultValidator ??= new DefaultJsonSchemaValidator()));\n}\n"],"mappings":"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"}