Skip to main content

Module stdio

Module stdio 

Source
Expand description

The stdio transport’s byte layer.

§What this is

Three adapters and a shared error type, and none of them knows what a JSON-RPC message is. StdioSource turns a byte stream into whole frames, StdioSink turns documents into framed bytes, and Diagnostics is the side channel that must never be confused for either. The framing itself is crate::domain::FrameDecoder and crate::domain::encode_frame — this module supplies the stream, the ceiling, and the errors, and delegates the rest, so the two halves of the transport cannot disagree about where a frame ends or how long one may be.

§The ceiling is one value, not two

StdioConfig is handed to both halves. That is the point: a limit applied only to reads protects the server from a hostile peer but not from a handler that answers with more than the peer will read, and a limit applied only to writes is the same argument with the roles swapped. One MaxFrameSize, both directions, so a reply is refused with a typed error instead of being written as a line the peer’s own decoder has to treat as a malformed message.

§Why the transport is generic over its stream

StdioSource<R> and StdioSink<W> are generic rather than hard-wired to a process’s stdin and stdout. The transport boundary is where a tokio::process::Child becomes a byte stream, and everything below that line is identical whether the bytes arrive from a child, a pipe, a socket, or a test. Naming the concrete stream here would make every one of those cases a second implementation, and the framing logic — the part with the security properties — would be duplicated along with it.

§Where the conversation is

The three adapters above are a byte layer and stay one. The loop that reads a frame, routes it and answers it is serve::ServeLoop, and the reply document it writes is reply. Both sit in this module because they are still stdio: the loop’s central rule is that this transport’s stdout carries MCP messages and nothing else, and that rule is about this process’s streams rather than about MCP.

§What is deliberately not here

No spawning and no lifecycle. The loop runs against whatever byte streams it is given, and the process management that gives it a child’s stdin and stdout in the first place is a separate concern. A single unterminated line has no framing error either, because the framing layer cannot know whether the peer is mid-message or has gone away.

Re-exports§

pub use crate::domain::jsonrpc::reply::ReplyError;

Structs§

Diagnostics
A writer bound to one stream, safe to share.
DiagnosticsWriter
A held borrow of a Diagnostics channel, usable as an AsyncWrite.
ServeLoop
Reads a peer’s frames, routes them, and writes the answers back.
ServeReport
The handler’s view of the peer’s id.
StdioConfig
The settings both halves of the transport are built from.
StdioSink
Writes framed documents to the peer, and to nothing else.
StdioSource
A FrameSource over any AsyncRead.

Enums§

ServeError
Why the session ended when it was not the peer ending it.
StdioError
Why the stdio transport could not carry a frame.