/** * Streamable HTTP Transport for MCP * * Implements the MCP Streamable HTTP transport specification (2025-06-18). * https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http * * Features: * - Single MCP endpoint supporting both POST and GET * - POST for sending messages to server * - GET for SSE streams from server * - Session management with Mcp-Session-Id header * - Resumability support with Last-Event-ID * - Multiple concurrent client connections * - Protocol version header support */ import { Express, Request, Response } from 'express'; import type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js'; import { Tool as McpTool } from '@modelcontextprotocol/sdk/types.js'; /** * Mutable per-session context shared between the transport and the MCP server * instance handling that session. The transport refreshes it on each HTTP * request; MCP request handlers read it when building the tool * ExecutionContext, which is how the HTTP Authorization header reaches * OAuth-aware guards. */ export interface SessionContext { /** Raw HTTP Authorization header from the most recent request on this session. */ authHeader?: string; /** Active session ID */ sessionId?: string; /** Authenticated user ID */ userId?: string; /** Authenticated tenant ID */ tenantId?: string; } /** * Factory that builds a fully-configured MCP server instance. * Each Streamable HTTP session gets its own server, since an SDK server can * only be connected to a single transport at a time. */ export type McpServerFactory = (sessionContext?: SessionContext) => McpServer; /** Handles legacy HTTP+SSE clients that open GET without a Streamable HTTP session id (e.g. Cursor). */ export type LegacySseHandler = (req: Request, res: Response) => Promise; export interface StreamableHttpTransportOptions { /** * Port to listen on (default: 3000) */ port?: number; /** * Host to bind to (default: 'localhost' for security) */ host?: string; /** * MCP endpoint path (default: '/mcp') */ endpoint?: string; /** * Enable session management (default: true) */ enableSessions?: boolean; /** * Session timeout in ms (default: 30 minutes) * Idle sessions are torn down after this period of inactivity. */ sessionTimeout?: number; /** * Maximum number of concurrent MCP sessions (default: 1000). * Once reached, new `initialize` requests are rejected with HTTP 429 to * bound memory usage against unauthenticated session-creation floods. */ maxSessions?: number; /** * Custom Express app (optional) */ app?: Express; /** * Enable CORS (default: false for security) */ enableCors?: boolean; } /** * Streamable HTTP host. * * This class owns the Express application (CORS, origin checks, OAuth discovery * routes, health, documentation page, legacy-SSE lifecycle) but delegates the * actual MCP protocol handling on `/mcp` to the official * `@modelcontextprotocol/sdk` `StreamableHTTPServerTransport`. That transport * returns the JSON-RPC result on the POST response (or an SSE stream on the same * request), which is what modern MCP clients such as Cursor expect. */ export declare class StreamableHttpTransport { private app; private server; private options; private getToolsCallback?; private serverConfig?; private logoBase64?; private _routesRegistered; private mcpServerFactory?; private legacySseHandler?; /** * When set, the MCP endpoint (POST/GET/DELETE) is delegated to this handler * instead of the legacy per-session SDK path. Used by the modern * (2026-07-28) protocol adapter, which serves stateless requests itself. */ private modernHandler?; /** Protocol revision advertised on the health endpoint and startup logs. */ private protocolVersionLabel; private mcpSessions; private pendingSessions; private sessionCleanupInterval?; /** Grace period for a created session to finish `initialize` before it is reaped. */ private static readonly PENDING_SESSION_TIMEOUT_MS; constructor(options?: StreamableHttpTransportOptions); /** * Provide the factory used to build a configured MCP server per session. * Must be called before `start()`. */ setMcpServerFactory(factory: McpServerFactory): void; /** * Fallback for clients that speak legacy HTTP+SSE on GET /mcp (no mcp-session-id). * Streamable HTTP clients always begin with POST initialize instead. */ setLegacySseHandler(handler: LegacySseHandler): void; /** * Delegate the MCP endpoint to a modern (2026-07-28) stateless handler * instead of the legacy per-session SDK path. Must be called before `start()`. */ setModernHandler(handler: (req: Request, res: Response) => void): void; /** * Set the protocol revision label used on `/mcp/health` and startup logs. */ setProtocolVersionLabel(label: string): void; /** * Load logo image as base64 for embedding in documentation page */ private loadLogo; /** * Setup Express middleware */ private setupMiddleware; /** * Setup MCP endpoint routes */ private setupRoutes; /** * Delegate an `/mcp` request (POST/GET/DELETE) to the official SDK * Streamable HTTP transport. A new session (and its own MCP server instance) * is created on an `initialize` POST; subsequent requests are routed by the * `mcp-session-id` header. */ private handleMcpRequest; /** * Create a new MCP session: a fresh configured MCP server connected to a new * official Streamable HTTP transport. The transport registers itself in the * session map once it has negotiated a session id, and removes itself on close. */ private createSession; /** * Tear down a session, removing it from both the live and pending collections * and closing its transport and server. Safe to call from the sweeper, request * error paths, and shutdown. */ private destroySession; /** * Periodically tear down sessions that have been idle longer than * `sessionTimeout`. Prevents unbounded memory growth from clients that * initialize but never disconnect or send DELETE. */ private startSessionCleanup; /** * Start the HTTP server */ start(): Promise; /** * Register additional HTTP routes * Allows modules (like OAuthModule) to add custom endpoints */ on(path: string, handler: (req: any, res: any) => void): void; /** * Close the transport: tear down all live MCP sessions and the HTTP server. */ close(): Promise; /** * Whether a parsed JSON-RPC body is an `initialize` request. */ private isInitializeRequest; private isLocalhost; /** * Get the Express app (for adding custom routes) */ getApp(): Express; /** * Set callback to get tools list for documentation page */ setToolsCallback(callback: () => Promise): void; /** * Set server configuration for documentation page */ setServerConfig(config: { name: string; version: string; description?: string; }): void; /** * Generate HTML documentation page */ private generateDocumentationPage; /** * Escape HTML to prevent XSS */ private escapeHtml; } //# sourceMappingURL=streamable-http.d.ts.map