ClientTransport

The transport seam under McpClient. The client speaks pure JSON-RPC and protocol logic; a ClientTransport carries the bytes — over Streamable HTTP (HttpClientTransport) or stdio (StdioClientTransport).

The client installs its inbound dispatcher via setInboundHandler (it passes McpClient.dispatchInbound); the transport invokes that handler for every interleaved notification and server->client request it reads on any stream. A response to a server->client request, and any client-originated notification, are sent with sendOneway. Per-request work goes through deliver, which sends the request and returns its correlated result (or throws McpException on an error response), dispatching anything else it sees in the meantime to the inbound handler.

Members

Functions

close
void close()

Release transport resources: stdio terminates the subprocess (when one was spawned); HTTP stops any background streams.

deliver
Json deliver(Json requestMessage, long expectId)

Send a JSON-RPC request requestMessage and return its result Json (throwing McpException on an error response). The id to await is expectId. Interleaved notifications and server->client requests seen while awaiting are dispatched to the inbound handler.

openListen
SubscriptionStream openListen(Json listenMessage)

Open a long-lived subscriptions/listen stream for listenMessage, dispatching every inbound message on it to the inbound handler. Returns a handle whose cancel()/close() stops the stream.

repliesSynchronously
bool repliesSynchronously()

Whether a reply to a server->client request may be written *inline* from the inbound-read callback rather than deferred to a background task. The reply is sent from inside the read loop of an in-flight request, while that loop must keep draining inbound bytes, so the answer turns on whether a nested synchronous send can wedge the read loop: - false (both concrete transports): defer the reply. stdio would block the single read-loop task on the OS pipe buffer (the child may be blocked writing stdout we have stopped draining while we block writing its stdin); HTTP's reply travels on a different request that could deadlock the connection. McpClient dispatches the reply via runTask, which requires a running event loop — both transports already provide one. - true: send inline, with no event loop required. Reserved for a future transport whose read loop neither blocks nor holds the awaited response.

sendOneway
void sendOneway(Json message)

Send a message that expects no correlated reply: a notification, or a response to a server->client request.

setBearerToken
void setBearerToken(string token)

Attach an OAuth bearer access token (HTTP Authorization: Bearer); a no-op on stdio. An empty string clears it.

setDraftProtocol
void setDraftProtocol(bool isDraft)

Signal whether the negotiated protocol version is modern (2026-07-28 / draft). The HTTP transport uses this to skip Last-Event-ID resumption (GET) that the draft removed; a no-op on stdio and on transports where the flag is irrelevant.

setInboundHandler
void setInboundHandler(void delegate(Message) @(safe) handler)

Install the client's inbound dispatcher (McpClient.dispatchInbound), invoked for notifications and server->client requests on any stream.

setProtocol
void setProtocol(ClientProtocol protocol)

Install the client's ClientProtocol collaborator, through which the transport obtains the protocol-derived request headers (headersFor) and the cancelled-response predicate (isCancelled). A transport that needs neither (e.g. stdio) may keep it but ignore it. McpClient calls this once at construction.

startLegacyFallback
void startLegacyFallback()

Initiate the transport's backward-compatibility fallback after a modern request was rejected in a way that signals an older server (HTTP: a 400/404/405 POST -> open the legacy HTTP+SSE GET stream and switch to the two-endpoint transport). A no-op on transports without a fallback path (stdio), symmetric with startServerStream/setBearerToken. The client follows this with the legacy initialize handshake.

startServerStream
void startServerStream()

Open the standalone server->client stream, if the transport has one (HTTP GET SSE). A no-op on stdio.