webStandardStreamableHttp.d.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /**
  2. * Web Standards Streamable HTTP Server Transport
  3. *
  4. * This is the core transport implementation using Web Standard APIs (Request, Response, ReadableStream).
  5. * It can run on any runtime that supports Web Standards: Node.js 18+, Cloudflare Workers, Deno, Bun, etc.
  6. *
  7. * For Node.js Express/HTTP compatibility, use `StreamableHTTPServerTransport` which wraps this transport.
  8. */
  9. import { Transport } from '../shared/transport.js';
  10. import { AuthInfo } from './auth/types.js';
  11. import { MessageExtraInfo, JSONRPCMessage, RequestId } from '../types.js';
  12. export type StreamId = string;
  13. export type EventId = string;
  14. /**
  15. * Interface for resumability support via event storage
  16. */
  17. export interface EventStore {
  18. /**
  19. * Stores an event for later retrieval
  20. * @param streamId ID of the stream the event belongs to
  21. * @param message The JSON-RPC message to store
  22. * @returns The generated event ID for the stored event
  23. */
  24. storeEvent(streamId: StreamId, message: JSONRPCMessage): Promise<EventId>;
  25. /**
  26. * Get the stream ID associated with a given event ID.
  27. * @param eventId The event ID to look up
  28. * @returns The stream ID, or undefined if not found
  29. *
  30. * Optional: If not provided, the SDK will use the streamId returned by
  31. * replayEventsAfter for stream mapping.
  32. */
  33. getStreamIdForEventId?(eventId: EventId): Promise<StreamId | undefined>;
  34. replayEventsAfter(lastEventId: EventId, { send }: {
  35. send: (eventId: EventId, message: JSONRPCMessage) => Promise<void>;
  36. }): Promise<StreamId>;
  37. }
  38. /**
  39. * Configuration options for WebStandardStreamableHTTPServerTransport
  40. */
  41. export interface WebStandardStreamableHTTPServerTransportOptions {
  42. /**
  43. * Function that generates a session ID for the transport.
  44. * The session ID SHOULD be globally unique and cryptographically secure (e.g., a securely generated UUID, a JWT, or a cryptographic hash)
  45. *
  46. * If not provided, session management is disabled (stateless mode).
  47. */
  48. sessionIdGenerator?: () => string;
  49. /**
  50. * A callback for session initialization events
  51. * This is called when the server initializes a new session.
  52. * Useful in cases when you need to register multiple mcp sessions
  53. * and need to keep track of them.
  54. * @param sessionId The generated session ID
  55. */
  56. onsessioninitialized?: (sessionId: string) => void | Promise<void>;
  57. /**
  58. * A callback for session close events
  59. * This is called when the server closes a session due to a DELETE request.
  60. * Useful in cases when you need to clean up resources associated with the session.
  61. * Note that this is different from the transport closing, if you are handling
  62. * HTTP requests from multiple nodes you might want to close each
  63. * WebStandardStreamableHTTPServerTransport after a request is completed while still keeping the
  64. * session open/running.
  65. * @param sessionId The session ID that was closed
  66. */
  67. onsessionclosed?: (sessionId: string) => void | Promise<void>;
  68. /**
  69. * If true, the server will return JSON responses instead of starting an SSE stream.
  70. * This can be useful for simple request/response scenarios without streaming.
  71. * Default is false (SSE streams are preferred).
  72. */
  73. enableJsonResponse?: boolean;
  74. /**
  75. * Event store for resumability support
  76. * If provided, resumability will be enabled, allowing clients to reconnect and resume messages
  77. */
  78. eventStore?: EventStore;
  79. /**
  80. * List of allowed host header values for DNS rebinding protection.
  81. * If not specified, host validation is disabled.
  82. * @deprecated Use external middleware for host validation instead.
  83. */
  84. allowedHosts?: string[];
  85. /**
  86. * List of allowed origin header values for DNS rebinding protection.
  87. * If not specified, origin validation is disabled.
  88. * @deprecated Use external middleware for origin validation instead.
  89. */
  90. allowedOrigins?: string[];
  91. /**
  92. * Enable DNS rebinding protection (requires allowedHosts and/or allowedOrigins to be configured).
  93. * Default is false for backwards compatibility.
  94. * @deprecated Use external middleware for DNS rebinding protection instead.
  95. */
  96. enableDnsRebindingProtection?: boolean;
  97. /**
  98. * Retry interval in milliseconds to suggest to clients in SSE retry field.
  99. * When set, the server will send a retry field in SSE priming events to control
  100. * client reconnection timing for polling behavior.
  101. */
  102. retryInterval?: number;
  103. }
  104. /**
  105. * Options for handling a request
  106. */
  107. export interface HandleRequestOptions {
  108. /**
  109. * Pre-parsed request body. If provided, the transport will use this instead of parsing req.json().
  110. * Useful when using body-parser middleware that has already parsed the body.
  111. */
  112. parsedBody?: unknown;
  113. /**
  114. * Authentication info from middleware. If provided, will be passed to message handlers.
  115. */
  116. authInfo?: AuthInfo;
  117. }
  118. /**
  119. * Server transport for Web Standards Streamable HTTP: this implements the MCP Streamable HTTP transport specification
  120. * using Web Standard APIs (Request, Response, ReadableStream).
  121. *
  122. * This transport works on any runtime that supports Web Standards: Node.js 18+, Cloudflare Workers, Deno, Bun, etc.
  123. *
  124. * Usage example:
  125. *
  126. * ```typescript
  127. * // Stateful mode - server sets the session ID
  128. * const statefulTransport = new WebStandardStreamableHTTPServerTransport({
  129. * sessionIdGenerator: () => crypto.randomUUID(),
  130. * });
  131. *
  132. * // Stateless mode - explicitly set session ID to undefined
  133. * const statelessTransport = new WebStandardStreamableHTTPServerTransport({
  134. * sessionIdGenerator: undefined,
  135. * });
  136. *
  137. * // Hono.js usage
  138. * app.all('/mcp', async (c) => {
  139. * return transport.handleRequest(c.req.raw);
  140. * });
  141. *
  142. * // Cloudflare Workers usage
  143. * export default {
  144. * async fetch(request: Request): Promise<Response> {
  145. * return transport.handleRequest(request);
  146. * }
  147. * };
  148. * ```
  149. *
  150. * In stateful mode:
  151. * - Session ID is generated and included in response headers
  152. * - Session ID is always included in initialization responses
  153. * - Requests with invalid session IDs are rejected with 404 Not Found
  154. * - Non-initialization requests without a session ID are rejected with 400 Bad Request
  155. * - State is maintained in-memory (connections, message history)
  156. *
  157. * In stateless mode:
  158. * - No Session ID is included in any responses
  159. * - No session validation is performed
  160. */
  161. export declare class WebStandardStreamableHTTPServerTransport implements Transport {
  162. private sessionIdGenerator;
  163. private _started;
  164. private _hasHandledRequest;
  165. private _streamMapping;
  166. private _requestToStreamMapping;
  167. private _requestResponseMap;
  168. private _initialized;
  169. private _enableJsonResponse;
  170. private _standaloneSseStreamId;
  171. private _eventStore?;
  172. private _onsessioninitialized?;
  173. private _onsessionclosed?;
  174. private _allowedHosts?;
  175. private _allowedOrigins?;
  176. private _enableDnsRebindingProtection;
  177. private _retryInterval?;
  178. sessionId?: string;
  179. onclose?: () => void;
  180. onerror?: (error: Error) => void;
  181. onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;
  182. constructor(options?: WebStandardStreamableHTTPServerTransportOptions);
  183. /**
  184. * Starts the transport. This is required by the Transport interface but is a no-op
  185. * for the Streamable HTTP transport as connections are managed per-request.
  186. */
  187. start(): Promise<void>;
  188. /**
  189. * Helper to create a JSON error response
  190. */
  191. private createJsonErrorResponse;
  192. /**
  193. * Validates request headers for DNS rebinding protection.
  194. * @returns Error response if validation fails, undefined if validation passes.
  195. */
  196. private validateRequestHeaders;
  197. /**
  198. * Handles an incoming HTTP request, whether GET, POST, or DELETE
  199. * Returns a Response object (Web Standard)
  200. */
  201. handleRequest(req: Request, options?: HandleRequestOptions): Promise<Response>;
  202. /**
  203. * Writes a priming event to establish resumption capability.
  204. * Only sends if eventStore is configured (opt-in for resumability) and
  205. * the client's protocol version supports empty SSE data (>= 2025-11-25).
  206. */
  207. private writePrimingEvent;
  208. /**
  209. * Handles GET requests for SSE stream
  210. */
  211. private handleGetRequest;
  212. /**
  213. * Replays events that would have been sent after the specified event ID
  214. * Only used when resumability is enabled
  215. */
  216. private replayEvents;
  217. /**
  218. * Writes an event to an SSE stream via controller with proper formatting
  219. */
  220. private writeSSEEvent;
  221. /**
  222. * Handles unsupported requests (PUT, PATCH, etc.)
  223. */
  224. private handleUnsupportedRequest;
  225. /**
  226. * Handles POST requests containing JSON-RPC messages
  227. */
  228. private handlePostRequest;
  229. /**
  230. * Handles DELETE requests to terminate sessions
  231. */
  232. private handleDeleteRequest;
  233. /**
  234. * Validates session ID for non-initialization requests.
  235. * Returns Response error if invalid, undefined otherwise
  236. */
  237. private validateSession;
  238. /**
  239. * Validates the MCP-Protocol-Version header on incoming requests.
  240. *
  241. * For initialization: Version negotiation handles unknown versions gracefully
  242. * (server responds with its supported version).
  243. *
  244. * For subsequent requests with MCP-Protocol-Version header:
  245. * - Accept if in supported list
  246. * - 400 if unsupported
  247. *
  248. * For HTTP requests without the MCP-Protocol-Version header:
  249. * - Accept and default to the version negotiated at initialization
  250. */
  251. private validateProtocolVersion;
  252. close(): Promise<void>;
  253. /**
  254. * Close an SSE stream for a specific request, triggering client reconnection.
  255. * Use this to implement polling behavior during long-running operations -
  256. * client will reconnect after the retry interval specified in the priming event.
  257. */
  258. closeSSEStream(requestId: RequestId): void;
  259. /**
  260. * Close the standalone GET SSE stream, triggering client reconnection.
  261. * Use this to implement polling behavior for server-initiated notifications.
  262. */
  263. closeStandaloneSSEStream(): void;
  264. send(message: JSONRPCMessage, options?: {
  265. relatedRequestId?: RequestId;
  266. }): Promise<void>;
  267. }
  268. //# sourceMappingURL=webStandardStreamableHttp.d.ts.map