πŸ“„ File detail

services/mcp/SdkControlTransport.ts

🧩 .tsπŸ“ 137 linesπŸ’Ύ 4,503 bytesπŸ“ text
← Back to All Files

🎯 Use case

This file lives under β€œservices/”, which covers long-lived services (LSP, MCP, OAuth, tool execution, memory, compaction, voice, settings sync, …). On the API surface it exposes SendMcpMessageCallback, SdkControlClientTransport, and SdkControlServerTransport β€” mainly types, interfaces, or factory objects. Dependencies touch @modelcontextprotocol. What the file header says: SDK MCP Transport Bridge This file implements a transport bridge that allows MCP servers running in the SDK process to communicate with the Claude Code CLI process through control messages. ## Architecture Overview Unlike regular MCP servers that run as separate processes, SDK MC.

Generated from folder role, exports, dependency roots, and inline comments β€” not hand-reviewed for every path.

🧠 Inline summary

SDK MCP Transport Bridge This file implements a transport bridge that allows MCP servers running in the SDK process to communicate with the Claude Code CLI process through control messages. ## Architecture Overview Unlike regular MCP servers that run as separate processes, SDK MCP servers run in-process within the SDK. This requires a special transport mechanism to bridge communication between: - The CLI process (where the MCP client runs) - The SDK process (where the SDK MCP server runs) ## Message Flow ### CLI β†’ SDK (via SdkControlClientTransport) 1. CLI's MCP Client calls a tool β†’ sends JSONRPC request to SdkControlClientTransport 2. Transport wraps the message in a control request with server_name and request_id 3. Control request is sent via stdout to the SDK process 4. SDK's StructuredIO receives the control response and routes it back to the transport 5. Transport unwraps the response and returns it to the MCP Client ### SDK β†’ CLI (via SdkControlServerTransport) 1. Query receives control request with MCP message and calls transport.onmessage 2. MCP server processes the message and calls transport.send() with response 3. Transport calls sendMcpMessage callback with the

πŸ“€ Exports (heuristic)

  • SendMcpMessageCallback
  • SdkControlClientTransport
  • SdkControlServerTransport

πŸ“š External import roots

Package roots from from "…" (relative paths omitted).

  • @modelcontextprotocol

πŸ–₯️ Source preview

/**
 * SDK MCP Transport Bridge
 *
 * This file implements a transport bridge that allows MCP servers running in the SDK process
 * to communicate with the Claude Code CLI process through control messages.
 *
 * ## Architecture Overview
 *
 * Unlike regular MCP servers that run as separate processes, SDK MCP servers run in-process
 * within the SDK. This requires a special transport mechanism to bridge communication between:
 * - The CLI process (where the MCP client runs)
 * - The SDK process (where the SDK MCP server runs)
 *
 * ## Message Flow
 *
 * ### CLI β†’ SDK (via SdkControlClientTransport)
 * 1. CLI's MCP Client calls a tool β†’ sends JSONRPC request to SdkControlClientTransport
 * 2. Transport wraps the message in a control request with server_name and request_id
 * 3. Control request is sent via stdout to the SDK process
 * 4. SDK's StructuredIO receives the control response and routes it back to the transport
 * 5. Transport unwraps the response and returns it to the MCP Client
 *
 * ### SDK β†’ CLI (via SdkControlServerTransport)
 * 1. Query receives control request with MCP message and calls transport.onmessage
 * 2. MCP server processes the message and calls transport.send() with response
 * 3. Transport calls sendMcpMessage callback with the response
 * 4. Query's callback resolves the pending promise with the response
 * 5. Query returns the response to complete the control request
 *
 * ## Key Design Points
 *
 * - SdkControlClientTransport: StructuredIO tracks pending requests
 * - SdkControlServerTransport: Query tracks pending requests
 * - The control request wrapper includes server_name to route to the correct SDK server
 * - The system supports multiple SDK MCP servers running simultaneously
 * - Message IDs are preserved through the entire flow for proper correlation
 */

import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'

/**
 * Callback function to send an MCP message and get the response
 */
export type SendMcpMessageCallback = (
  serverName: string,
  message: JSONRPCMessage,
) => Promise<JSONRPCMessage>

/**
 * CLI-side transport for SDK MCP servers.
 *
 * This transport is used in the CLI process to bridge communication between:
 * - The CLI's MCP Client (which wants to call tools on SDK MCP servers)
 * - The SDK process (where the actual MCP server runs)
 *
 * It converts MCP protocol messages into control requests that can be sent
 * through stdout/stdin to the SDK process.
 */
export class SdkControlClientTransport implements Transport {
  private isClosed = false

  onclose?: () => void
  onerror?: (error: Error) => void
  onmessage?: (message: JSONRPCMessage) => void

  constructor(
    private serverName: string,
    private sendMcpMessage: SendMcpMessageCallback,
  ) {}

  async start(): Promise<void> {}

  async send(message: JSONRPCMessage): Promise<void> {
    if (this.isClosed) {
      throw new Error('Transport is closed')
    }

    // Send the message and wait for the response
    const response = await this.sendMcpMessage(this.serverName, message)

    // Pass the response back to the MCP client
    if (this.onmessage) {
      this.onmessage(response)
    }
  }

  async close(): Promise<void> {
    if (this.isClosed) {
      return
    }
    this.isClosed = true
    this.onclose?.()
  }
}

/**
 * SDK-side transport for SDK MCP servers.
 *
 * This transport is used in the SDK process to bridge communication between:
 * - Control requests coming from the CLI (via stdin)
 * - The actual MCP server running in the SDK process
 *
 * It acts as a simple pass-through that forwards messages to the MCP server
 * and sends responses back via a callback.
 *
 * Note: Query handles all request/response correlation and async flow.
 */
export class SdkControlServerTransport implements Transport {
  private isClosed = false

  constructor(private sendMcpMessage: (message: JSONRPCMessage) => void) {}

  onclose?: () => void
  onerror?: (error: Error) => void
  onmessage?: (message: JSONRPCMessage) => void

  async start(): Promise<void> {}

  async send(message: JSONRPCMessage): Promise<void> {
    if (this.isClosed) {
      throw new Error('Transport is closed')
    }

    // Simply pass the response back through the callback
    this.sendMcpMessage(message)
  }

  async close(): Promise<void> {
    if (this.isClosed) {
      return
    }
    this.isClosed = true
    this.onclose?.()
  }
}