mcp.transport.streamable_http

Undocumented in source.

Members

Classes

LegacySseChannel
class LegacySseChannel

The legacy 2024-11-05 HTTP+SSE server->client channel. It manages the open GET SSE listeners and routes JSON-RPC server messages onto them as SSE message events. On registration a listener is minted a per-stream session token and immediately receives the endpoint event whose data carries that token as a ?sessionId= query parameter on the message-POST path, as the transport requires before any other traffic. The client echoes the token on every POST, so a response is routed back ONLY to the originating stream — never broadcast to other concurrently-connected clients (which would leak one client's results onto another client's stream).

Functions

acceptsEventStream
bool acceptsEventStream(string accept)

Whether the given Accept request-header value admits a text/event-stream response. The standalone GET stream the MCP endpoint opens is always an SSE stream, so a client whose Accept provably excludes that media type cannot consume what the server would send. A media type is admitted by an exact text/event-stream token, by the text/* subtype wildcard, or by the */* full wildcard; quality and other parameters after a ; are ignored. An empty value (no Accept header) is treated permissively as acceptable, since the transport never required clients to send one. Only a header that names media types and omits any matching one returns false.

acceptsJson
bool acceptsJson(string accept)

Whether the given Accept request-header value admits an application/json response — the single-JSON reply a plain POST request receives. Matching mirrors acceptsEventStream: an exact application/json token, the application/* subtype wildcard, or the */* full wildcard; quality and other ;-parameters are ignored. An empty value (no Accept) is permissive.

allowedMethodsHeader
string allowedMethodsHeader(ProtocolVersion negotiated, bool getSupported)

The value of the Allow header a 405 Method Not Allowed response must carry at the MCP endpoint. Per RFC 9110 §10.2.1 the Allow header MUST enumerate the set of methods the resource actually supports. On the stable revisions (2025-03-26 / 2025-06-18 / 2025-11-25) the single MCP endpoint "supports both POST and GET methods" (basic/transports §Streamable HTTP) — the standalone server->client SSE stream is mounted on GET (getOpensSseStream is true) — so a 405 (e.g. to a DELETE the server does not honour) MUST advertise GET, POST. On the draft the standalone GET stream and protocol-level DELETE are both dropped, leaving POST as the only supported method, so the header is POST.

deleteTerminatesSession
bool deleteTerminatesSession(ProtocolVersion negotiated)

Decide how to answer an HTTP DELETE to the MCP endpoint (basic/transports §Session Management / §Backward Compatibility). The stable revisions (2025-03-26 / 2025-06-18 / 2025-11-25) carry protocol-level sessions a client tears down via DELETE + Mcp-Session-Id. The draft removed protocol-level sessions ("Removal of protocol-level sessions"), so there is nothing to terminate: a draft-negotiated server "SHOULD respond as follows: HTTP GET or DELETE to the MCP endpoint: respond with 405 Method Not Allowed."

endpointWithSession
string endpointWithSession(string endpointPath, string sessionId)

Append a per-stream session token to the legacy message-POST URI so the leading endpoint event tells a client where to POST AND which stream its replies belong to. The token is added as a sessionId query parameter, preserving any existing query string already present on the path.

formatLegacyEndpointEvent
string formatLegacyEndpointEvent(string uri)

Frame the legacy endpoint SSE event (2024-11-05 basic/transports §HTTP with SSE): a typed endpoint event whose data is the URI the client must POST subsequent messages to.

formatLegacyMessageEventRaw
string formatLegacyMessageEventRaw(string jsonText)

Frame a legacy server message SSE event from already-serialised JSON text (2024-11-05 basic/transports §HTTP with SSE: "Server messages are sent as SSE message events, with the message content encoded as JSON in the event data").

getOpensSseStream
bool getOpensSseStream(ProtocolVersion negotiated)

Decide how to answer an HTTP GET to the MCP endpoint (basic/transports §Listening for Messages from the Server): the server "MUST either return Content-Type: text/event-stream in response to this HTTP GET, or else return HTTP 405 Method Not Allowed".

handleLegacyPostBody
bool handleLegacyPostBody(McpServer server, LegacySseChannel channel, string sessionId, string payload)

Process a single JSON-RPC message POSTed to the legacy message endpoint and route any response back onto the originating client's legacy GET SSE stream as a message event. sessionId is the per-stream token the client echoed from its endpoint event, so the response is delivered ONLY to the stream that issued this POST — never broadcast across concurrently-connected clients. Returns true (the server accepts every well-formed POST on this transport; a parse failure still yields a JSON-RPC error response delivered on the stream). A notification produces no response, so nothing is delivered. Exposed (package-level) so the two-endpoint flow can be exercised without a live socket.

hostAllowed
bool hostAllowed(string host, string[] extra)

Whether a Host header value (e.g. "127.0.0.1:3000") is localhost or listed.

httpStatusForResponse
int httpStatusForResponse(Json resp, bool isDraft)

