StdioServerTransport

Server-side MCP transport that exchanges JSON-RPC messages over arbitrary byte streams.

Reads framed messages from the supplied Source and writes framed messages to the supplied Sink. Three internal coroutines drive the pipeline:

Both internal channels are bounded, so a slow handler or slow output naturally back-pressures the upstream producer — send suspends when the outbound channel is full.

Both explicit close and a natural EOF from the input perform a graceful drain: in-flight outbound messages are flushed before onClose fires, and the input source and output sink are released.

Example:

val transport = StdioServerTransport(
input = System.`in`.asSource().buffered(),
output = System.out.asSink().buffered(),
) {
scope = myScope
}
transport.start()

Constructors

Link copied to clipboard
constructor(input: Source, output: Sink, block: StdioServerTransport.Builder.() -> Unit = {})

Creates a StdioServerTransport reading from input and writing to output, with optional configuration applied through a Builder block.

constructor(inputStream: Source, outputStream: Sink)

Creates a StdioServerTransport from the given input source and output sink with default configuration. Retained for binary compatibility; prefer the Builder-based constructor.

Types

Link copied to clipboard
class Builder

Configuration builder for StdioServerTransport. Used via the StdioServerTransport(input, output) { ... } factory; the I/O endpoints are supplied positionally, while scope, handlerDispatcher, and ioDispatcher are configurable inside the block.

Functions

Link copied to clipboard
open suspend override fun close()

Closes the transport. When called after start, waits for in-flight outbound messages to be flushed, releases the input source and output sink, cancels the internal scope when the transport owns it, and invokes onClose. When called before start, transitions directly to the closed state without releasing the input/output or invoking onClose — the caller remains responsible for resources that were never handed off to a running transport. Safe to call multiple times and safe to race with start.

Link copied to clipboard
open override fun onClose(block: () -> Unit)
Link copied to clipboard
open override fun onError(block: (Throwable) -> Unit)
Link copied to clipboard
open override fun onMessage(block: suspend (JSONRPCMessage) -> Unit)
Link copied to clipboard
open suspend override fun send(message: JSONRPCMessage, options: TransportSendOptions?)

Queues message for the writer coroutine. Suspends when the outbound channel is full, applying back-pressure to the caller. Throws McpException with RPCError.ErrorCode.CONNECTION_CLOSED if the transport has not been started or has already closed.

Link copied to clipboard
open suspend override fun start()

Starts the reader, processor, and writer coroutines. Must be called exactly once before messages can be exchanged; subsequent calls throw.