InProcessTransport.ts
1 import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' 2 import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js' 3 4 /** 5 * In-process linked transport pair for running an MCP server and client 6 * in the same process without spawning a subprocess. 7 * 8 * `send()` on one side delivers to `onmessage` on the other. 9 * `close()` on either side calls `onclose` on both. 10 */ 11 class InProcessTransport implements Transport { 12 private peer: InProcessTransport | undefined 13 private closed = false 14 15 onclose?: () => void 16 onerror?: (error: Error) => void 17 onmessage?: (message: JSONRPCMessage) => void 18 19 /** @internal */ 20 _setPeer(peer: InProcessTransport): void { 21 this.peer = peer 22 } 23 24 async start(): Promise<void> {} 25 26 async send(message: JSONRPCMessage): Promise<void> { 27 if (this.closed) { 28 throw new Error('Transport is closed') 29 } 30 // Deliver to the other side asynchronously to avoid stack depth issues 31 // with synchronous request/response cycles 32 queueMicrotask(() => { 33 this.peer?.onmessage?.(message) 34 }) 35 } 36 37 async close(): Promise<void> { 38 if (this.closed) { 39 return 40 } 41 this.closed = true 42 this.onclose?.() 43 // Close the peer if it hasn't already closed 44 if (this.peer && !this.peer.closed) { 45 this.peer.closed = true 46 this.peer.onclose?.() 47 } 48 } 49 } 50 51 /** 52 * Creates a pair of linked transports for in-process MCP communication. 53 * Messages sent on one transport are delivered to the other's `onmessage`. 54 * 55 * @returns [clientTransport, serverTransport] 56 */ 57 export function createLinkedTransportPair(): [Transport, Transport] { 58 const a = new InProcessTransport() 59 const b = new InProcessTransport() 60 a._setPeer(b) 61 b._setPeer(a) 62 return [a, b] 63 }