Map a JSON-RPC response to the HTTP status the Streamable HTTP transport must surface. Successful results and ordinary application errors ride on 200. The draft reserves specific statuses so intermediaries — and clients probing modern-vs-legacy servers — can act without parsing the body: - UnsupportedProtocolVersionError (-32004) -> 400 (all modern versions), - HeaderMismatch (-32001) -> 400, - MissingRequiredClientCapability (-32003) -> 400 (all modern versions), - Method not found (-32601) -> 404 on draft requests, which lets a client tell a modern MCP endpoint apart from a legacy HTTP+SSE 404. Pre-draft versions keep the legacy JSON-RPC-error-over-200 shape.

initializeSucceeded
bool initializeSucceeded(Json resp)

Whether a JSON-RPC response to an initialize request carries a successful result rather than an error. Session Management (basic/transports §Session Management) ties the Mcp-Session-Id header to "the HTTP response containing the InitializeResult", so a freshly-minted session is committed (its header set, the session kept) only when this is true; an error response (e.g. a second initialize rejected with invalidRequest) rolls the minted session back instead.

mountLegacyHttpSse
void mountLegacyHttpSse(URLRouter router, McpServer server, StreamableHttpOptions opts)

Mount the deprecated 2024-11-05 HTTP+SSE two-endpoint transport (basic/transports §HTTP with SSE) onto router, so a legacy-only client can still negotiate it against a D MCP server. Called by mountMcp when opts.legacyHttpSse is set, but also usable directly to host ONLY the legacy transport. It mounts: - GET opts.legacySsePath: opens a text/event-stream; the FIRST event is the endpoint event whose data is opts.legacyMessagePath ("When a client connects, the server MUST send an endpoint event containing a URI for the client to use for sending messages"); the stream is then held open and every server message is delivered as an SSE message event; - POST opts.legacyMessagePath: accepts a single JSON-RPC message, replies 202 Accepted with no body, and pushes any JSON-RPC response back onto the open GET stream as a message event ("Server messages are sent as SSE message events, with the message content encoded as JSON in the event data"). Origin/auth guards mirror the modern endpoint.

mountMcp
void mountMcp(URLRouter router, McpServer server, StreamableHttpOptions opts)

Mount an McpServer onto a vibe.d URLRouter at the configured path, implementing the modern Streamable HTTP transport (single endpoint): - POST: a JSON-RPC message/batch; returns application/json for requests, or 202 Accepted with no body when the payload needs no reply. - GET: on the stable revisions, opens a standalone server->client SSE stream wired to the server-push channel (McpServer.notify); on the draft, which drops the standalone stream, GET -> 405. - DELETE: the draft has no protocol-level sessions to tear down -> 405.

opensListenStream
bool opensListenStream(string method, bool isDraft)

Decide whether a request must be answered with a long-lived text/event-stream notification stream rather than the ordinary one-shot JSON response (draft basic/transports / basic/utilities/subscriptions). Only the draft subscriptions/listen request takes this path: "The server's response is itself an SSE stream that stays open and delivers the change notifications." Pre-draft versions never defined subscriptions/listen, so they answer normally.

originAllowed
bool originAllowed(string origin, string[] extra)

Whether an Origin header value (e.g. "http://localhost:3000") is localhost or listed.

postAccepted
bool postAccepted(string accept)

Whether a POSTed request's Accept header admits at least one of the two media types the Streamable HTTP transport can answer a request with — application/json (a single JSON reply) or text/event-stream (an SSE stream). The spec REQUIRES POST clients to send Accept: application/json, text/event-stream; a request whose Accept provably excludes BOTH could not consume any response the server may produce, so it is rejected up front with 406 Not Acceptable. A missing/blank Accept is permissive (both helpers admit it), matching the GET path's tolerance.

postProtocolVersionGate
McpException postProtocolVersionGate(string protoHeader)

The transport-level MCP-Protocol-Version check that gates EVERY POST to the MCP endpoint, irrespective of whether the body is a request, notification, or response. basic/transports §Protocol Version Header (2025-06-18 / 2025-11-25): "If the server receives a request with an invalid or unsupported MCP-Protocol-Version, it MUST respond with 400 Bad Request." Returns the rejecting McpException (mapped to HTTP 400) or null when the header is absent or names a supported version. This is just validateProtocolVersionHeader, named to make explicit that it runs before the per-kind routing switch.

runStreamableHttp
void runStreamableHttp(McpServer server, ushort port, StreamableHttpOptions opts)

Start a standalone Streamable HTTP server for server on port and run the vibe.d event loop. Blocks until the application exits.

runStreamableHttp
void runStreamableHttp(McpServer server, ushort port, string host)

Convenience: start a Streamable HTTP server for server on port, bound to a single host. Sets StreamableHttpOptions.bindAddresses = [host] and forwards to runStreamableHttp(server, port, opts). Blocks until exit.

