import type { MCPJsonRpcMessage, MCPStreamableHttpTransportConfig, MCPTransport, MCPTransportSendOptions } from '../../types/connector/index.js'; import { type Logger } from '../../utils/logger.js'; export declare class StreamableHttpTransport implements MCPTransport { private readonly config; private messageHandlers; private closeHandlers; private errorHandlers; private connected; private sessionId; /** * The most recent SSE event `id` this transport has seen, if any. * * Legacy-only in effect, never in name: this transport only ever holds a * `sessionId` on a legacy connection (a modern connection has no * `initialize` reply to capture one from — `MCPClient` probes with * `server/discover` instead), and {@link buildHeaders} sends * `Last-Event-ID` only alongside a session id. Deliberately NOT cleared * by `close()`: the whole point is arming the header for the request * that follows a reconnect, once a fresh session exists to carry it. */ private lastEventId; private generation; private activeSends; private log; private readonly timeoutMs; /** Defaults to the ambient global `fetch`; never read again once captured. */ private readonly fetchImpl; constructor(config: MCPStreamableHttpTransportConfig, log?: Logger); connect(): Promise; close(): Promise; /** * Forget the session this transport has been attaching to requests, * without otherwise disturbing the connection. * * Used by `MCPClient`'s legacy session-recovery path: a `404` on a * request means the server has forgotten this session, and the spec's * remedy is a fresh `initialize` sent with no session id attached — which * only happens if this transport stops sending the stale one first. */ resetSession(): void; /** Whether this transport is currently attaching a session id to its requests. */ hasSession(): boolean; /** * Tell the peer this session is done, without making `close()` wait on * the answer. * * A SHOULD, not a MUST: it lets a cooperative server free resources * promptly instead of waiting out its own idle timeout, but a server that * never hears it is no worse off than before this existed. Fire-and-forget * on purpose — `close()`'s existing bounded-teardown guarantee must not * grow a dependency on a round trip to a peer that may already be gone — * and bounded by its own short timeout so a peer that never answers does * not leave a request open indefinitely. A modern origin never reaches * this: it never had a session id to send in the first place. */ private sendSessionDelete; /** See {@link StdioTransport} — the same append-only handler leak. */ private clearHandlers; send(message: MCPJsonRpcMessage, options?: MCPTransportSendOptions): Promise; onMessage(handler: (message: MCPJsonRpcMessage) => void): void; onClose(handler: () => void): void; onError(handler: (error: Error) => void): void; isConnected(): boolean; /** * `extra` comes from `MCPTransportSendOptions.headers` — the client's * per-send authority, `MCP-Protocol-Version` today — and is merged over * this transport's own static config headers so a caller's value wins * on a collision. `Mcp-Session-Id` is applied after both: it is * transport-managed state a caller cannot see to conflict with. */ private buildHeaders; private captureSessionId; private beginSend; private assertCurrent; private dispatchResponseMessages; /** * Parse an SSE body and remember the newest event `id` it carried, if * any. * * The id survives past this one call — see the `lastEventId` field's own * doc comment — so it is available to arm `Last-Event-ID` on whatever * request follows a later reconnect. */ private parseSseAndCaptureEventId; } /** What one SSE-formatted Streamable HTTP response body parsed into. */ export interface MCPSseParseResult { readonly messages: MCPJsonRpcMessage[]; /** * The value of the last `id:` field seen across every event in the body * — including one whose `data:` was empty, SSE's own priming event. * `undefined` when no event in the body carried an id at all. */ readonly lastEventId?: string; } /** * Parse a response body served as `text/event-stream`. * * Exported and pure so it is a direct unit-test target, with no transport, * socket or server needed to see what it decides. * * Already correct on three counts before this: `data:` with or without a * leading space, multi-line data joined with `\n`, and the empty-data * priming event skipped as a message. This adds the two genuinely new * pieces — capturing `id:` (armed by the caller for a legacy * `Last-Event-ID` reconnect) and treating a `:`-prefixed comment or another * unrecognized line as exactly what the spec says it is: not a field, never * malformed input. Neither needed a special case: both filters below already * select a line by its OWN prefix and so already ignore anything else — a * comment, an `event:` line, a `retry:` line — without one. */ export declare function parseSseMessages(raw: string): MCPSseParseResult; //# sourceMappingURL=streamable-http.d.ts.map