import { T as Transport, M as MicroMcpServer } from '../server-Dwo9LOxh.js'; import { d as JsonRpcMessage } from '../types-ITRQFAir.js'; import '../tool-host-BqJRPr_T.js'; /** * StdioTransport — newline-delimited JSON-RPC over a byte stream pair. * * This is the framing every MCP client uses when it launches a server as a * subprocess (Claude Code, Codex, Cursor, Claude Desktop): one JSON-RPC message * per line on stdin, one per line on stdout. Anything else a server wants to say * goes to stderr, because a stray write to stdout corrupts the stream and the * client sees a parse error instead of a message. * * NODE ONLY, which is why it is its own subpath (`/mcp/stdio`) rather than part * of `/mcp`: browser bundles import `/mcp`, and `node:*` imports there would * break them. * * import { MicroMcpServer } from "@particle-academy/agent-integrations/mcp"; * import { attachStdio } from "@particle-academy/agent-integrations/mcp/stdio"; * * const server = new MicroMcpServer({ info: { name: "my-server", version: "1.0.0" } }); * server.registerTool(...); * attachStdio(server); * * What it deliberately does: * * - **A bad line costs that line, not the session.** A line that is not JSON, or * is JSON but not a single JSON-RPC message (a number, `null`, a batch), is * reported through `onError` and skipped. No reply is written for it — there * is no request id to address one to, and stdout carries protocol frames only. * - **Input ending does not drop a reply that is still owed.** `printf * '\n' | server` closes stdin straight after the request; replies * already in flight are delivered, then the transport closes. * - **Bytes, not chunks, are decoded.** A chunk boundary can cut a multi-byte * character in half; decoding chunk by chunk would corrupt it. */ /** The readable half. `process.stdin` and any Node `Readable` satisfy it. */ interface StdioInput { on(event: "data", listener: (chunk: unknown) => void): unknown; on(event: "end", listener: () => void): unknown; on(event: "error", listener: (error: Error) => void): unknown; removeListener(event: string, listener: (...args: any[]) => void): unknown; pause?(): unknown; } /** The writable half. `process.stdout` and any Node `Writable` satisfy it. */ interface StdioOutput { write(chunk: string): unknown; on?(event: "error", listener: (error: Error) => void): unknown; removeListener?(event: string, listener: (...args: any[]) => void): unknown; } type StdioOptions = { /** Defaults to `process.stdin`. */ input?: StdioInput; /** Defaults to `process.stdout`. */ output?: StdioOutput; /** * A line that could not be served, or a stream error. Defaults to one line on * `process.stderr` — the channel the MCP spec reserves for exactly this. */ onError?: (error: Error) => void; /** Called once, when the transport closes (input ended, or `close()`). */ onClose?: () => void; }; declare class StdioTransport implements Transport { private server?; private readonly input; private readonly output; private readonly reportError; private readonly onClose?; private readonly decoder; private buffer; private closed; private started; private readonly inFlight; constructor(options?: StdioOptions); /** Bind to a server. `attachStdio` does this for you. */ bindServer(server: MicroMcpServer): void; /** Begin reading input. `attachStdio` does this for you. */ start(): void; /** Server → client: one message, one line. `JSON.stringify` escapes every newline inside it. */ send(message: JsonRpcMessage): void; close(): void; private readonly onData; private readonly onEnd; private readonly onStreamError; private serve; } /** * Serve `server` over stdio (or the streams you pass). Returns the transport; * `server.detach(transport)` or `transport.close()` stops it. */ declare function attachStdio(server: MicroMcpServer, options?: StdioOptions): StdioTransport; export { type StdioInput, type StdioOptions, type StdioOutput, StdioTransport, attachStdio };