sessionStatus
int sessionStatus(SessionManager sessions, string sessionId)

Decide the HTTP status for a non-initialize request when session management is enabled (basic/transports §Session Management): - 0 — the session id is present and active; proceed. - 400 — no Mcp-Session-Id header was supplied ("Servers that require a session ID SHOULD respond to requests without an Mcp-Session-Id header (other than initialization) with HTTP 400 Bad Request"). - 404 — the id names an unknown or already-terminated session ("after termination it MUST respond ... with HTTP 404 Not Found").

sessionsApply
bool sessionsApply(ProtocolVersion negotiated)

Decide whether protocol-level sessions apply to a POST request (basic/transports §Backward Compatibility / §Earlier Streamable HTTP Revisions). The stable revisions (2025-03-26 / 2025-06-18 / 2025-11-25) carry protocol-level sessions: the server mints an Mcp-Session-Id on the InitializeResult and requires the client to echo it on every later request. The draft removed protocol-level sessions (revision 2026-07-28: "Removal of protocol-level sessions"), so a draft-only server "SHOULD respond as follows: ... An Mcp-Session-Id header on a request: ignore it, and do not mint or echo session IDs."

streamableBatchAllowed
bool streamableBatchAllowed(ProtocolVersion v)

Whether a JSON-RPC batch (array body) is permitted on the Streamable HTTP POST endpoint for the given effective protocol version.

subscriptionsAcknowledgedNotification
Json subscriptionsAcknowledgedNotification(Json subset)

Build the leading event the transport sends when it opens a subscriptions/listen stream: a notifications/subscriptions/acknowledged notification carrying the agreed-upon subset of change-notification types the server will deliver on the stream (draft basic/utilities/subscriptions). The subset is what McpServer.acknowledgedSubsetFor reported for that one stream's filter (each subscription is independent — §Multiple Concurrent Subscriptions).

suppressOnDisconnect
bool suppressOnDisconnect(bool isDraft, bool connected)

Decide whether a finished POST response must be suppressed because the client disconnected mid-request. On the draft Streamable HTTP transport a client disconnect IS the cancellation signal (draft basic/utilities/cancellation §Transport-Specific Cancellation: "Closing the SSE response stream is the cancellation signal. The server MUST treat a client disconnect as cancellation of that request. No notifications/cancelled message is required or expected."). So when the connection has dropped on a draft request, the response is suppressed. Released versions (2025-*) never suppress on this basis: that MUST is draft-only, so their wire behaviour is unchanged.

validateAuthConfig
void validateAuthConfig(ResourceServerConfig cfg)

Validate that an auth-enabled ResourceServerConfig can publish a spec-compliant Protected Resource Metadata document before the transport starts serving it. basic/authorization §Authorization Server Location (all of 2025-06-18 / 2025-11-25 / draft) makes RFC 9728 a MUST: "The Protected Resource Metadata document returned by the MCP server MUST include the authorization_servers field containing at least one authorization server." An operator who sets auth.validator but forgets auth.authorizationServers would otherwise publish "authorization_servers": [], a silent MUST violation. We fail loudly here (at mount time) rather than serve it.

validateDraftHeaders
McpException validateDraftHeaders(string protoHeader, string methodHeader, string nameHeader, Message msg, bool isDraft)

Validate the draft Streamable HTTP request headers against the JSON-RPC body. Returns a HeaderMismatch (-32001) exception on failure, or null when the request is valid — or when the request is not a draft request (older versions did not define these headers, so they are not enforced).

validateParamHeaders
McpException validateParamHeaders(Json inputSchema, Json args, string delegate(string) @(safe) headerGet)

Validate draft x-mcp-header mirroring: every parameter annotated with x-mcp-header (at any nesting depth) whose value is present in args MUST have a matching (decoded) Mcp-Param-* header; absent parameters MUST NOT carry the header. The annotation set itself is also validated against the draft value constraints (non-empty, HTTP token syntax, no CR/LF, primitive types only with number forbidden, case-insensitive uniqueness). Returns a HeaderMismatch exception on violation, else null.

validateProtocolVersionHeader
McpException validateProtocolVersionHeader(string protoHeader)

Validate the HTTP MCP-Protocol-Version header for a request. Per the 2025-06-18 / 2025-11-25 transport ("Protocol Version Header"): if the header is present but carries an invalid or unsupported version, the server MUST respond with 400 Bad Request. Returns an UnsupportedProtocolVersionError (-32004, which the transport maps to HTTP 400) in that case, else null.

Manifest constants

ProtectedResourceMetadataPath
enum ProtectedResourceMetadataPath;

The well-known path (RFC 9728 §3) at which a protected resource server publishes its OAuth 2.0 Protected Resource Metadata document.

SessionHeader
enum SessionHeader;

The HTTP header carrying the session id (basic/transports §Session Management).

Structs

StreamableHttpOptions
struct StreamableHttpOptions

Configuration for the Streamable HTTP server transport.