/** * Codex MCP server — a hand-rolled JSON-RPC 2.0 stdio transport for rolebox's * canonical tools. * * Codex spawns the process behind this server (src/entries/codex.ts) and * speaks newline-delimited JSON-RPC on its stdin/stdout. This module owns the * read loop, the method dispatch (initialize, ping, tools/list, tools/call), * and the response/error mapping; it NEVER writes anything but protocol * messages to the configured output stream, never calls process.exit, and * never lets a tool failure escape to the protocol layer. * * Framing: output carries one JSON object per line, newline-terminated. The * loop tolerates chunk boundaries (a line split across two data events is * reassembled, and Buffer chunks are decoded with a streaming UTF-8 decoder so * a multi-byte character split across two chunks survives), CRLF line endings, * and blank lines (skipped as framing noise, not a parse error). A trailing * line that was never newline-terminated is NOT treated as a message: it was * never a complete frame, and a trailing incomplete UTF-8 sequence inside it * stays undecoded for the same reason. * * Ordering: REQUESTS are handled strictly one at a time, awaited sequentially. * A slow tool call therefore delays the next request, but responses stay in * request order and the canonical tools' shared state (terminals, hashline * edits, memory writes) never runs two calls concurrently by accident. * NOTIFICATIONS are the deliberate exception: a message without an id is * handled immediately on arrival and never queues behind the in-flight * request, so notifications/cancelled can abort the call it names while that * call is still running. The server owns one AbortController per in-flight * request id and hands its signal to the tool context. A cancelled call is not * special on the wire: it still gets a normal response carrying whatever the * tool body returns or throws. * * @module */ import type { CanonicalToolContext, CanonicalToolDef } from "../../types.ts"; /** Default serverInfo.name reported in the initialize result. */ export declare const DEFAULT_MCP_SERVER_NAME = "rolebox"; /** Everything the server needs to boot. */ export interface CodexMcpServerOptions { /** The canonical tools to expose — the same record buildCanonicalTools returns. */ tools: Record; /** serverInfo.version reported in the initialize result. */ serverVersion: string; /** serverInfo.name; defaults to DEFAULT_MCP_SERVER_NAME. */ serverName?: string; /** Optional instructions string included in the initialize result. */ instructions?: string; /** Protocol input stream; defaults to process.stdin. */ input?: NodeJS.ReadableStream; /** Protocol output stream; defaults to process.stdout. */ output?: NodeJS.WritableStream; /** Diagnostics sink; defaults to stderr ("[rolebox-mcp] ..."). */ onError?: (message: string) => void; /** * Build the canonical tool context for one call. Called once per * tools/call, before the tool runs; if it throws, the call is answered with * an isError tool result rather than crashing the loop. The second argument * is the signal of the AbortController the server owns for this request id — * notifications/cancelled aborts it (an existing one-parameter factory stays * valid, it just cannot observe cancellation). */ contextFactory?: (info: { name: string; args: Record; }, signal: AbortSignal) => CanonicalToolContext | Promise; } /** * The stdio MCP server. Construct with createCodexMcpServer, then either * await serve() (reads the input stream until end/close) or call * await handleLine(line) directly for tests and embedded use. */ export declare class CodexMcpServer { #private; constructor(opts: CodexMcpServerOptions); /** * The params.clientInfo object from the initialize handshake, when the client * sent one. Exposed so a context factory can derive a session id from the * handshake (the stdio transport carries no Mcp-Session-Id header). */ get clientInfo(): Record | undefined; /** * Handle one newline-framed line. Blank lines are ignored; a notification is * never answered and is handled out of band (see the module docstring on * ordering); every error path is converted into a protocol response (or an * isError tool result) — this method never rejects. */ handleLine(line: string): Promise; /** * Run the newline-framed read loop until the input ends or closes, then * drain queued handlers so every response is written before resolving. * Never throws and never calls process.exit. Calling it twice returns the * same promise. */ serve(): Promise; } /** Create a CodexMcpServer from explicit options. */ export declare function createCodexMcpServer(opts: CodexMcpServerOptions): CodexMcpServer; //# sourceMappingURL=server.d.ts.map