Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
# stdio
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`StdioTransport` frames JSON-RPC messages over a newline-delimited JSON byte stream — the MCP spec default for sub-process child servers. The transport is universal: it accepts an injectable `readable: AsyncIterable<Uint8Array>` and a `writable: UniversalWritable`, so callers wire `Readable.toWeb(process.stdin)`, `Bun.stdin.stream()`, browser `ReadableStream`, or an in-memory queue without the package depending on any Node-only module.
## Features
- Newline-delimited JSON framing — one message per `\n`-terminated line.
- Injectable streams: no `node:fs` / `node:process` / `node:stream` imports.
- Streaming parser tolerates partial chunks via a buffer + `TextDecoder({ stream: true })`.
- Malformed lines are skipped silently; framing recovers on the next newline.
- Inherits `closed` guard, listener registry, and idempotent close from `BaseTransport`.
## Usage: Basic Example
```ts
import { createStdioTransport } from "@suaveplan/mcp/transport/stdio";
import type { JsonRpcMessage } from "@suaveplan/mcp";
const sentLines: string[] = [];
const inbox = new ReadableStream<Uint8Array>({ start() { /* … */ } });
const transport = createStdioTransport({
readable: inbox,
writable: {
async write(chunk) { sentLines.push(new TextDecoder().decode(chunk)); },
async close() {},
},
});
transport.onMessage((m: JsonRpcMessage) => console.log("rx", m));
await transport.send({ jsonrpc: "2.0", id: 1, method: "ping" });
```
## Usage: Advanced Example
```ts
import { createStdioTransport } from "@suaveplan/mcp";
// Bridge to a child process spawned by your test harness.
async function bridgeProcess(child: { stdout: AsyncIterable<Uint8Array>; stdin: WritableStream }) {
const writer = child.stdin.getWriter();
return createStdioTransport({
readable: child.stdout,
writable: {
async write(chunk) { await writer.write(chunk); },
async close() { await writer.close(); },
},
});
}
```
## API Reference
- `interface StdioTransportOptions` — `{ readable: AsyncIterable<Uint8Array>; writable: UniversalWritable }`.
- `class StdioTransport extends BaseTransport` — constructor takes `StdioTransportOptions`; starts reading asynchronously.
- `createStdioTransport(options): StdioTransport` — convenience factory mirroring the documented public API.
## Implementation Notes
The reader loop maintains an internal text buffer; each chunk is decoded with `TextDecoder({ stream: true })` so multi-byte sequences split across reads still decode cleanly. After each newline the line is `trim`-ed and `JSON.parse`-d; failures or schema rejections are dropped because line framing makes the next message independent. Send framing writes `JSON.stringify(message) + "\n"` — `JSON.stringify` always escapes control characters in strings, so a literal newline in payload text becomes `\\n` and never breaks the framing. `closeInternal` flips a `stopped` flag the reader checks on each iteration and then closes the writable side; calling close while a long read is pending lets the reader exit on its next `for await` step.