@orkestrel/mcp 0.0.20 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -324,22 +324,28 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
324
324
 
325
325
  /**
326
326
  * Creates the stdio CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
327
- * — a {@link MCPClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
327
+ * — a {@link StdioClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
328
328
  * over newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
329
329
  * createHTTPClientTransport} and {@link createWebSocketClientTransport}.
330
330
  *
331
331
  * @remarks
332
332
  * Hand it to `createMCPClient({ transport })`: `start()` (run by `client.connect()`)
333
333
  * spawns `options.command` with `options.args` and `options.env`, piping its
334
- * `stdin`/`stdout` for the JSON-RPC channel (its `stderr` inherits the parent's for
335
- * diagnostics). Each JSON-RPC message the client `send`s is written as one
334
+ * `stdin`/`stdout` for the JSON-RPC channel. The child's `stderr` is piped too, and
335
+ * retained as a bounded tail this transport reports as `evidence` the parent never
336
+ * inherits it. Each JSON-RPC message the client `send`s is written as one
336
337
  * newline-terminated line to the child's `stdin`; each decoded reply line from the
337
338
  * child's `stdout` is surfaced on the transport's `message` event for the client's
338
- * id correlation.
339
+ * id correlation. That write is bounded: a child that stays alive without ever reading
340
+ * its `stdin` fills the pipe, and `options.delivery` is how long the unconfirmed write
341
+ * waits before the `send` rejects. An omitted `delivery` selects {@link
342
+ * import('./constants.js').DEFAULT_MCP_DELIVERY}; an explicit `0` removes the bound.
339
343
  *
340
344
  * @param options - `command` (the executable to spawn; REQUIRED), optional `args`,
341
- * and optional `env`; see {@link StdioClientTransportOptions}
342
- * @returns A working {@link MCPClientTransportInterface} over a child process's stdio
345
+ * optional `env`, and an optional `delivery` bound in milliseconds on an unconfirmed
346
+ * `stdin` write; see {@link StdioClientTransportOptions}
347
+ * @returns A working {@link StdioClientTransportInterface} over a child process's stdio,
348
+ * whose `evidence` carries the supervised child's bounded stderr tail
343
349
  *
344
350
  * @example
345
351
  * ```ts
@@ -353,7 +359,7 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
353
359
  * const tools = await client.tools()
354
360
  * ```
355
361
  */
356
- export declare function createStdioClientTransport(options: StdioClientTransportOptions): MCPClientTransportInterface;
362
+ export declare function createStdioClientTransport(options: StdioClientTransportOptions): StdioClientTransportInterface;
357
363
 
358
364
  /**
359
365
  * Creates the MCP stdio transport INGRESS — pumps a transport-agnostic {@link
@@ -496,6 +502,19 @@ export declare function createWebSocketServer(mcp: MCPDispatcherInterface, optio
496
502
  */
497
503
  export declare function decodeEvent(data: string): JSONRPCMessage | undefined;
498
504
 
