HttpClientTransport

A ClientTransport over the MCP Streamable HTTP transport.

Owns the HTTP/SSE machinery: the POST-and-await loop (with SSE resumability), the standalone server->client GET SSE stream, the subscriptions/listen stream, session-id capture, the OAuth bearer token, and the legacy 2024-11-05 HTTP+SSE two-endpoint fallback (legacyMode). The owning McpClient supplies the protocol-derived request headers (version / draft method+name / Mcp-Param-*) and the cancelled-response predicate through the ClientProtocol it installs via setProtocol, so this transport never needs the tool inputSchema cache or draft state.

Members

Functions

close
void close()

Stop the transport: signal the background stream readers (server->client, legacy GET) to stop between reads and force-close their held sockets so any blocked conn.read unblocks immediately, terminating the spawned tasks. A subscriptions/listen stream is owned by its SubscriptionStream handle and torn down through cancel().

repliesSynchronously
bool repliesSynchronously()

False: a reply to a server->client request travels on a *different* HTTP request than the one whose inbound stream delivered it, and a nested synchronous POST from inside an awaiting read loop could deadlock the connection. McpClient therefore defers the reply to a background task (which the HTTP transport already runs under an event loop).

setDraftProtocol
void setDraftProtocol(bool isDraft)

Mark whether the negotiated protocol version is modern (2026-07-28 / draft). When true, postAndAwait skips Last-Event-ID resumption via GET because the draft removed SSE resumability; a draft server responds to such a GET with 405.

setProtocol
void setProtocol(ClientProtocol p)

Install the owning client's ClientProtocol, through which this transport obtains the protocol-derived request headers and the cancelled-response predicate, so the draft header/schema logic and the cancellation set stay in the client.

startLegacyFallback
void startLegacyFallback()

Establish the legacy HTTP+SSE (2024-11-05) two-endpoint transport: open the GET SSE stream at the server URL, read the first endpoint event to learn the message-POST URI, then keep the stream open in a background task to receive JSON-RPC responses and server notifications. Throws if the endpoint event is not received. Called by McpClient.connect once a modern POST has been rejected with 400/404/405.

startServerStream
void startServerStream()

Open the standalone server->client SSE stream (GET /mcp) in a background task, so the server can deliver sampling / elicitation / roots requests and notifications outside of any POST response. A server that does not offer this stream (e.g. responds 405) is tolerated as a no-op.

Inherited Members

From ClientTransport

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.

sendOneway
void sendOneway(Json message)

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

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.

startServerStream
void startServerStream()

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

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.

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.

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.

close
void close()

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