/** * Transport Composition * * Base wrapper transport class and composition helper for building * middleware pipelines at the transport layer. */ import type { JSONRPCMessage, JSONRPCRequest, } from "@modelcontextprotocol/sdk/types.js"; import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; /** * Transport middleware function type * Takes a transport and returns a wrapped transport */ export type TransportMiddleware = (transport: Transport) => Transport; /** * Base wrapper transport that delegates to an inner transport * Override handleOutgoingMessage/handleIncomingMessage to intercept messages */ export abstract class WrapperTransport implements Transport { constructor(protected innerTransport: Transport) {} get sessionId(): string | undefined { return (this.innerTransport as any).sessionId; } async start(): Promise { // Set up message forwarding before starting this.innerTransport.onmessage = (message: JSONRPCMessage) => { this.handleIncomingMessage(message); }; this.innerTransport.onerror = (error: Error) => { this.onerror?.(error); }; this.innerTransport.onclose = () => { this.onclose?.(); }; return this.innerTransport.start(); } async send(message: JSONRPCMessage): Promise { return this.handleOutgoingMessage(message); } async close(): Promise { return this.innerTransport.close(); } /** * Override in subclasses to intercept outgoing messages * Default: forward to inner transport */ protected async handleOutgoingMessage( message: JSONRPCMessage, ): Promise { return this.innerTransport.send(message); } /** * Override in subclasses to intercept incoming messages * Default: forward to onmessage callback */ protected handleIncomingMessage(message: JSONRPCMessage): void { this.onmessage?.(message); } /** * Helper to check if message is a JSON-RPC request */ protected isRequest(message: JSONRPCMessage): message is JSONRPCRequest { return "method" in message && message.method !== undefined; } /** * Helper to check if message is a JSON-RPC response */ protected isResponse(message: JSONRPCMessage): boolean { return ( "result" in message || ("error" in message && !("method" in message)) ); } // Transport callbacks onmessage?: (message: JSONRPCMessage) => void; onerror?: (error: Error) => void; onclose?: () => void; } /** * Compose multiple transport middlewares * Applies from left to right (first middleware wraps innermost) * * @example * const transport = composeTransport( * baseTransport, * (t) => new AuthTransport(t, options), * (t) => new MonitoringTransport(t, options) * ); * // Request flow: MonitoringTransport -> AuthTransport -> baseTransport * // Response flow: baseTransport -> AuthTransport -> MonitoringTransport */ export function composeTransport( baseTransport: Transport, ...middlewares: TransportMiddleware[] ): Transport { return middlewares.reduce( (transport, middleware) => middleware(transport), baseTransport, ); }