505
+ /**
506
+ * The default bound in milliseconds on one unconfirmed write to a stdio client transport's
507
+ * child `stdin` — the `delivery` a `createStdioClientTransport` caller who supplies none gets.
508
+ *
509
+ * @remarks
510
+ * Ten seconds. The load-bearing property is the ordering, not the magnitude: this bound stays
511
+ * BELOW {@link import('@orkestrel/mcp').DEFAULT_MCP_REQUEST_TIMEOUT}, so a write the child never
512
+ * reads fails as an undeliverable message while the request that carried it is still open,
513
+ * rather than being masked by that request's own deadline expiring first. Override per
514
+ * transport with `delivery`; an explicit `0` there removes the bound.
515
+ */
516
+ export declare const DEFAULT_MCP_DELIVERY = 10000;
517
+
499
518
  /**
500
519
  * The default interval in milliseconds between SSE keepalive comments on held-open MCP
501
520
  * responses.
@@ -1242,7 +1261,7 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1242
1261
 
1243
1262
  /**
1244
1263
  * The stdio CLIENT transport for the Model Context Protocol — a
1245
- * {@link MCPClientTransportInterface} that drives a CHILD PROCESS MCP server over
1264
+ * {@link StdioClientTransportInterface} that drives a CHILD PROCESS MCP server over
1246
1265
  * newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
1247
1266
  * import('./HTTPClientTransport.js').HTTPClientTransport} and {@link
1248
1267
  * import('./WebSocketClientTransport.js').WebSocketClientTransport}.
@@ -1260,19 +1279,36 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1260
1279
  * - **Outbound (`send`).** `send(message)` writes one newline-terminated `JSON.stringify`d line
1261
1280
  * through the supervisor's `send` and AWAITS its answer, so this promise settles only after the
1262
1281
  * host reports the line handled rather than the moment the write is queued. The supervisor never
1263
- * rejects — it answers `false` for a channel that was closed, destroyed, or ended, and for a write
1264
- * that failed so a `false` answer REJECTS here with the same not-connected error a transport
1265
- * that was never started raises. A dead peer surfaces at the caller instead of vanishing.
1266
- * - **`close()`** releases this transport's line pump without waiting for the child's stdout
1267
- * iterator, then runs the supervisor's bounded `SIGTERM` grace `SIGKILL` group-kill and
1268
- * teardown before firing `close` once (idempotent). A descendant can retain an inherited stdout
1269
- * pipe after the child exits; the pump's release barrier keeps that substrate limit from keeping
1270
- * this transport's `close()` pending. On a POSIX host the child leads its own process group, so
1271
- * the group-kill reaches its grandchildren rather than orphaning them.
1282
+ * rejects — it answers `false` for a channel that was closed, destroyed, or ended, for a write
1283
+ * that failed, or for one that remained unconfirmed through `delivery`. A call made without a
1284
+ * live child rejects as not connected; a `false` answer from a live child rejects as unable to
1285
+ * deliver. The supervisor does not disclose which cause produced that answer.
1286
+ * - **`close()`** runs the supervisor's bounded termination and teardown, then fires `close` once
1287
+ * (idempotent). That teardown reaches the child's TERMINAL MOMENT, where the supervisor freezes
1288
+ * `evidence`, ends `lines`, and settles `exit` together, so this transport needs no release of
1289
+ * its own to get its line pump back: the stream ends under the pump rather than throwing at it.
1290
+ * A line the supervisor had already framed behind the one being delivered is dropped rather than
1291
+ * emitted onto a transport whose teardown has begun. A `close()` issued while that teardown runs
1292
+ * joins it rather than opening a second one, so it resolves only after `close` has fired, and a
1293
+ * `start()` issued while it runs waits behind the same barrier, so lifetimes never overlap. A
1294
+ * descendant can retain an inherited stdout pipe after the child exits; the supervisor's `drain`
1295
+ * bound cuts that wait off, so this transport's `close()` settles within that bound rather than
1296
+ * on the descendant. The termination itself belongs to the host: a POSIX host signals the
1297
+ * child's own process group `SIGTERM`, waits the grace window, then `SIGKILL`s through the same
1298
+ * route, so the kill reaches grandchildren rather than orphaning them, while Windows ends the
1299
+ * tree with `taskkill /F /T`, which nothing in the child can intercept.
1300
+ * - **Evidence.** `evidence` reports that retained stderr tail off the HELD child — its live tail
1301
+ * while the child runs, and the value the supervisor froze at that child's terminal moment
1302
+ * afterwards. The reference is held past that moment and replaced only by the next `start()`,
1303
+ * which is what keeps a post-`close()` read stable without a private copy: the frozen value
1304
+ * never moves again, so a detached descendant writing to the inherited stderr after the cutoff
1305
+ * cannot grow it. See {@link StdioClientTransportInterface.evidence} for the readings and the
1306
+ * byte bound.
1272
1307
  * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
1273
1308
  * emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
1274
- * fault, including the child spawn cause the supervisor surfaces), distinct from the emitter's
1275
- * own listener-error channel.
1309
+ * fault, including the child spawn cause the supervisor surfaces and the notice that this
1310
+ * lifetime's `evidence` was cut off at the `drain` bound), distinct from the emitter's own
1311
+ * listener-error channel.
1276
1312
  *
1277
1313
  * @example
1278
1314
  * ```ts
@@ -1281,17 +1317,96 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1281
1317
  * await client.connect() // start() spawns the child, then the MCP initialize runs over stdio
1282
1318
  * ```
1283
1319
  */
