@modelcontextprotocol/node
Advanced tools
+2
-214
@@ -1,219 +0,7 @@ | ||
| import { WebStandardStreamableHTTPServerTransportOptions } from "@modelcontextprotocol/server"; | ||
| import { AuthInfo, JSONRPCMessage, MessageExtraInfo, RequestId, Transport, WebStandardStreamableHTTPServerTransportOptions } from "@modelcontextprotocol/server"; | ||
| import { IncomingMessage, ServerResponse } from "node:http"; | ||
| import * as z from "zod/v4"; | ||
| //#region ../../core/src/types/schemas.d.ts | ||
| //#region src/streamableHttp.d.ts | ||
| /** | ||
| * A uniquely identifying ID for a request in JSON-RPC. | ||
| */ | ||
| declare const RequestIdSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>; | ||
| declare const JSONRPCMessageSchema: z.ZodUnion<readonly [z.ZodObject<{ | ||
| method: z.ZodString; | ||
| params: z.ZodOptional<z.ZodObject<{ | ||
| _meta: z.ZodOptional<z.ZodObject<{ | ||
| /** | ||
| * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. | ||
| */ | ||
| progressToken: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>; | ||
| /** | ||
| * If specified, this request is related to the provided task. | ||
| */ | ||
| "io.modelcontextprotocol/related-task": z.ZodOptional<z.ZodObject<{ | ||
| taskId: z.ZodString; | ||
| }, z.core.$strip>>; | ||
| }, z.core.$loose>>; | ||
| }, z.core.$loose>>; | ||
| jsonrpc: z.ZodLiteral<"2.0">; | ||
| id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>; | ||
| }, z.core.$strict>, z.ZodObject<{ | ||
| method: z.ZodString; | ||
| params: z.ZodOptional<z.ZodObject<{ | ||
| _meta: z.ZodOptional<z.ZodObject<{ | ||
| /** | ||
| * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. | ||
| */ | ||
| progressToken: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>; | ||
| /** | ||
| * If specified, this request is related to the provided task. | ||
| */ | ||
| "io.modelcontextprotocol/related-task": z.ZodOptional<z.ZodObject<{ | ||
| taskId: z.ZodString; | ||
| }, z.core.$strip>>; | ||
| }, z.core.$loose>>; | ||
| }, z.core.$loose>>; | ||
| jsonrpc: z.ZodLiteral<"2.0">; | ||
| }, z.core.$strict>, z.ZodObject<{ | ||
| jsonrpc: z.ZodLiteral<"2.0">; | ||
| id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>; | ||
| result: z.ZodObject<{ | ||
| /** | ||
| * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) | ||
| * for notes on `_meta` usage. | ||
| */ | ||
| _meta: z.ZodOptional<z.ZodObject<{ | ||
| /** | ||
| * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. | ||
| */ | ||
| progressToken: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>; | ||
| /** | ||
| * If specified, this request is related to the provided task. | ||
| */ | ||
| "io.modelcontextprotocol/related-task": z.ZodOptional<z.ZodObject<{ | ||
| taskId: z.ZodString; | ||
| }, z.core.$strip>>; | ||
| }, z.core.$loose>>; | ||
| }, z.core.$loose>; | ||
| }, z.core.$strict>, z.ZodObject<{ | ||
| jsonrpc: z.ZodLiteral<"2.0">; | ||
| id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>; | ||
| error: z.ZodObject<{ | ||
| code: z.ZodNumber; | ||
| message: z.ZodString; | ||
| data: z.ZodOptional<z.ZodUnknown>; | ||
| }, z.core.$strip>; | ||
| }, z.core.$strict>]>; | ||
| //#endregion | ||
| //#region ../../core/src/types/types.d.ts | ||
| type Primitive = string | number | boolean | bigint | null | undefined; | ||
| type Flatten<T$1> = T$1 extends Primitive ? T$1 : T$1 extends Array<infer U> ? Array<Flatten<U>> : T$1 extends Set<infer U> ? Set<Flatten<U>> : T$1 extends Map<infer K, infer V> ? Map<Flatten<K>, Flatten<V>> : T$1 extends object ? { [K in keyof T$1]: Flatten<T$1[K]> } : T$1; | ||
| type Infer<Schema extends z.ZodTypeAny> = Flatten<z.infer<Schema>>; | ||
| type RequestId = Infer<typeof RequestIdSchema>; | ||
| type JSONRPCMessage = Infer<typeof JSONRPCMessageSchema>; | ||
| /** | ||
| * Information about a validated access token, provided to request handlers. | ||
| */ | ||
| interface AuthInfo { | ||
| /** | ||
| * The access token. | ||
| */ | ||
| token: string; | ||
| /** | ||
| * The client ID associated with this token. | ||
| */ | ||
| clientId: string; | ||
| /** | ||
| * Scopes associated with this token. | ||
| */ | ||
| scopes: string[]; | ||
| /** | ||
| * When the token expires (in seconds since epoch). | ||
| */ | ||
| expiresAt?: number; | ||
| /** | ||
| * The RFC 8707 resource server identifier for which this token is valid. | ||
| * If set, this MUST match the MCP server's resource identifier (minus hash fragment). | ||
| */ | ||
| resource?: URL; | ||
| /** | ||
| * Additional data associated with the token. | ||
| * This field should be used for any additional data that needs to be attached to the auth info. | ||
| */ | ||
| extra?: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * Extra information about a message. | ||
| */ | ||
| interface MessageExtraInfo { | ||
| /** | ||
| * The original HTTP request. | ||
| */ | ||
| request?: globalThis.Request; | ||
| /** | ||
| * The authentication information. | ||
| */ | ||
| authInfo?: AuthInfo; | ||
| /** | ||
| * Callback to close the SSE stream for this request, triggering client reconnection. | ||
| * Only available when using {@linkcode @modelcontextprotocol/node!streamableHttp.NodeStreamableHTTPServerTransport | NodeStreamableHTTPServerTransport} with eventStore configured. | ||
| */ | ||
| closeSSEStream?: () => void; | ||
| /** | ||
| * Callback to close the standalone GET SSE stream, triggering client reconnection. | ||
| * Only available when using {@linkcode @modelcontextprotocol/node!streamableHttp.NodeStreamableHTTPServerTransport | NodeStreamableHTTPServerTransport} with eventStore configured. | ||
| */ | ||
| closeStandaloneSSEStream?: () => void; | ||
| } | ||
| //#endregion | ||
| //#region ../../core/src/shared/transport.d.ts | ||
| /** | ||
| * Options for sending a JSON-RPC message. | ||
| */ | ||
| type TransportSendOptions = { | ||
| /** | ||
| * If present, `relatedRequestId` is used to indicate to the transport which incoming request to associate this outgoing message with. | ||
| */ | ||
| relatedRequestId?: RequestId | undefined; | ||
| /** | ||
| * The resumption token used to continue long-running requests that were interrupted. | ||
| * | ||
| * This allows clients to reconnect and continue from where they left off, if supported by the transport. | ||
| */ | ||
| resumptionToken?: string | undefined; | ||
| /** | ||
| * A callback that is invoked when the resumption token changes, if supported by the transport. | ||
| * | ||
| * This allows clients to persist the latest token for potential reconnection. | ||
| */ | ||
| onresumptiontoken?: ((token: string) => void) | undefined; | ||
| }; | ||
| /** | ||
| * Describes the minimal contract for an MCP transport that a client or server can communicate over. | ||
| */ | ||
| interface Transport { | ||
| /** | ||
| * Starts processing messages on the transport, including any connection steps that might need to be taken. | ||
| * | ||
| * This method should only be called after callbacks are installed, or else messages may be lost. | ||
| * | ||
| * NOTE: This method should not be called explicitly when using {@linkcode @modelcontextprotocol/client!client/client.Client | Client} or {@linkcode @modelcontextprotocol/server!server/server.Server | Server} classes, as they will implicitly call {@linkcode Transport.start | start()}. | ||
| */ | ||
| start(): Promise<void>; | ||
| /** | ||
| * Sends a JSON-RPC message (request or response). | ||
| * | ||
| * If present, `relatedRequestId` is used to indicate to the transport which incoming request to associate this outgoing message with. | ||
| */ | ||
| send(message: JSONRPCMessage, options?: TransportSendOptions): Promise<void>; | ||
| /** | ||
| * Closes the connection. | ||
| */ | ||
| close(): Promise<void>; | ||
| /** | ||
| * Callback for when the connection is closed for any reason. | ||
| * | ||
| * This should be invoked when {@linkcode Transport.close | close()} is called as well. | ||
| */ | ||
| onclose?: (() => void) | undefined; | ||
| /** | ||
| * Callback for when an error occurs. | ||
| * | ||
| * Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band. | ||
| */ | ||
| onerror?: ((error: Error) => void) | undefined; | ||
| /** | ||
| * Callback for when a message (request or response) is received over the connection. | ||
| * | ||
| * Includes the {@linkcode MessageExtraInfo.request | request} and {@linkcode MessageExtraInfo.authInfo | authInfo} if the transport is authenticated. | ||
| * | ||
| * The {@linkcode MessageExtraInfo.request | request} can be used to get the original request information (headers, etc.) | ||
| */ | ||
| onmessage?: (<T$1 extends JSONRPCMessage>(message: T$1, extra?: MessageExtraInfo) => void) | undefined; | ||
| /** | ||
| * The session ID generated for this connection. | ||
| */ | ||
| sessionId?: string | undefined; | ||
| /** | ||
| * Sets the protocol version used for the connection (called when the initialize response is received). | ||
| */ | ||
| setProtocolVersion?: ((version: string) => void) | undefined; | ||
| /** | ||
| * Sets the supported protocol versions for header validation (called during connect). | ||
| * This allows the server to pass its supported versions to the transport. | ||
| */ | ||
| setSupportedProtocolVersions?: ((versions: string[]) => void) | undefined; | ||
| } | ||
| //#endregion | ||
| //#region src/streamableHttp.d.ts | ||
| /** | ||
| * Configuration options for {@linkcode NodeStreamableHTTPServerTransport} | ||
@@ -220,0 +8,0 @@ * |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.mts","names":[],"sources":["../../../core/src/types/schemas.ts","../../../core/src/types/types.ts","../../../core/src/shared/transport.ts","../src/streamableHttp.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AG+ImB,cHtBN,eGsBM,EHtBS,CAAA,CAAA,QGsBT,CAAA,SAAA,CHtBS,CAAA,CAAA,SGsBT,EHtBS,CAAA,CAAA,SGsBT,CAAA,CAAA;cHoCN,sBAAoB,CAAA,CAAA,mBAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KCT5B,SAAA;KACA,eAAa,YAAU,YACtB,MACA,YAAU,iBACR,MAAM,QAAQ,MACd,YAAU,eACR,IAAI,QAAQ,MACZ,YAAU,wBACR,IAAI,QAAQ,IAAI,QAAQ,MACxB,mCACgB,MAAI,QAAQ,IAAE,QAC5B;KAET,qBAAqB,CAAA,CAAE,cAAc,QAAQ,CAAA,CAAE,MAAM;KAU9C,SAAA,GAAY,aAAa;KAMzB,cAAA,GAAiB,aAAa;;;;UAiNzB,QAAA;;;;;;;;;;;;;;;;;;;;;aAyBF;;;;;UAMH;;;;;UA+EK,gBAAA;;;;YAIH,UAAA,CAAW;;;;aAKV;;;;;;;;;;;;;;;;;KC7dH,oBAAA;;;;qBAIW;;;;;;;;;;;;;;;;;UAmBN,SAAA;;;;;;;;WAQJ;;;;;;gBAOK,0BAA0B,uBAAuB;;;;WAKtD;;;;;;;;;;;;qBAcU;;;;;;;;4BASK,yBAAyB,aAAW;;;;EF+D/B,SAAA,CAAA,EAAA,MAAA,GAAA,SAAA;EAAA;;;;ECT5B;AAAS;;;EAER,4BAAA,CAAA,EAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,EAAA,GAAA,IAAA,CAAA,GAAA,SAAA;;;;;;;;;KEvJM,oCAAA,GAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6CtC,iCAAA,YAA6C;;;;wBAMjC;;;;;;;;;;;;;gCAyCS;0BAIN;;;;EH8DK,IAAA,SAAA,CAAA,OAAA,EAAA,CAAA,CAAA,OAAA,EGvDK,cHuDL,EAAA,KAAA,CAAA,EGvD6B,gBHuD7B,EAAA,GAAA,IAAA,CAAA,GAAA,SAAA;EAAA,IAAA,SAAA,CAAA,CAAA,EAAA,CAAA,CAAA,OAAA,EGnDD,cHmDC,EAAA,KAAA,CAAA,EGnDuB,gBHmDvB,EAAA,GAAA,IAAA,CAAA,GAAA,SAAA;;;;ACX2F;EAGvH,KAAA,CAAA,CAAA,EEnCc,OFmCP,CAAA,IAAA,CAAA;EAAM;;;EAEZ,KAAA,CAAA,CAAA,EE9Ba,OF8Bb,CAAA,IAAA,CAAA;EAAU;;;EACR,IAAA,CAAA,OAAA,EExBgB,cFwBhB,EAAA,OACU,CADV,EAAA;IACA,gBAAA,CAAA,EEzB+D,SFyB/D;EAAU,CAAA,CAAA,EEzBmE,OFyBnE,CAAA,IAAA,CAAA;EACI;;;;;;;;;;EAGV,aAAA,CAAA,GAAA,EEfiB,eFejB,GAAA;IACgB,IAAA,CAAA,EEhB4B,QFgB5B;EAAY,CAAA,EAAA,GAAA,EEhBiC,cFgBjC,EAAA,UAAA,CAAA,EAAA,OAAA,CAAA,EEhBwE,OFgBxE,CAAA,IAAA,CAAA;EAAE;;;;AAC3B;EAEa,cAAA,CAAA,SAAA,EESE,SFTF,CAAA,EAAA,IAAA;EAA8B;;;;EAU9C,wBAAS,CAAA,CAAgB,EAAA,IAAA;AAMrC"} | ||
| {"version":3,"file":"index.d.mts","names":[],"sources":["../src/streamableHttp.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AA4JwB,KAjIZ,oCAAA,GAAuC,+CAiI3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cApFX,iCAAA,YAA6C;;;;wBAMjC;;;;;;;;;;;;;gCAyCS;0BAIN;;;;oCAOU,wBAAwB;8BAI9B,wBAAwB;;;;;WAQrC;;;;WAOA;;;;gBAOK;uBAA+C;MAAc;;;;;;;;;;;qBAcxD;WAA2B;UAAiB,uCAAuC;;;;;;4BA4BlF"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.mjs","names":[],"sources":["../src/streamableHttp.ts"],"sourcesContent":["/**\n * Node.js Streamable HTTP Server Transport\n *\n * This is a thin wrapper around {@linkcode WebStandardStreamableHTTPServerTransport} that provides\n * compatibility with Node.js HTTP server (`IncomingMessage`/`ServerResponse`).\n *\n * For web-standard environments (Cloudflare Workers, Deno, Bun), use {@linkcode WebStandardStreamableHTTPServerTransport} directly.\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport { getRequestListener } from '@hono/node-server';\nimport type { AuthInfo, JSONRPCMessage, MessageExtraInfo, RequestId, Transport } from '@modelcontextprotocol/core';\nimport type { WebStandardStreamableHTTPServerTransportOptions } from '@modelcontextprotocol/server';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';\n\n/**\n * Configuration options for {@linkcode NodeStreamableHTTPServerTransport}\n *\n * This is an alias for {@linkcode WebStandardStreamableHTTPServerTransportOptions} for backward compatibility.\n */\nexport type StreamableHTTPServerTransportOptions = WebStandardStreamableHTTPServerTransportOptions;\n\n/**\n * Server transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.\n * It supports both SSE streaming and direct HTTP responses.\n *\n * This is a wrapper around {@linkcode WebStandardStreamableHTTPServerTransport} that provides Node.js HTTP compatibility.\n * It uses the `@hono/node-server` library to convert between Node.js HTTP and Web Standard APIs.\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#NodeStreamableHTTPServerTransport_stateful\"\n * const server = new McpServer({ name: 'my-server', version: '1.0.0' });\n *\n * const transport = new NodeStreamableHTTPServerTransport({\n * sessionIdGenerator: () => randomUUID()\n * });\n *\n * await server.connect(transport);\n * ```\n *\n * @example Stateless setup\n * ```ts source=\"./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_stateless\"\n * const transport = new NodeStreamableHTTPServerTransport({\n * sessionIdGenerator: undefined\n * });\n * ```\n *\n * @example Using with a pre-parsed request body (e.g. Express)\n * ```ts source=\"./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_express\"\n * app.post('/mcp', (req, res) => {\n * transport.handleRequest(req, res, req.body);\n * });\n * ```\n */\nexport class NodeStreamableHTTPServerTransport implements Transport {\n private _webStandardTransport: WebStandardStreamableHTTPServerTransport;\n private _requestListener: ReturnType<typeof getRequestListener>;\n // Store auth and parsedBody per request for passing through to handleRequest\n private _requestContext: WeakMap<Request, { authInfo?: AuthInfo; parsedBody?: unknown }> = new WeakMap();\n\n constructor(options: StreamableHTTPServerTransportOptions = {}) {\n this._webStandardTransport = new WebStandardStreamableHTTPServerTransport(options);\n\n // Create a request listener that wraps the web standard transport\n // getRequestListener converts Node.js HTTP to Web Standard and properly handles SSE streaming\n // overrideGlobalObjects: false prevents Hono from overwriting global Response, which would\n // break frameworks like Next.js whose response classes extend the native Response\n this._requestListener = getRequestListener(\n async (webRequest: Request) => {\n // Get context if available (set during handleRequest)\n const context = this._requestContext.get(webRequest);\n return this._webStandardTransport.handleRequest(webRequest, {\n authInfo: context?.authInfo,\n parsedBody: context?.parsedBody\n });\n },\n { overrideGlobalObjects: false }\n );\n }\n\n /**\n * Gets the session ID for this transport instance.\n */\n get sessionId(): string | undefined {\n return this._webStandardTransport.sessionId;\n }\n\n /**\n * Sets callback for when the transport is closed.\n */\n set onclose(handler: (() => void) | undefined) {\n this._webStandardTransport.onclose = handler;\n }\n\n get onclose(): (() => void) | undefined {\n return this._webStandardTransport.onclose;\n }\n\n /**\n * Sets callback for transport errors.\n */\n set onerror(handler: ((error: Error) => void) | undefined) {\n this._webStandardTransport.onerror = handler;\n }\n\n get onerror(): ((error: Error) => void) | undefined {\n return this._webStandardTransport.onerror;\n }\n\n /**\n * Sets callback for incoming messages.\n */\n set onmessage(handler: ((message: JSONRPCMessage, extra?: MessageExtraInfo) => void) | undefined) {\n this._webStandardTransport.onmessage = handler;\n }\n\n get onmessage(): ((message: JSONRPCMessage, extra?: MessageExtraInfo) => void) | undefined {\n return this._webStandardTransport.onmessage;\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<void> {\n return this._webStandardTransport.start();\n }\n\n /**\n * Closes the transport and all active connections.\n */\n async close(): Promise<void> {\n return this._webStandardTransport.close();\n }\n\n /**\n * Sends a JSON-RPC message through the transport.\n */\n async send(message: JSONRPCMessage, options?: { relatedRequestId?: RequestId }): Promise<void> {\n return this._webStandardTransport.send(message, options);\n }\n\n /**\n * Handles an incoming HTTP request, whether `GET` or `POST`.\n *\n * This method converts Node.js HTTP objects to Web Standard Request/Response\n * and delegates to the underlying {@linkcode WebStandardStreamableHTTPServerTransport}.\n *\n * @param req - Node.js `IncomingMessage`, optionally with `auth` property from middleware\n * @param res - Node.js `ServerResponse`\n * @param parsedBody - Optional pre-parsed body from body-parser middleware\n */\n async handleRequest(req: IncomingMessage & { auth?: AuthInfo }, res: ServerResponse, parsedBody?: unknown): Promise<void> {\n // Store context for this request to pass through auth and parsedBody\n // We need to intercept the request creation to attach this context\n const authInfo = req.auth;\n\n // Create a custom handler that includes our context\n // overrideGlobalObjects: false prevents Hono from overwriting global Response, which would\n // break frameworks like Next.js whose response classes extend the native Response\n const handler = getRequestListener(\n async (webRequest: Request) => {\n return this._webStandardTransport.handleRequest(webRequest, {\n authInfo,\n parsedBody\n });\n },\n { overrideGlobalObjects: false }\n );\n\n // Delegate to the request listener which handles all the Node.js <-> Web Standard conversion\n // including proper SSE streaming support\n await handler(req, res);\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 this._webStandardTransport.closeSSEStream(requestId);\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 this._webStandardTransport.closeStandaloneSSEStream();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,IAAa,oCAAb,MAAoE;CAChE,AAAQ;CACR,AAAQ;CAER,AAAQ,kCAAmF,IAAI,SAAS;CAExG,YAAY,UAAgD,EAAE,EAAE;AAC5D,OAAK,wBAAwB,IAAI,yCAAyC,QAAQ;AAMlF,OAAK,mBAAmB,mBACpB,OAAO,eAAwB;GAE3B,MAAM,UAAU,KAAK,gBAAgB,IAAI,WAAW;AACpD,UAAO,KAAK,sBAAsB,cAAc,YAAY;IACxD,UAAU,SAAS;IACnB,YAAY,SAAS;IACxB,CAAC;KAEN,EAAE,uBAAuB,OAAO,CACnC;;;;;CAML,IAAI,YAAgC;AAChC,SAAO,KAAK,sBAAsB;;;;;CAMtC,IAAI,QAAQ,SAAmC;AAC3C,OAAK,sBAAsB,UAAU;;CAGzC,IAAI,UAAoC;AACpC,SAAO,KAAK,sBAAsB;;;;;CAMtC,IAAI,QAAQ,SAA+C;AACvD,OAAK,sBAAsB,UAAU;;CAGzC,IAAI,UAAgD;AAChD,SAAO,KAAK,sBAAsB;;;;;CAMtC,IAAI,UAAU,SAAoF;AAC9F,OAAK,sBAAsB,YAAY;;CAG3C,IAAI,YAAuF;AACvF,SAAO,KAAK,sBAAsB;;;;;;CAOtC,MAAM,QAAuB;AACzB,SAAO,KAAK,sBAAsB,OAAO;;;;;CAM7C,MAAM,QAAuB;AACzB,SAAO,KAAK,sBAAsB,OAAO;;;;;CAM7C,MAAM,KAAK,SAAyB,SAA2D;AAC3F,SAAO,KAAK,sBAAsB,KAAK,SAAS,QAAQ;;;;;;;;;;;;CAa5D,MAAM,cAAc,KAA4C,KAAqB,YAAqC;EAGtH,MAAM,WAAW,IAAI;AAiBrB,QAZgB,mBACZ,OAAO,eAAwB;AAC3B,UAAO,KAAK,sBAAsB,cAAc,YAAY;IACxD;IACA;IACH,CAAC;KAEN,EAAE,uBAAuB,OAAO,CACnC,CAIa,KAAK,IAAI;;;;;;;CAQ3B,eAAe,WAA4B;AACvC,OAAK,sBAAsB,eAAe,UAAU;;;;;;CAOxD,2BAAiC;AAC7B,OAAK,sBAAsB,0BAA0B"} | ||
| {"version":3,"file":"index.mjs","names":[],"sources":["../src/streamableHttp.ts"],"sourcesContent":["/**\n * Node.js Streamable HTTP Server Transport\n *\n * This is a thin wrapper around {@linkcode WebStandardStreamableHTTPServerTransport} that provides\n * compatibility with Node.js HTTP server (`IncomingMessage`/`ServerResponse`).\n *\n * For web-standard environments (Cloudflare Workers, Deno, Bun), use {@linkcode WebStandardStreamableHTTPServerTransport} directly.\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport { getRequestListener } from '@hono/node-server';\nimport type {\n AuthInfo,\n JSONRPCMessage,\n MessageExtraInfo,\n RequestId,\n Transport,\n WebStandardStreamableHTTPServerTransportOptions\n} from '@modelcontextprotocol/server';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';\n\n/**\n * Configuration options for {@linkcode NodeStreamableHTTPServerTransport}\n *\n * This is an alias for {@linkcode WebStandardStreamableHTTPServerTransportOptions} for backward compatibility.\n */\nexport type StreamableHTTPServerTransportOptions = WebStandardStreamableHTTPServerTransportOptions;\n\n/**\n * Server transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.\n * It supports both SSE streaming and direct HTTP responses.\n *\n * This is a wrapper around {@linkcode WebStandardStreamableHTTPServerTransport} that provides Node.js HTTP compatibility.\n * It uses the `@hono/node-server` library to convert between Node.js HTTP and Web Standard APIs.\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#NodeStreamableHTTPServerTransport_stateful\"\n * const server = new McpServer({ name: 'my-server', version: '1.0.0' });\n *\n * const transport = new NodeStreamableHTTPServerTransport({\n * sessionIdGenerator: () => randomUUID()\n * });\n *\n * await server.connect(transport);\n * ```\n *\n * @example Stateless setup\n * ```ts source=\"./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_stateless\"\n * const transport = new NodeStreamableHTTPServerTransport({\n * sessionIdGenerator: undefined\n * });\n * ```\n *\n * @example Using with a pre-parsed request body (e.g. Express)\n * ```ts source=\"./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_express\"\n * app.post('/mcp', (req, res) => {\n * transport.handleRequest(req, res, req.body);\n * });\n * ```\n */\nexport class NodeStreamableHTTPServerTransport implements Transport {\n private _webStandardTransport: WebStandardStreamableHTTPServerTransport;\n private _requestListener: ReturnType<typeof getRequestListener>;\n // Store auth and parsedBody per request for passing through to handleRequest\n private _requestContext: WeakMap<Request, { authInfo?: AuthInfo; parsedBody?: unknown }> = new WeakMap();\n\n constructor(options: StreamableHTTPServerTransportOptions = {}) {\n this._webStandardTransport = new WebStandardStreamableHTTPServerTransport(options);\n\n // Create a request listener that wraps the web standard transport\n // getRequestListener converts Node.js HTTP to Web Standard and properly handles SSE streaming\n // overrideGlobalObjects: false prevents Hono from overwriting global Response, which would\n // break frameworks like Next.js whose response classes extend the native Response\n this._requestListener = getRequestListener(\n async (webRequest: Request) => {\n // Get context if available (set during handleRequest)\n const context = this._requestContext.get(webRequest);\n return this._webStandardTransport.handleRequest(webRequest, {\n authInfo: context?.authInfo,\n parsedBody: context?.parsedBody\n });\n },\n { overrideGlobalObjects: false }\n );\n }\n\n /**\n * Gets the session ID for this transport instance.\n */\n get sessionId(): string | undefined {\n return this._webStandardTransport.sessionId;\n }\n\n /**\n * Sets callback for when the transport is closed.\n */\n set onclose(handler: (() => void) | undefined) {\n this._webStandardTransport.onclose = handler;\n }\n\n get onclose(): (() => void) | undefined {\n return this._webStandardTransport.onclose;\n }\n\n /**\n * Sets callback for transport errors.\n */\n set onerror(handler: ((error: Error) => void) | undefined) {\n this._webStandardTransport.onerror = handler;\n }\n\n get onerror(): ((error: Error) => void) | undefined {\n return this._webStandardTransport.onerror;\n }\n\n /**\n * Sets callback for incoming messages.\n */\n set onmessage(handler: ((message: JSONRPCMessage, extra?: MessageExtraInfo) => void) | undefined) {\n this._webStandardTransport.onmessage = handler;\n }\n\n get onmessage(): ((message: JSONRPCMessage, extra?: MessageExtraInfo) => void) | undefined {\n return this._webStandardTransport.onmessage;\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<void> {\n return this._webStandardTransport.start();\n }\n\n /**\n * Closes the transport and all active connections.\n */\n async close(): Promise<void> {\n return this._webStandardTransport.close();\n }\n\n /**\n * Sends a JSON-RPC message through the transport.\n */\n async send(message: JSONRPCMessage, options?: { relatedRequestId?: RequestId }): Promise<void> {\n return this._webStandardTransport.send(message, options);\n }\n\n /**\n * Handles an incoming HTTP request, whether `GET` or `POST`.\n *\n * This method converts Node.js HTTP objects to Web Standard Request/Response\n * and delegates to the underlying {@linkcode WebStandardStreamableHTTPServerTransport}.\n *\n * @param req - Node.js `IncomingMessage`, optionally with `auth` property from middleware\n * @param res - Node.js `ServerResponse`\n * @param parsedBody - Optional pre-parsed body from body-parser middleware\n */\n async handleRequest(req: IncomingMessage & { auth?: AuthInfo }, res: ServerResponse, parsedBody?: unknown): Promise<void> {\n // Store context for this request to pass through auth and parsedBody\n // We need to intercept the request creation to attach this context\n const authInfo = req.auth;\n\n // Create a custom handler that includes our context\n // overrideGlobalObjects: false prevents Hono from overwriting global Response, which would\n // break frameworks like Next.js whose response classes extend the native Response\n const handler = getRequestListener(\n async (webRequest: Request) => {\n return this._webStandardTransport.handleRequest(webRequest, {\n authInfo,\n parsedBody\n });\n },\n { overrideGlobalObjects: false }\n );\n\n // Delegate to the request listener which handles all the Node.js <-> Web Standard conversion\n // including proper SSE streaming support\n await handler(req, res);\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 this._webStandardTransport.closeSSEStream(requestId);\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 this._webStandardTransport.closeStandaloneSSEStream();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,IAAa,oCAAb,MAAoE;CAChE,AAAQ;CACR,AAAQ;CAER,AAAQ,kCAAmF,IAAI,SAAS;CAExG,YAAY,UAAgD,EAAE,EAAE;AAC5D,OAAK,wBAAwB,IAAI,yCAAyC,QAAQ;AAMlF,OAAK,mBAAmB,mBACpB,OAAO,eAAwB;GAE3B,MAAM,UAAU,KAAK,gBAAgB,IAAI,WAAW;AACpD,UAAO,KAAK,sBAAsB,cAAc,YAAY;IACxD,UAAU,SAAS;IACnB,YAAY,SAAS;IACxB,CAAC;KAEN,EAAE,uBAAuB,OAAO,CACnC;;;;;CAML,IAAI,YAAgC;AAChC,SAAO,KAAK,sBAAsB;;;;;CAMtC,IAAI,QAAQ,SAAmC;AAC3C,OAAK,sBAAsB,UAAU;;CAGzC,IAAI,UAAoC;AACpC,SAAO,KAAK,sBAAsB;;;;;CAMtC,IAAI,QAAQ,SAA+C;AACvD,OAAK,sBAAsB,UAAU;;CAGzC,IAAI,UAAgD;AAChD,SAAO,KAAK,sBAAsB;;;;;CAMtC,IAAI,UAAU,SAAoF;AAC9F,OAAK,sBAAsB,YAAY;;CAG3C,IAAI,YAAuF;AACvF,SAAO,KAAK,sBAAsB;;;;;;CAOtC,MAAM,QAAuB;AACzB,SAAO,KAAK,sBAAsB,OAAO;;;;;CAM7C,MAAM,QAAuB;AACzB,SAAO,KAAK,sBAAsB,OAAO;;;;;CAM7C,MAAM,KAAK,SAAyB,SAA2D;AAC3F,SAAO,KAAK,sBAAsB,KAAK,SAAS,QAAQ;;;;;;;;;;;;CAa5D,MAAM,cAAc,KAA4C,KAAqB,YAAqC;EAGtH,MAAM,WAAW,IAAI;AAiBrB,QAZgB,mBACZ,OAAO,eAAwB;AAC3B,UAAO,KAAK,sBAAsB,cAAc,YAAY;IACxD;IACA;IACH,CAAC;KAEN,EAAE,uBAAuB,OAAO,CACnC,CAIa,KAAK,IAAI;;;;;;;CAQ3B,eAAe,WAA4B;AACvC,OAAK,sBAAsB,eAAe,UAAU;;;;;;CAOxD,2BAAiC;AAC7B,OAAK,sBAAsB,0BAA0B"} |
+18
-8
| { | ||
| "name": "@modelcontextprotocol/node", | ||
| "version": "2.0.0-alpha.2", | ||
| "version": "2.0.0-alpha.3", | ||
| "description": "Model Context Protocol implementation for TypeScript - Node.js middleware", | ||
@@ -29,2 +29,10 @@ "license": "MIT", | ||
| }, | ||
| "types": "./dist/index.d.mts", | ||
| "typesVersions": { | ||
| "*": { | ||
| "sse": [ | ||
| "dist/sse.d.mts" | ||
| ] | ||
| } | ||
| }, | ||
| "files": [ | ||
@@ -38,4 +46,9 @@ "dist" | ||
| "hono": "^4.11.4", | ||
| "@modelcontextprotocol/server": "^2.0.0-alpha.2" | ||
| "@modelcontextprotocol/server": "^2.0.0-alpha.3" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "hono": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "devDependencies": { | ||
@@ -49,8 +62,7 @@ "@eslint/js": "^9.39.2", | ||
| "tsdown": "^0.18.0", | ||
| "tsx": "^4.16.5", | ||
| "typescript": "^5.9.3", | ||
| "typescript-eslint": "^8.48.1", | ||
| "vitest": "^4.0.15", | ||
| "@modelcontextprotocol/server": "^2.0.0-alpha.2", | ||
| "@modelcontextprotocol/core": "^2.0.0-alpha.1", | ||
| "@modelcontextprotocol/server": "^2.0.0-alpha.3", | ||
| "@modelcontextprotocol/core-internal": "^2.0.0-alpha.2", | ||
| "@modelcontextprotocol/eslint-config": "^2.0.0", | ||
@@ -69,6 +81,4 @@ "@modelcontextprotocol/test-helpers": "^2.0.0-alpha.0", | ||
| "test": "vitest run", | ||
| "test:watch": "vitest", | ||
| "server": "tsx watch --clear-screen=false scripts/cli.ts server", | ||
| "client": "tsx scripts/cli.ts client" | ||
| "test:watch": "vitest" | ||
| } | ||
| } |
16
-5.88%36639
-22.19%