1284
- export declare class StdioClientTransport implements MCPClientTransportInterface_2 {
1320
+ export declare class StdioClientTransport implements StdioClientTransportInterface {
1285
1321
  #private;
1286
1322
  constructor(options: StdioClientTransportOptions);
1287
1323
  get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
1288
1324
  get session(): string | undefined;
1289
1325
  get duplex(): boolean;
1326
+ get evidence(): string | undefined;
1290
1327
  start(): Promise<void>;
1328
+ /**
1329
+ * Sends one newline-delimited JSON-RPC message to the live child.
1330
+ *
1331
+ * @param message - The message to write to the child's `stdin`
1332
+ * @returns Resolves when the supervisor confirms the write
1333
+ * @throws Thrown with `stdio transport is not connected` when no live child is available before
1334
+ * the write
1335
+ * @throws Thrown with `stdio transport could not deliver the message` when a live child's write
1336
+ * resolves `false`
1337
+ */
1291
1338
  send(message: JSONRPCMessage_2): Promise<void>;
1292
1339
  close(): Promise<void>;
1293
1340
  }
1294
1341
 
1342
+ /**
1343
+ * The contract `createStdioClientTransport` returns — a {@link MCPClientTransportInterface}
1344
+ * that also reports the supervised child's stderr tail, the diagnostic a child that dies at
1345
+ * startup leaves behind.
1346
+ *
1347
+ * @remarks
1348
+ * This contract adds `evidence` and changes nothing else: `emitter`, `session`, `duplex`,
1349
+ * `start`, `send`, and `close` are the shared client-transport surface, unchanged. It sits here
1350
+ * rather than on {@link MCPClientTransportInterface} because a transport that supervises no child —
1351
+ * Streamable HTTP, WebSocket, a `MessagePort` pair — has no such tail, and a member every one
1352
+ * of them answers `undefined` to forever is a stdio detail rather than a shared contract. A
1353
+ * consumer that widens this value back to {@link MCPClientTransportInterface}, including by
1354
+ * reading `client.transport`, loses the reader and must keep the original reference.
1355
+ */
1356
+ export declare interface StdioClientTransportInterface extends MCPClientTransportInterface {
1357
+ /**
1358
+ * The supervised child's decoded stderr tail — live while a child is held, and the value
1359
+ * captured at that child's end afterwards.
1360
+ *
1361
+ * @remarks
1362
+ * - **Readings.** `undefined` while no child has run and none has been captured — before the
1363
+ * first `start()`. The live tail while a child is held, which reads `''` from the moment
1364
+ * that child is spawned until it writes. The captured tail after the child ended, whether
1365
+ * it exited on its own or `close()` terminated it, and `''` there for a child that ran and
1366
+ * wrote nothing — an empty tail is a real reading of a silent child, distinct from the
1367
+ * absent one.
1368
+ * - **Lifetime.** The tail follows the child that produced it. The supervisor FREEZES it at
1369
+ * that child's terminal moment — the moment `close()`'s teardown resolves past, and the
1370
+ * moment the exit that fires this transport's `close` settles at — and this transport keeps
1371
+ * reading that same child afterwards. The frozen value never moves again, which is what
1372
+ * makes a late read stable: a detached descendant holding the child's inherited stderr can
1373
+ * still write bytes after `close()` resolves, and those bytes reach no reading this
1374
+ * transport reports. The next `start()` replaces the held child, so a replacement never
1375
+ * reports its predecessor's stderr as current. Lifetimes never overlap: a `start()` issued
1376
+ * while a `close()` is still tearing down waits for that teardown to report `close` before
1377
+ * it opens the next one, so an older tail cannot arrive over a newer one however the calls
1378
+ * interleave. Read the tail before you open a replacement, and read how far it reaches
1379
+ * inside one `close` emit off the way the lifetime ended. An explicit `close()` still holds
1380
+ * its teardown barrier while those listeners run, so a `start()` one of them calls parks
1381
+ * behind it and every later listener reads the ended child's frozen tail. A natural exit
1382
+ * holds that barrier only across the `error` it reports at that end, so a restart begun
1383
+ * THERE parks until `close` has been delivered, while a `close` listener that calls
1384
+ * `start()` opens the next lifetime itself and replaces the value every listener after it
1385
+ * would have read.
1386
+ * - **What the close path carries.** The frozen value is what the supervisor had received by
1387
+ * that terminal moment, not the child's complete output.
1388
+ * Windows ends the tree with `taskkill /F /T`, which nothing in the child can intercept: a
1389
+ * `SIGTERM` handler never runs there, so the bytes it would have written never exist. A
1390
+ * child that ends on its own closes its stderr first, and THAT tail is complete.
1391
+ * Where that moment arrived at the supervisor's `drain` bound rather than at the child's
1392
+ * own stream close, the tail stops at the cutoff and later diagnostics may have existed;
1393
+ * the transport emits an `error` naming that lifetime, so a partial tail reads as partial.
1394
+ * - **Bound.** The supervisor keeps the END of the child's raw stderr bytes, at most
1395
+ * `@orkestrel/process`'s {@link import('@orkestrel/process').PROCESS_EVIDENCE} (2048
1396
+ * bytes under 0.0.6). A child that writes more than the bound loses its earliest output
1397
+ * and keeps its last, which is the half that names why it died. The bound counts raw
1398
+ * bytes before decoding rather than characters, so multibyte output yields fewer
1399
+ * characters than an ASCII run over the same byte window. The kept bytes never begin
1400
+ * inside a multibyte sequence: where the cut lands mid-character the start retreats to
1401
+ * that character's first byte, so the tail decodes without a replacement character and
1402
+ * can hold a few bytes fewer than the bound.
1403
+ * - **A spawn fault leaves no tail.** A spawn that produced no child wrote no stderr, so
1404
+ * `evidence` reads `''` for that lifetime. Its cause — the host's `ENOENT` for a command
1405
+ * that does not resolve — arrives on the `error` event instead.
1406
+ */
1407
+ readonly evidence: string | undefined;
1408
+ }
1409
+
1295
1410
  /**
1296
1411
  * Options for `createStdioClientTransport` — the child process to spawn as a
1297
1412
  * stdio-framed MCP server (newline-delimited JSON-RPC over `stdin`/`stdout`).
@@ -1304,11 +1419,34 @@ export declare class StdioClientTransport implements MCPClientTransportInterface
1304
1419
  * OMITTED the child inherits the full `process.env`, when PROVIDED each named key overrides
1305
1420
  * the inherited value while every unlisted key is still inherited. This transport cannot
1306
1421
  * REPLACE the inherited environment entirely — the supervisor always merges over the parent.
1422
+ * - `delivery` — the bound in milliseconds on one unconfirmed write to the child's `stdin`;
1423
+ * an explicit `0` opts out. Defaults to {@link import('./constants.js').DEFAULT_MCP_DELIVERY}.
1307
1424
  */
1308
1425
  export declare interface StdioClientTransportOptions {
1309
1426
  readonly command: string;
1310
1427
  readonly args?: readonly string[];
1311
1428
  readonly env?: Readonly<Record<string, string>>;
1429
+ /**
1430
+ * Bounds the wait on one unconfirmed `send` write to the child's `stdin`, in milliseconds.
1431
+ *
1432
+ * @remarks
1433
+ * A full pipe is the case this bounds: a live child that never reads its `stdin` leaves the
1434
+ * kernel unable to confirm the write, and the supervisor holds that write open. When the
1435
+ * window elapses the supervisor answers that write `false`, which this transport surfaces as
1436
+ * a rejection, so the caller learns the message did not land instead of waiting on a peer
1437
+ * that will never read it. Default: {@link import('./constants.js').DEFAULT_MCP_DELIVERY}.
1438
+ *
1439
+ * An explicit `0` opts out: the bound is off, and an unconfirmed write stays pending until
1440
+ * the channel faults or teardown settles it. Omission does NOT opt out here, which is where
1441
+ * this option diverges from the supervisor's own `delivery` on {@link
1442
+ * import('@orkestrel/process').ProcessOptions} — omitted THERE disables the bound, omitted
1443
+ * HERE selects the default.
1444
+ *
1445
+ * An out-of-range value surfaces at `start()` rather than at construction. This transport
1446
+ * forwards the value verbatim and adds no validator of its own, so the supervisor's own timer
1447
+ * range is what rejects it, at the moment it spawns the child.
1448
+ */
1449
+ readonly delivery?: number;
1312
1450
  }
1313
1451
 
1314
1452
  /**
@@ -324,22 +324,28 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
324
324
 
325
325
  /**
326
326
  * Creates the stdio CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
327
- * — a {@link MCPClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
327
+ * — a {@link StdioClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
328
328
  * over newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
329
329
  * createHTTPClientTransport} and {@link createWebSocketClientTransport}.
330
330
  *
331
331
  * @remarks
332
332
  * Hand it to `createMCPClient({ transport })`: `start()` (run by `client.connect()`)
333
333
  * spawns `options.command` with `options.args` and `options.env`, piping its
334
- * `stdin`/`stdout` for the JSON-RPC channel (its `stderr` inherits the parent's for
335
- * diagnostics). Each JSON-RPC message the client `send`s is written as one
334
+ * `stdin`/`stdout` for the JSON-RPC channel. The child's `stderr` is piped too, and
335
+ * retained as a bounded tail this transport reports as `evidence` the parent never
336
+ * inherits it. Each JSON-RPC message the client `send`s is written as one
336
337
  * newline-terminated line to the child's `stdin`; each decoded reply line from the
337
338
  * child's `stdout` is surfaced on the transport's `message` event for the client's
338
- * id correlation.
339
+ * id correlation. That write is bounded: a child that stays alive without ever reading
340
+ * its `stdin` fills the pipe, and `options.delivery` is how long the unconfirmed write
341
+ * waits before the `send` rejects. An omitted `delivery` selects {@link
342
+ * import('./constants.js').DEFAULT_MCP_DELIVERY}; an explicit `0` removes the bound.
339
343
  *
340
344
  * @param options - `command` (the executable to spawn; REQUIRED), optional `args`,
341
- * and optional `env`; see {@link StdioClientTransportOptions}
342
- * @returns A working {@link MCPClientTransportInterface} over a child process's stdio
345
+ * optional `env`, and an optional `delivery` bound in milliseconds on an unconfirmed
346
+ * `stdin` write; see {@link StdioClientTransportOptions}
347
+ * @returns A working {@link StdioClientTransportInterface} over a child process's stdio,
348
+ * whose `evidence` carries the supervised child's bounded stderr tail
343
349
  *
344
350
  * @example
345
351
  * ```ts
@@ -353,7 +359,7 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
353
359
  * const tools = await client.tools()
354
360
  * ```
355
361
  */
356
- export declare function createStdioClientTransport(options: StdioClientTransportOptions): MCPClientTransportInterface;
362
+ export declare function createStdioClientTransport(options: StdioClientTransportOptions): StdioClientTransportInterface;
357
363
 
358
364
  /**
359
365
  * Creates the MCP stdio transport INGRESS — pumps a transport-agnostic {@link
@@ -496,6 +502,19 @@ export declare function createWebSocketServer(mcp: MCPDispatcherInterface, optio
496
502
  */
497
503
  export declare function decodeEvent(data: string): JSONRPCMessage | undefined;
498
504
 
505
+ /**
506
+ * The default bound in milliseconds on one unconfirmed write to a stdio client transport's
507
+ * child `stdin` — the `delivery` a `createStdioClientTransport` caller who supplies none gets.
508
+ *
509
+ * @remarks
510
+ * Ten seconds. The load-bearing property is the ordering, not the magnitude: this bound stays
511
+ * BELOW {@link import('@orkestrel/mcp').DEFAULT_MCP_REQUEST_TIMEOUT}, so a write the child never
512
+ * reads fails as an undeliverable message while the request that carried it is still open,
513
+ * rather than being masked by that request's own deadline expiring first. Override per
514
+ * transport with `delivery`; an explicit `0` there removes the bound.
515
+ */
516
+ export declare const DEFAULT_MCP_DELIVERY = 10000;
517
+
499
518
  /**
500
519
  * The default interval in milliseconds between SSE keepalive comments on held-open MCP
501
520
  * responses.
@@ -1242,7 +1261,7 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1242
1261
 
1243
1262
  /**
1244
1263
  * The stdio CLIENT transport for the Model Context Protocol — a
1245
- * {@link MCPClientTransportInterface} that drives a CHILD PROCESS MCP server over
1264
+ * {@link StdioClientTransportInterface} that drives a CHILD PROCESS MCP server over
1246
1265
  * newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
1247
1266
  * import('./HTTPClientTransport.js').HTTPClientTransport} and {@link
1248
1267
  * import('./WebSocketClientTransport.js').WebSocketClientTransport}.
@@ -1260,19 +1279,36 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1260
1279
  * - **Outbound (`send`).** `send(message)` writes one newline-terminated `JSON.stringify`d line
1261
1280
  * through the supervisor's `send` and AWAITS its answer, so this promise settles only after the
1262
1281
  * host reports the line handled rather than the moment the write is queued. The supervisor never
1263
- * rejects — it answers `false` for a channel that was closed, destroyed, or ended, and for a write
1264
- * that failed so a `false` answer REJECTS here with the same not-connected error a transport
1265
- * that was never started raises. A dead peer surfaces at the caller instead of vanishing.
1266
- * - **`close()`** releases this transport's line pump without waiting for the child's stdout
1267
- * iterator, then runs the supervisor's bounded `SIGTERM` grace `SIGKILL` group-kill and
1268
- * teardown before firing `close` once (idempotent). A descendant can retain an inherited stdout
1269
- * pipe after the child exits; the pump's release barrier keeps that substrate limit from keeping
1270
- * this transport's `close()` pending. On a POSIX host the child leads its own process group, so
1271
- * the group-kill reaches its grandchildren rather than orphaning them.
1282
+ * rejects — it answers `false` for a channel that was closed, destroyed, or ended, for a write
1283
+ * that failed, or for one that remained unconfirmed through `delivery`. A call made without a
1284
+ * live child rejects as not connected; a `false` answer from a live child rejects as unable to
1285
+ * deliver. The supervisor does not disclose which cause produced that answer.
1286
+ * - **`close()`** runs the supervisor's bounded termination and teardown, then fires `close` once
1287
+ * (idempotent). That teardown reaches the child's TERMINAL MOMENT, where the supervisor freezes
1288
+ * `evidence`, ends `lines`, and settles `exit` together, so this transport needs no release of
1289
+ * its own to get its line pump back: the stream ends under the pump rather than throwing at it.
1290
+ * A line the supervisor had already framed behind the one being delivered is dropped rather than
1291
+ * emitted onto a transport whose teardown has begun. A `close()` issued while that teardown runs
1292
+ * joins it rather than opening a second one, so it resolves only after `close` has fired, and a
1293
+ * `start()` issued while it runs waits behind the same barrier, so lifetimes never overlap. A
1294
+ * descendant can retain an inherited stdout pipe after the child exits; the supervisor's `drain`
1295
+ * bound cuts that wait off, so this transport's `close()` settles within that bound rather than
1296
+ * on the descendant. The termination itself belongs to the host: a POSIX host signals the
1297
+ * child's own process group `SIGTERM`, waits the grace window, then `SIGKILL`s through the same
1298
+ * route, so the kill reaches grandchildren rather than orphaning them, while Windows ends the
1299
+ * tree with `taskkill /F /T`, which nothing in the child can intercept.
1300
+ * - **Evidence.** `evidence` reports that retained stderr tail off the HELD child — its live tail
1301
+ * while the child runs, and the value the supervisor froze at that child's terminal moment
1302
+ * afterwards. The reference is held past that moment and replaced only by the next `start()`,
1303
+ * which is what keeps a post-`close()` read stable without a private copy: the frozen value
1304
+ * never moves again, so a detached descendant writing to the inherited stderr after the cutoff
1305
+ * cannot grow it. See {@link StdioClientTransportInterface.evidence} for the readings and the
1306
+ * byte bound.
1272
1307
  * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
1273
1308
  * emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
1274
- * fault, including the child spawn cause the supervisor surfaces), distinct from the emitter's
1275
- * own listener-error channel.
1309
+ * fault, including the child spawn cause the supervisor surfaces and the notice that this
1310
+ * lifetime's `evidence` was cut off at the `drain` bound), distinct from the emitter's own
1311
+ * listener-error channel.
1276
1312
  *
1277
1313
  * @example
1278
1314
  * ```ts
@@ -1281,17 +1317,96 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1281
1317
  * await client.connect() // start() spawns the child, then the MCP initialize runs over stdio
1282
1318
  * ```
1283
1319
  */
1284
- export declare class StdioClientTransport implements MCPClientTransportInterface_2 {
1320
+ export declare class StdioClientTransport implements StdioClientTransportInterface {
1285
1321
  #private;
1286
1322
  constructor(options: StdioClientTransportOptions);
1287
1323
  get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
1288
1324
  get session(): string | undefined;
1289
1325
  get duplex(): boolean;
1326
+ get evidence(): string | undefined;
1290
1327
  start(): Promise<void>;
1328
+ /**
1329
+ * Sends one newline-delimited JSON-RPC message to the live child.
1330
+ *
1331
+ * @param message - The message to write to the child's `stdin`
1332
+ * @returns Resolves when the supervisor confirms the write
1333
+ * @throws Thrown with `stdio transport is not connected` when no live child is available before
1334
+ * the write
1335
+ * @throws Thrown with `stdio transport could not deliver the message` when a live child's write
1336
+ * resolves `false`
1337
+ */
1291
1338
  send(message: JSONRPCMessage_2): Promise<void>;
1292
1339
  close(): Promise<void>;
1293
1340
  }
1294
1341
 
1342
+ /**
1343
+ * The contract `createStdioClientTransport` returns — a {@link MCPClientTransportInterface}
1344
+ * that also reports the supervised child's stderr tail, the diagnostic a child that dies at
1345
+ * startup leaves behind.
1346
+ *
1347
+ * @remarks
1348
+ * This contract adds `evidence` and changes nothing else: `emitter`, `session`, `duplex`,
1349
+ * `start`, `send`, and `close` are the shared client-transport surface, unchanged. It sits here
1350
+ * rather than on {@link MCPClientTransportInterface} because a transport that supervises no child —
1351
+ * Streamable HTTP, WebSocket, a `MessagePort` pair — has no such tail, and a member every one
1352
+ * of them answers `undefined` to forever is a stdio detail rather than a shared contract. A
1353
+ * consumer that widens this value back to {@link MCPClientTransportInterface}, including by
1354
+ * reading `client.transport`, loses the reader and must keep the original reference.
1355
+ */
1356
+ export declare interface StdioClientTransportInterface extends MCPClientTransportInterface {
1357
+ /**
1358
+ * The supervised child's decoded stderr tail — live while a child is held, and the value
1359
+ * captured at that child's end afterwards.
1360
+ *
1361
+ * @remarks
1362
+ * - **Readings.** `undefined` while no child has run and none has been captured — before the
1363
+ * first `start()`. The live tail while a child is held, which reads `''` from the moment
1364
+ * that child is spawned until it writes. The captured tail after the child ended, whether
1365
+ * it exited on its own or `close()` terminated it, and `''` there for a child that ran and
1366
+ * wrote nothing — an empty tail is a real reading of a silent child, distinct from the
1367
+ * absent one.
1368
+ * - **Lifetime.** The tail follows the child that produced it. The supervisor FREEZES it at
1369
+ * that child's terminal moment — the moment `close()`'s teardown resolves past, and the
1370
+ * moment the exit that fires this transport's `close` settles at — and this transport keeps
1371
+ * reading that same child afterwards. The frozen value never moves again, which is what
1372
+ * makes a late read stable: a detached descendant holding the child's inherited stderr can
1373
+ * still write bytes after `close()` resolves, and those bytes reach no reading this
1374
+ * transport reports. The next `start()` replaces the held child, so a replacement never
1375
+ * reports its predecessor's stderr as current. Lifetimes never overlap: a `start()` issued
1376
+ * while a `close()` is still tearing down waits for that teardown to report `close` before
1377
+ * it opens the next one, so an older tail cannot arrive over a newer one however the calls
1378
+ * interleave. Read the tail before you open a replacement, and read how far it reaches
1379
+ * inside one `close` emit off the way the lifetime ended. An explicit `close()` still holds
1380
+ * its teardown barrier while those listeners run, so a `start()` one of them calls parks
1381
+ * behind it and every later listener reads the ended child's frozen tail. A natural exit
1382
+ * holds that barrier only across the `error` it reports at that end, so a restart begun
1383
+ * THERE parks until `close` has been delivered, while a `close` listener that calls
1384
+ * `start()` opens the next lifetime itself and replaces the value every listener after it
1385
+ * would have read.
1386
+ * - **What the close path carries.** The frozen value is what the supervisor had received by
1387
+ * that terminal moment, not the child's complete output.
1388
+ * Windows ends the tree with `taskkill /F /T`, which nothing in the child can intercept: a
1389
+ * `SIGTERM` handler never runs there, so the bytes it would have written never exist. A
1390
+ * child that ends on its own closes its stderr first, and THAT tail is complete.
1391
+ * Where that moment arrived at the supervisor's `drain` bound rather than at the child's
1392
+ * own stream close, the tail stops at the cutoff and later diagnostics may have existed;
1393
+ * the transport emits an `error` naming that lifetime, so a partial tail reads as partial.
1394
+ * - **Bound.** The supervisor keeps the END of the child's raw stderr bytes, at most
1395
+ * `@orkestrel/process`'s {@link import('@orkestrel/process').PROCESS_EVIDENCE} (2048
1396
+ * bytes under 0.0.6). A child that writes more than the bound loses its earliest output
1397
+ * and keeps its last, which is the half that names why it died. The bound counts raw
1398
+ * bytes before decoding rather than characters, so multibyte output yields fewer
1399
+ * characters than an ASCII run over the same byte window. The kept bytes never begin
1400
+ * inside a multibyte sequence: where the cut lands mid-character the start retreats to
1401
+ * that character's first byte, so the tail decodes without a replacement character and
1402
+ * can hold a few bytes fewer than the bound.
1403
+ * - **A spawn fault leaves no tail.** A spawn that produced no child wrote no stderr, so
1404
+ * `evidence` reads `''` for that lifetime. Its cause — the host's `ENOENT` for a command
1405
+ * that does not resolve — arrives on the `error` event instead.
1406
+ */
1407
+ readonly evidence: string | undefined;
1408
+ }
1409
+
1295
1410
  /**
1296
1411
  * Options for `createStdioClientTransport` — the child process to spawn as a
1297
1412
  * stdio-framed MCP server (newline-delimited JSON-RPC over `stdin`/`stdout`).
@@ -1304,11 +1419,34 @@ export declare class StdioClientTransport implements MCPClientTransportInterface
1304
1419
  * OMITTED the child inherits the full `process.env`, when PROVIDED each named key overrides
1305
1420
  * the inherited value while every unlisted key is still inherited. This transport cannot
1306
1421
  * REPLACE the inherited environment entirely — the supervisor always merges over the parent.
1422
+ * - `delivery` — the bound in milliseconds on one unconfirmed write to the child's `stdin`;
1423
+ * an explicit `0` opts out. Defaults to {@link import('./constants.js').DEFAULT_MCP_DELIVERY}.
1307
1424
  */
1308
1425
  export declare interface StdioClientTransportOptions {
1309
1426
  readonly command: string;
1310
1427
  readonly args?: readonly string[];
1311
1428
  readonly env?: Readonly<Record<string, string>>;
1429
+ /**
1430
+ * Bounds the wait on one unconfirmed `send` write to the child's `stdin`, in milliseconds.
1431
+ *
1432
+ * @remarks
1433
+ * A full pipe is the case this bounds: a live child that never reads its `stdin` leaves the
1434
+ * kernel unable to confirm the write, and the supervisor holds that write open. When the
1435
+ * window elapses the supervisor answers that write `false`, which this transport surfaces as
1436
+ * a rejection, so the caller learns the message did not land instead of waiting on a peer
1437
+ * that will never read it. Default: {@link import('./constants.js').DEFAULT_MCP_DELIVERY}.
1438
+ *
1439
+ * An explicit `0` opts out: the bound is off, and an unconfirmed write stays pending until
1440
+ * the channel faults or teardown settles it. Omission does NOT opt out here, which is where
1441
+ * this option diverges from the supervisor's own `delivery` on {@link
1442
+ * import('@orkestrel/process').ProcessOptions} — omitted THERE disables the bound, omitted
1443
+ * HERE selects the default.
1444
+ *
1445
+ * An out-of-range value surfaces at `start()` rather than at construction. This transport
1446
+ * forwards the value verbatim and adds no validator of its own, so the supervisor's own timer
1447
+ * range is what rejects it, at the moment it spawns the child.
1448
+ */
1449
+ readonly delivery?: number;
1312
1450
  }
1313
1451
 
1314
1452
  /**