@orkestrel/mcp 0.0.18 → 0.0.20

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.
@@ -55,17 +55,17 @@ export declare function acceptsEventStream(request: Request): boolean;
55
55
  export declare function allowsOrigin(request: Request, options?: MCPOriginOptions): boolean;
56
56
 
57
57
  /**
58
- * Bridge a message-channel {@link MCPClientTransportInterface} (the shape the stdio and
58
+ * Bridges a message-channel {@link MCPClientTransportInterface} (the shape the stdio and
59
59
  * WebSocket SERVER transports already implement) into the environment-agnostic
60
- * {@link import('@src/core').MCPTransportInterface} port — the adapter
60
+ * {@link import('@orkestrel/mcp').MCPTransportInterface} port — the adapter
61
61
  * {@link import('./factories.js').createStdioServer} and {@link
62
62
  * import('./factories.js').createWebSocketServer} pipe through `bindServer`, so the
63
- * request/reply/error pump those two factories used to hand-roll identically now
63
+ * request/reply/error pump those factories used to hand-roll identically now
64
64
  * lives ONCE in the core binder.
65
65
  *
66
66
  * @remarks
67
67
  * `send` decodes the already-serialized reply string back to a {@link JSONRPCMessage}
68
- * and writes it via `transport.send` (the SAME `JSON.stringify` the underlying
68
+ * and writes it through `transport.send` (the same `JSON.stringify` the underlying
69
69
  * transport already performs, so the wire bytes are unchanged). `listen` filters
70
70
  * `transport`'s `message` event to INVOCATIONS ONLY — requests and notifications, never a
71
71
  * stray response, exactly as the prior hand-rolled pumps did — and re-serializes each one
@@ -83,24 +83,24 @@ export declare function allowsOrigin(request: Request, options?: MCPOriginOption
83
83
  * decode rather than after it. Removing the cost means giving `MCPTransportInterface` a
84
84
  * message-shaped face beside its string one, which every transport would then carry.
85
85
  *
86
- * @remarks Per {@link import('@src/core').MCPTransportInterface}, `listen`/`closed`
86
+ * @remarks Per {@link import('@orkestrel/mcp').MCPTransportInterface}, `listen`/`closed`
87
87
  * each hold THE SINGLE current handler (a second call REPLACES the first, never adds).
88
- * Since the underlying `transport.emitter` is ADD-based (`on` subscribes, never
88
+ * Because the underlying `transport.emitter` is ADD-based (`on` subscribes, never
89
89
  * replaces), this bridge installs ONE stable emitter listener per event on first use
90
- * and re-routes it to whichever handler is CURRENTLY registered (`undefined` while
90
+ * and re-routes it to whichever handler is active (`undefined` while
91
91
  * none is), so rebinding never double-dispatches.
92
92
  *
93
- * @remarks A response whose `result` serializes away (e.g. `undefined`) is dropped by
93
+ * @remarks A response whose `result` serializes away (for example, `undefined`) is dropped by
94
94
  * the message validators on the wire's decode side — an asymmetry the stdio/WS carrier
95
- * shares with the streamable-HTTP face, since both round-trip through `JSON.stringify`
95
+ * shares with the streamable-HTTP face, because both round-trip through `JSON.stringify`
96
96
  * / `JSON.parse` before re-validation.
97
97
  *
98
98
  * @param transport - The message-channel transport to bridge (stdio or WebSocket)
99
- * @returns An {@link import('@src/core').MCPTransportInterface} `bindServer` can drive
99
+ * @returns An {@link import('@orkestrel/mcp').MCPTransportInterface} `bindServer` can drive
100
100
  *
101
101
  * @example
102
102
  * ```ts
103
- * import { bindServer } from '@src/core'
103
+ * import { bindServer } from '@orkestrel/mcp'
104
104
  *
105
105
  * const transport = new StdioServerTransport(process.stdin, process.stdout)
106
106
  * bindServer(mcp, bridgeMessageTransport(transport))
@@ -109,7 +109,7 @@ export declare function allowsOrigin(request: Request, options?: MCPOriginOption
109
109
  export declare function bridgeMessageTransport(transport: MCPClientTransportInterface): MCPTransportInterface;
110
110
 
111
111
  /**
112
- * Create the HTTP CLIENT transport for an {@link import('@src/core').MCPClientInterface}
112
+ * Creates the HTTP CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
113
113
  * — a {@link MCPClientTransportInterface} that drives a REMOTE Streamable-HTTP MCP server
114
114
  * over `fetch`. The egress mirror of {@link createMCPRoutes}.
115
115
  *
@@ -117,9 +117,9 @@ export declare function bridgeMessageTransport(transport: MCPClientTransportInte
117
117
  * Hand it to `createMCPClient({ transport })`: each JSON-RPC message the client sends is
118
118
  * `POST`ed to `options.url` with `content-type: application/json` and an `Accept` of
119
119
  * both `application/json` and `text/event-stream` (the server answers with EITHER — a
120
- * plain JSON envelope or a Streamable-HTTP SSE `data:` event, decoded via `@orkestrel/sse`),
120
+ * plain JSON envelope or a Streamable-HTTP SSE `data:` event, decoded with `@orkestrel/sse`),
121
121
  * and the reply is surfaced on the transport's `message` event for the client's id
122
- * correlation. Add `options.headers` (e.g. an `Authorization` bearer) to reach a guarded
122
+ * correlation. Add `options.headers` (for example, an `Authorization` bearer) to reach a guarded
123
123
  * server. `start` / `close` hold no connection; against a STATEFUL server it captures the
124
124
  * `mcp-session-id` from `initialize` and echoes it on later requests. It also captures
125
125
  * the initialize result's `protocolVersion` and sends `mcp-protocol-version` alone on each
@@ -128,13 +128,13 @@ export declare function bridgeMessageTransport(transport: MCPClientTransportInte
128
128
  *
129
129
  * @param options - `url` (the remote endpoint; REQUIRED), optional `headers` merged onto
130
130
  * every request, optional `fetch` (default `globalThis.fetch`), and optional `timeout`
131
- * (ms, applied via `AbortSignal.timeout`); see {@link HTTPClientTransportOptions}
131
+ * (ms, applied with `AbortSignal.timeout`); see {@link HTTPClientTransportOptions}
132
132
  * @returns A working {@link MCPClientTransportInterface} over `fetch`
133
133
  *
134
134
  * @example
135
135
  * ```ts
136
- * import { createMCPClient } from '@src/core'
137
- * import { createHTTPClientTransport } from '@src/server'
136
+ * import { createMCPClient } from '@orkestrel/mcp'
137
+ * import { createHTTPClientTransport } from '@orkestrel/mcp/server'
138
138
  *
139
139
  * const client = createMCPClient({
140
140
  * transport: createHTTPClientTransport({ url: 'http://localhost:3000/mcp' }),
@@ -146,7 +146,7 @@ export declare function bridgeMessageTransport(transport: MCPClientTransportInte
146
146
  export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): MCPClientTransportInterface;
147
147
 
148
148
  /**
149
- * Adapt the installed server token primitives to the host-neutral MCP continuation port.
149
+ * Adapts the installed server token primitives to the host-neutral MCP continuation port.
150
150
  *
151
151
  * @param secret - Current signing secret or `[current, ...older]` rotation list
152
152
  * @returns A continuation port that seals and opens opaque canonical state strings
@@ -154,7 +154,7 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
154
154
  export declare function createMCPContinuation(secret: TokenSecret): MCPContinuationInterface;
155
155
 
156
156
  /**
157
- * Create the Streamable-HTTP POST handler used by `createMCPRoutes`.
157
+ * Creates the Streamable-HTTP POST handler used by `createMCPRoutes`.
158
158
  *
159
159
  * @remarks
160
160
  * Modern requests require matching protocol/method headers and a matching name header only
@@ -180,7 +180,7 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
180
180
  * import { createToolManager } from '@orkestrel/tool'
181
181
  *
182
182
  * const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
183
- * const handler = createMCPPostHandler(createMCPLegacy(mcp), { streaming: true })
183
+ * const handler = createMCPPostHandler(createMCPLegacy(mcp), { streaming: true }) // answers `initialize` too; pass `mcp` alone for modern-only
184
184
  * await handler(new Request('http://localhost/mcp', {
185
185
  * method: 'POST',
186
186
  * body: '{"jsonrpc":"2.0","method":"ping","id":1}',
@@ -190,8 +190,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
190
190
  export declare function createMCPPostHandler<TState = unknown>(mcp: MCPDispatcherInterface, options?: HTTPHandlerOptions<TState>): (request: Request, context?: RouteContext<string, TState>) => Promise<Response>;
191
191
 
192
192
  /**
193
- * Create the MCP Streamable-HTTP transport routes — mounts a transport-agnostic
194
- * {@link MCPDispatcherInterface} (the `@src/core` dispatch boundary) on the fetch-standard router
193
+ * Creates the MCP Streamable-HTTP transport routes — mounts a transport-agnostic
194
+ * {@link MCPDispatcherInterface} (the `@orkestrel/mcp` dispatch boundary) on the fetch-standard router
195
195
  * spine, pumping each `POST` body through `mcp.dispatch`. Returns the {@link RouteInput}s to
196
196
  * hand to `router.add(...)`.
197
197
  *
@@ -214,7 +214,7 @@ export declare function createMCPPostHandler<TState = unknown>(mcp: MCPDispatche
214
214
  *
215
215
  * When `streaming` is enabled (the default) and the client `Accept`s `text/event-stream`,
216
216
  * the `200` reply is framed as a Streamable-HTTP SSE response (one `data:` event carrying
217
- * the JSON-RPC envelope, then the stream ends) via `@orkestrel/server`'s generic
217
+ * the JSON-RPC envelope, then the stream ends) through `@orkestrel/server`'s generic
218
218
  * {@link import('@orkestrel/server').openStream} seam; otherwise it is a plain JSON body.
219
219
  *
220
220
  * **Sessions are a SEPARATE, plug-and-play middleware.** `createMCPRoutes` mints / reads no
@@ -236,19 +236,20 @@ export declare function createMCPPostHandler<TState = unknown>(mcp: MCPDispatche
236
236
  *
237
237
  * @example
238
238
  * ```ts
239
- * import { createMCPLegacy, createMCPServer, createToolManager } from '@src/core'
240
- * import { createMCPRoutes } from '@src/server'
239
+ * import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
240
+ * import { createMCPRoutes } from '@orkestrel/mcp/server'
241
+ * import { createToolManager } from '@orkestrel/tool'
241
242
  *
242
243
  * const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
243
- * const routes = createMCPRoutes(createMCPLegacy(mcp)) // both eras; pass `mcp` for modern only
244
+ * const routes = createMCPRoutes(createMCPLegacy(mcp)) // answers `initialize` too; pass `mcp` alone for modern-only
244
245
  * ```
245
246
  */
246
247
  export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInterface, options?: HTTPTransportOptions<TState>): ReadonlyArray<RouteInput<string, TState>>;
247
248
 
248
249
  /**
249
- * Create the native MCP session {@link MiddlewareHandler} — the plug-and-play stateful layer
250
+ * Creates the native MCP session {@link MiddlewareHandler} — the plug-and-play stateful layer
250
251
  * that fronts a session-agnostic {@link import('./factories.js').createMCPRoutes}. Compose it
251
- * via `router.use(createMCPSession())` (or the equivalent middleware seam), mirroring any
252
+ * with `router.use(createMCPSession())` (or the equivalent middleware seam), mirroring any
252
253
  * other closure-scoped stateful middleware. Has NO dependency on `@orkestrel/middleware` — the
253
254
  * session store, mint-on-`initialize`, and resumable stream are all native to this package.
254
255
  *
@@ -257,11 +258,11 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
257
258
  * `path` (default {@link DEFAULT_MCP_PATH}); a request to any other path passes straight
258
259
  * through (`next()`).
259
260
  *
260
- * A modern-shaped POST also passes straight through via `next()`, ignoring any session id.
261
+ * A modern-shaped POST also passes straight through with `next()`, ignoring any session id.
261
262
  * The remaining behavior is the legacy session layer:
262
263
  *
263
264
  * - **`POST {path}`.** Buffers `const text = await request.text()` (so the downstream route
264
- * can re-read it via a freshly-built forwarded `Request`). Resolves a session via {@link
265
+ * can re-read it from a freshly-built forwarded `Request`). Resolves a session through {@link
265
266
  * readSessionHeader}: a VALID id touches the entry and sets `context.state.session`; an
266
267
  * ABSENT / unknown id whose (guarded) body parses to an `initialize` request ({@link
267
268
  * isInitializeRequest}) MINTS a fresh {@link MCPSession} (`crypto.randomUUID()`, `capacity`)
@@ -276,7 +277,7 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
276
277
  * a `DELETE` arriving while the request was suspended is not undone.
277
278
  * - **`GET {path}`.** Resolves the session the same way (no mint — only `initialize` mints);
278
279
  * an invalid / unknown id is the same `404`. A valid session opens the resumable
279
- * server→client stream via `@orkestrel/server`'s {@link import('@orkestrel/server').openStream}:
280
+ * server→client stream through `@orkestrel/server`'s {@link import('@orkestrel/server').openStream}:
280
281
  * replays every event after the client's `Last-Event-ID` ({@link readLastEventId}) BEFORE
281
282
  * attaching the stream for live pushes, then attaches; cancellation of the streamed response
282
283
  * body composes with `request.signal` and detaches it. Long-lived — never `end()`ed here.
@@ -300,18 +301,20 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
300
301
  *
301
302
  * @example
302
303
  * ```ts
303
- * import { createMCPServer, createToolManager } from '@src/core'
304
- * import { createMCPRoutes, createMCPSession } from '@src/server'
304
+ * import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
305
+ * import { createMCPRoutes, createMCPSession } from '@orkestrel/mcp/server'
306
+ * import { createToolManager } from '@orkestrel/tool'
305
307
  *
306
308
  * const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
307
309
  * router.use(createMCPSession({ ttl: 60_000 })) // stateful: mint + validate + resumable GET / DELETE
308
- * router.add(createMCPRoutes(mcp)) // the route stays session-agnostic
310
+ * // The route stays session-agnostic:
311
+ * router.add(createMCPRoutes(createMCPLegacy(mcp))) // answers `initialize` too; pass `mcp` alone for modern-only
309
312
  * ```
310
313
  */
311
314
  export declare function createMCPSession<TState extends MCPSessionState>(options?: MCPSessionOptions): MiddlewareHandler<TState>;
312
315
 
313
316
  /**
314
- * Create a readable stream from its pull and cancellation behaviours.
317
+ * Creates a readable stream from its pull and cancellation behaviours.
315
318
  *
316
319
  * @param pull - The behaviour that supplies the stream's next chunk
317
320
  * @param cancel - The behaviour that releases the stream after consumer cancellation
@@ -320,7 +323,7 @@ export declare function createMCPSession<TState extends MCPSessionState>(options
320
323
  export declare function createReadableStream<T>(pull: (controller: ReadableStreamDefaultController<T>) => void | PromiseLike<void>, cancel: (reason?: unknown) => void | PromiseLike<void>): ReadableStream<T>;
321
324
 
322
325
  /**
323
- * Create the stdio CLIENT transport for an {@link import('@src/core').MCPClientInterface}
326
+ * Creates the stdio CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
324
327
  * — a {@link MCPClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
325
328
  * over newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
326
329
  * createHTTPClientTransport} and {@link createWebSocketClientTransport}.
@@ -340,8 +343,8 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
340
343
  *
341
344
  * @example
342
345
  * ```ts
343
- * import { createMCPClient } from '@src/core'
344
- * import { createStdioClientTransport } from '@src/server'
346
+ * import { createMCPClient } from '@orkestrel/mcp'
347
+ * import { createStdioClientTransport } from '@orkestrel/mcp/server'
345
348
  *
346
349
  * const client = createMCPClient({
347
350
  * transport: createStdioClientTransport({ command: 'node', args: ['./server.js'] }),
@@ -353,16 +356,16 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
353
356
  export declare function createStdioClientTransport(options: StdioClientTransportOptions): MCPClientTransportInterface;
354
357
 
355
358
  /**
356
- * Create the MCP stdio transport INGRESS — pumps a transport-agnostic {@link
359
+ * Creates the MCP stdio transport INGRESS — pumps a transport-agnostic {@link
357
360
  * MCPDispatcherInterface} over newline-delimited JSON-RPC on `stdin`/`stdout` (or an
358
361
  * injected stream pair), the stdio mirror of {@link createWebSocketServer}.
359
362
  *
360
363
  * @remarks
361
364
  * Wraps `options.input` (default `process.stdin`) / `options.output` (default
362
365
  * `process.stdout`) in a {@link import('./transports/StdioServerTransport.js').StdioServerTransport}
363
- * and pipes it through the core {@link import('@src/core').MCPTransportInterface} port
364
- * via {@link import('./helpers.js').bridgeMessageTransport} + {@link
365
- * import('@src/core').bindServer}: each inbound REQUEST runs through `mcp.dispatch`, and
366
+ * and pipes it through the core {@link import('@orkestrel/mcp').MCPTransportInterface} port
367
+ * through {@link import('./helpers.js').bridgeMessageTransport} + {@link
368
+ * import('@orkestrel/mcp').bindServer}: each inbound REQUEST runs through `mcp.dispatch`, and
366
369
  * a defined response is written back as a newline-terminated line — a NOTIFICATION
367
370
  * writes nothing, and a non-request message is ignored. A `dispatch` / `send` fault
368
371
  * surfaces on `mcp.emitter`'s `error` event rather than escaping the (async) message
@@ -375,11 +378,13 @@ export declare function createStdioClientTransport(options: StdioClientTransport
375
378
  *
376
379
  * @example
377
380
  * ```ts
378
- * import { createMCPServer, createToolManager } from '@src/core'
379
- * import { createStdioServer } from '@src/server'
381
+ * import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
382
+ * import { createStdioServer } from '@orkestrel/mcp/server'
383
+ * import { createToolManager } from '@orkestrel/tool'
380
384
  *
381
385
  * const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
382
- * createStdioServer(mcp).start() // an MCP client now connects over this process's stdio
386
+ * // An MCP client now connects over this process's stdio:
387
+ * createStdioServer(createMCPLegacy(mcp)).start() // answers `initialize` too; pass `mcp` alone for modern-only
383
388
  * ```
384
389
  */
385
390
  export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): {
@@ -388,7 +393,7 @@ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?:
388
393
  };
389
394
 
390
395
  /**
391
- * Create the WebSocket CLIENT transport for an {@link import('@src/core').MCPClientInterface}
396
+ * Creates the WebSocket CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
392
397
  * — a {@link MCPClientTransportInterface} that drives a REMOTE MCP server over a WebSocket. The
393
398
  * egress mirror of {@link createWebSocketServer} and the WebSocket sibling of {@link
394
399
  * createHTTPClientTransport}.
@@ -397,11 +402,11 @@ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?:
397
402
  * Hand it to `createMCPClient({ transport })`: `start()` (run by `client.connect()`) performs
398
403
  * the RFC 6455 client handshake against `options.url` (accepting a `ws://` / `wss://` or an
399
404
  * `http://` / `https://` URL — a `ws(s)` scheme is converted to `http(s)` for the underlying
400
- * upgrade request), validates the `Sec-WebSocket-Accept` (via `@orkestrel/websocket`'s
405
+ * upgrade request), validates the `Sec-WebSocket-Accept` (with `@orkestrel/websocket`'s
401
406
  * `computeWebSocketAccept`), and opens a persistent bidirectional frame channel; each JSON-RPC
402
407
  * message the client `send`s is written as one masked text frame, and each decoded reply is
403
408
  * surfaced on the transport's `message` event for the client's id correlation. Add
404
- * `options.headers` (e.g. an `Authorization` bearer) to reach a guarded server.
409
+ * `options.headers` (for example, an `Authorization` bearer) to reach a guarded server.
405
410
  *
406
411
  * @param options - `url` (the remote WebSocket endpoint; REQUIRED) and optional `headers`
407
412
  * merged onto the upgrade request; see {@link WebSocketClientTransportOptions}
@@ -409,8 +414,8 @@ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?:
409
414
  *
410
415
  * @example
411
416
  * ```ts
412
- * import { createMCPClient } from '@src/core'
413
- * import { createWebSocketClientTransport } from '@src/server'
417
+ * import { createMCPClient } from '@orkestrel/mcp'
418
+ * import { createWebSocketClientTransport } from '@orkestrel/mcp/server'
414
419
  *
415
420
  * const client = createMCPClient({
416
421
  * transport: createWebSocketClientTransport({ url: 'ws://localhost:3000/mcp' }),
@@ -422,7 +427,7 @@ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?:
422
427
  export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): MCPClientTransportInterface;
423
428
 
424
429
  /**
425
- * Create the MCP WebSocket transport INGRESS — an {@link UpgradeHandler} that exposes a
430
+ * Creates the MCP WebSocket transport INGRESS — an {@link UpgradeHandler} that exposes a
426
431
  * transport-agnostic {@link MCPDispatcherInterface} over a WebSocket, the WebSocket mirror of
427
432
  * {@link createMCPRoutes}. Register it on the spine's upgrade seam.
428
433
  *
@@ -437,11 +442,11 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
437
442
  * A decline NEVER writes to the socket (it is not yet ours) — the spine owns the unclaimed
438
443
  * outcome.
439
444
  * - **Claims (returns `true`)** otherwise: it builds `createNodeWebSocket({ socket, key, head,
440
- * protocol })` (SERVER mode → writes the `101` handshake, echoing the `subprotocol`, default
441
- * {@link MCP_WEBSOCKET_SUBPROTOCOL} `'mcp'`, and sends UNMASKED frames), wraps it in a
445
+ * protocol })` (SERVER mode → writes the `101` handshake, selects the configured subprotocol
446
+ * only when the client's offer contains it, and sends UNMASKED frames), wraps it in a
442
447
  * {@link WebSocketServerTransport}, and pipes it through the core {@link
443
- * import('@src/core').MCPTransportInterface} port via {@link
444
- * import('./helpers.js').bridgeMessageTransport} + {@link import('@src/core').bindServer}:
448
+ * import('@orkestrel/mcp').MCPTransportInterface} port through {@link
449
+ * import('./helpers.js').bridgeMessageTransport} + {@link import('@orkestrel/mcp').bindServer}:
445
450
  * each inbound REQUEST runs through `mcp.dispatch`, and a defined response is written back
446
451
  * as a frame — a NOTIFICATION sends nothing, and a non-request message (a stray response) is
447
452
  * ignored. A `dispatch` / `send` fault surfaces on `mcp.emitter`'s `error` event rather than
@@ -466,23 +471,25 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
466
471
  *
467
472
  * @example
468
473
  * ```ts
469
- * import { createMCPServer, createToolManager } from '@src/core'
470
- * import { createWebSocketServer } from '@src/server'
474
+ * import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
475
+ * import { createWebSocketServer } from '@orkestrel/mcp/server'
476
+ * import { createToolManager } from '@orkestrel/tool'
471
477
  *
472
478
  * const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
473
- * server.upgrade(createWebSocketServer(mcp, { emitter: server.emitter })) // ws://…/mcp
479
+ * // Claims the MCP upgrade at ws://…/mcp:
480
+ * server.upgrade(createWebSocketServer(createMCPLegacy(mcp), { emitter: server.emitter })) // answers `initialize` too; pass `mcp` alone for modern-only
474
481
  * ```
475
482
  */
476
483
  export declare function createWebSocketServer(mcp: MCPDispatcherInterface, options: WebSocketServerOptions): UpgradeHandler;
477
484
 
478
485
  /**
479
- * Decode one SSE event's `data` string into a {@link JSONRPCMessage}, or `undefined`
486
+ * Decodes one SSE event's `data` string into a {@link JSONRPCMessage}, or `undefined`
480
487
  * when it is not one — the per-event step {@link readEventStream} folds over.
481
488
  *
482
489
  * @remarks
483
490
  * `JSON.parse`s the `data` (the server serializes the JSON-RPC envelope as the event's
484
491
  * `data`) inside a try/catch and narrows the parsed value with `parseJSONRPCMessage`.
485
- * Total (§14): malformed JSON or a non-message value yields `undefined`, never throws.
492
+ * Total: malformed JSON or a non-message value yields `undefined`, never throws.
486
493
  *
487
494
  * @param data - One SSE event's `data` payload
488
495
  * @returns The decoded {@link JSONRPCMessage}, or `undefined`
@@ -528,19 +535,20 @@ export declare const DEFAULT_MCP_SESSION_CAPACITY = 1024;
528
535
  export declare const DEFAULT_MCP_SESSION_TTL = 300000;
529
536
 
530
537
  /**
531
- * Decode and deliver each complete newline-framed line onto a {@link
538
+ * Decodes and delivers each complete newline-framed line onto a {@link
532
539
  * MCPClientTransportEventMap} emitter — the shared per-chunk dispatch step both stdio
533
- * transports (client and server) run their {@link extractLines} output through.
540
+ * transports run their framed lines through: the server transport frames with {@link
541
+ * extractLines}, the client transport takes its lines from the process supervisor.
534
542
  *
535
543
  * @remarks
536
544
  * A blank line is skipped (a stray trailing newline). Every other line is decoded
537
545
  * with {@link decodeEvent} (`JSON.parse` + `parseJSONRPCMessage`, guarded); a
538
546
  * well-formed {@link JSONRPCMessage} emits `message`, a malformed / non-message line
539
- * emits `error` (§14 — total, never throws). Pure w.r.t. its own state — the emit is
547
+ * emits `error` (total, never throws). Pure w.r.t. its own state — the emit is
540
548
  * the caller-owned side effect.
541
549
  *
542
550
  * @param emitter - The transport's {@link EmitterInterface} to emit `message` / `error` onto
543
- * @param lines - The complete lines (from {@link extractLines}) to decode and deliver
551
+ * @param lines - The complete lines to decode and deliver
544
552
  */
545
553
  export declare function dispatchLines(emitter: EmitterInterface<MCPClientTransportEventMap>, lines: readonly string[]): void;
546
554
 
@@ -555,7 +563,7 @@ export declare function dispatchLines(emitter: EmitterInterface<MCPClientTranspo
555
563
  * - `message` — the server→client {@link JSONRPCMessage} that was pushed.
556
564
  * - `timestamp` — the epoch-ms instant the entry was appended, read by the TTL eviction.
557
565
  *
558
- * A plain value record (no behavior, §4.5) — the unit {@link MCPSessionInterface.replay}
566
+ * A plain value record (no behavior) — the unit {@link MCPSessionInterface.replay}
559
567
  * returns.
560
568
  */
561
569
  export declare interface EventStoreEntry {
@@ -565,7 +573,7 @@ export declare interface EventStoreEntry {
565
573
  }
566
574
 
567
575
  /**
568
- * Fold one more chunk of raw stdio bytes into a newline-framed buffer — the shared
576
+ * Folds one more chunk of raw stdio bytes into a newline-framed buffer — the shared
569
577
  * line-framing step both stdio transports (client and server) read their inbound
570
578
  * newline-delimited JSON-RPC messages through.
571
579
  *
@@ -592,12 +600,12 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
592
600
  * - **Request/response over `fetch`.** `send(message)` POSTs the JSON-serialized
593
601
  * message to `options.url` with `content-type: application/json` and an
594
602
  * `Accept` of BOTH `application/json` and `text/event-stream` (so the server may
595
- * answer with either framing) — plus any `options.headers` (e.g. an `Authorization`
603
+ * answer with either framing) — plus any `options.headers` (for example, an `Authorization`
596
604
  * bearer). It then decodes the reply and emits each decoded {@link JSONRPCMessage} on
597
- * the `message` event the {@link import('@src/core').MCPClientInterface} subscribes
605
+ * the `message` event the {@link import('@orkestrel/mcp').MCPClientInterface} subscribes
598
606
  * to.
599
607
  * - **Both reply framings.** A `200` with an `application/json` body is parsed with
600
- * `parseJSONRPCMessage`; a `200` with a `text/event-stream` body is decoded via the
608
+ * `parseJSONRPCMessage`; a `200` with a `text/event-stream` body is decoded with the
601
609
  * `@orkestrel/sse` {@link import('@orkestrel/sse').SSEParserInterface} ({@link
602
610
  * readEventStream}) — the inverse of the server's `openStream` seam, so the wire
603
611
  * round-trips. A `202`
@@ -615,10 +623,15 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
615
623
  * Before initialize returns, neither captured legacy header is sent.
616
624
  * `close()` clears the captured protocol so a reconnect's `initialize`
617
625
  * POST is headerless; the captured `session` persists across `close()`.
618
- * - **Total at the boundary (§14).** Every reply is narrowed (`parseJSONRPCMessage`,
626
+ * - **`close()` releases what is in flight.** Every `fetch` this transport still has open is
627
+ * ABORTED, which cancels the response body a `send` is reading — an SSE reply the server
628
+ * never ends would otherwise outlive the transport, with nothing left able to reach it. The
629
+ * aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
630
+ * idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
631
+ * - **Total at the boundary.** Every reply is narrowed (`parseJSONRPCMessage`,
619
632
  * the SSE decoder) — a non-message reply is dropped, never asserted; a `fetch` /
620
633
  * decode failure surfaces on the `error` event rather than escaping `send`.
621
- * - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); fires
634
+ * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); fires
622
635
  * `message` per decoded reply, `error` on a fault, and `close` on `close()`.
623
636
  *
624
637
  * @example
@@ -645,9 +658,9 @@ export declare class HTTPClientTransport implements MCPClientTransportInterface_
645
658
  *
646
659
  * @remarks
647
660
  * - `url` — the absolute URL of the remote server's Streamable-HTTP endpoint (the
648
- * `POST` target every JSON-RPC message is written to, e.g.
661
+ * `POST` target every JSON-RPC message is written to, for example,
649
662
  * `http://localhost:3000/mcp`). REQUIRED.
650
- * - `headers` — extra request headers merged onto every `POST` (e.g. an
663
+ * - `headers` — extra request headers merged onto every `POST` (for example, an
651
664
  * `Authorization` bearer for a guarded server). The transport always sets
652
665
  * `content-type: application/json` and an `Accept` of both `application/json` and
653
666
  * `text/event-stream` (so the server may answer with either framing); a key supplied
@@ -655,8 +668,10 @@ export declare class HTTPClientTransport implements MCPClientTransportInterface_
655
668
  * - `fetch` — the `fetch` implementation to issue each `POST` with; defaults to
656
669
  * `globalThis.fetch`. Injectable for a test double or a non-global `fetch`.
657
670
  * - `timeout` — an optional per-request timeout in milliseconds; when set, each
658
- * `fetch` call is issued with `signal: AbortSignal.timeout(timeout)`. Omit for no
659
- * transport-level deadline.
671
+ * `fetch` call composes that deadline with the transport's own close through
672
+ * `AbortSignal.any([close, AbortSignal.timeout(timeout)])`, so whichever fires first
673
+ * ends the request. Omit for no transport-level deadline; the close signal is passed
674
+ * either way.
660
675
  */
661
676
  export declare interface HTTPClientTransportOptions {
662
677
  readonly url: string;
@@ -666,7 +681,7 @@ export declare interface HTTPClientTransportOptions {
666
681
  }
667
682
 
668
683
  /**
669
- * Compose one incoming HTTP request lifetime with one MCP-owned SSE response lifetime.
684
+ * Composes one incoming HTTP request lifetime with one MCP-owned SSE response lifetime.
670
685
  *
671
686
  * @remarks
672
687
  * The composed {@link signal} observes request abort and EVERY way this response can end
@@ -680,7 +695,8 @@ export declare interface HTTPClientTransportOptions {
680
695
  *
681
696
  * {@link bridge} preserves the source response status and headers, forwards its body bytes, and
682
697
  * owns keepalive comments plus listener/timer cleanup until upstream completion, request abort,
683
- * or consumer cancellation. This is a single-response lifecycle object, not a reusable bridge.
698
+ * or consumer cancellation. This is a single-response lifecycle object, not a reusable bridge:
699
+ * a second {@link bridge} call THROWS rather than arming a second keepalive over one lifecycle.
684
700
  * It supplies no handler or session policy.
685
701
  *
686
702
  * The keepalive interval is a BUDGET, sanitized like every other numeric knob in this package:
@@ -702,7 +718,7 @@ export declare interface HTTPClientTransportOptions {
702
718
  export declare class HTTPDisconnect {
703
719
  #private;
704
720
  /**
705
- * Create the lifecycle composition for one request and its future SSE response.
721
+ * Creates the lifecycle composition for one request and its future SSE response.
706
722
  *
707
723
  * @param signal - The incoming request signal
708
724
  * @param options - Optional keepalive `interval` in milliseconds; an invalid value falls back
@@ -717,7 +733,7 @@ export declare class HTTPDisconnect {
717
733
  */
718
734
  get signal(): AbortSignal;
719
735
  /**
720
- * Bridge one open SSE response through cancellation-aware byte forwarding and keepalives.
736
+ * Bridges one open SSE response through cancellation-aware byte forwarding and keepalives.
721
737
  *
722
738
  * Consumer cancellation, a read failure while forwarding, and a keepalive tick that finds the
723
739
  * SSE stream already closed each abort {@link signal}; consumer cancellation also cancels the
@@ -726,7 +742,8 @@ export declare class HTTPDisconnect {
726
742
  *
727
743
  * @param stream - The open SSE stream whose response will be consumed by the HTTP writer
728
744
  * @returns A one-use response preserving status, status text, headers, and SSE body bytes
729
- * @throws When the supplied SSE response has no body
745
+ * @throws When this disconnect has already bridged a stream, or the supplied SSE response
746
+ * has no body
730
747
  */
731
748
  bridge(stream: StreamInterface): Response;
732
749
  }
@@ -765,7 +782,7 @@ export declare interface HTTPHandlerOptions<TState = unknown> {
765
782
  /**
766
783
  * Options for `createMCPRoutes` — the mount path plus the shared POST-handler options.
767
784
  * `createMCPRoutes` is STATELESS; sessions are a separate middleware ({@link
768
- * import('./middlewares.js').createMCPSession}), composed via `server.use`.
785
+ * import('./middlewares.js').createMCPSession}), composed with `server.use`.
769
786
  *
770
787
  * @remarks
771
788
  * `path` is the request path the single `POST` route answers; it defaults to {@link
@@ -779,7 +796,7 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
779
796
  }
780
797
 
781
798
  /**
782
- * Infer the first required MCP HTTP header that is missing or mismatched.
799
+ * Infers the first required MCP HTTP header that is missing or mismatched.
783
800
  *
784
801
  * @remarks
785
802
  * A modern request derives its protocol, method, and tools/call-only name expectations from
@@ -800,7 +817,7 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
800
817
  export declare function inferHeaderIssue(request: Request, reference: JSONRPCInvocation | MCPVersion): MCPHeaderIssue | undefined;
801
818
 
802
819
  /**
803
- * Infer the legacy revision an `initialize` request negotiates.
820
+ * Infers the legacy revision an `initialize` request negotiates.
804
821
  *
805
822
  * @remarks
806
823
  * A supported legacy request is pinned exactly. A modern, malformed, absent, or unsupported
@@ -812,7 +829,7 @@ export declare function inferHeaderIssue(request: Request, reference: JSONRPCInv
812
829
  export declare function inferLegacyVersion(request: JSONRPCInvocation): MCPVersion;
813
830
 
814
831
  /**
815
- * Infer the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
832
+ * Infers the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
816
833
  *
817
834
  * @remarks
818
835
  * Notifications are accepted with `202`. Legacy response envelopes retain uniform `200`
@@ -832,7 +849,7 @@ export declare function inferStatus(response: JSONRPCResponse | undefined, era:
832
849
  * the trailing partial line carried forward as the new `remainder`.
833
850
  *
834
851
  * @remarks
835
- * A plain value record (no behavior, §4.5) {@link import('./helpers.js').extractLines}
852
+ * A plain value record (no behavior) {@link import('./helpers.js').extractLines}
836
853
  * returns; the caller threads `remainder` back in as the next call's `buffer`.
837
854
  */
838
855
  export declare interface LineExtraction {
@@ -879,7 +896,7 @@ export declare const MCP_SESSION_HEADER = "mcp-session-id";
879
896
  export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
880
897
 
881
898
  /**
882
- * Synchronously extract consumer-asserted caller context from an HTTP request after the
899
+ * Extracts consumer-asserted caller context synchronously from an HTTP request after the
883
900
  * transport has validated it for dispatch.
884
901
  *
885
902
  * @remarks
@@ -948,8 +965,8 @@ export declare interface MCPOriginOptions {
948
965
  * The single session entity (the old `SessionState` + `EventStore` merged): it holds the
949
966
  * session `id`, its OWN bounded, replayable log of pushed server→client messages (the
950
967
  * resumable GET-SSE channel — a private `#events` `Map` + a monotone `#counter`, with
951
- * `capacity` / `ttl` eviction, NOT a separate store), and the set of currently OPEN
952
- * server→client SSE streams (a resumable `GET {path}` registers via `attach`, unregisters via
968
+ * `capacity` / `ttl` eviction, not a separate store), and the set of open
969
+ * server→client SSE streams (a resumable `GET {path}` registers through `attach`, unregisters through
953
970
  * `detach` on disconnect). Still a small entity (not a record), built minimal + extensible.
954
971
  *
955
972
  * - **`push` is the server-initiated primitive.** It APPENDS the message to the log (assigning
@@ -969,7 +986,7 @@ export declare interface MCPOriginOptions {
969
986
  * lost (its cursor is OLDER than everything retained); returning `[]` lets the handler then
970
987
  * stream only the fresh pushes that follow `attach` — the spec-sane resume.
971
988
  *
972
- * - **Bounded, append-ordered, plain `Map` (§21).** The log lives in ONE insertion-ordered
989
+ * - **Bounded, append-ordered, plain `Map`.** The log lives in ONE insertion-ordered
973
990
  * `Map<id, entry>` — insertion order IS append order IS id order, so `replay` and capacity
974
991
  * eviction both walk the map directly. NO database mirror — the log is process-local
975
992
  * transport mechanics, not durable state. `push` first drops every entry older than `ttl`
@@ -983,8 +1000,8 @@ export declare interface MCPOriginOptions {
983
1000
  * serializes a message onto the already-open streams.
984
1001
  *
985
1002
  * - **Injected clock.** `push` / `replay` accept an optional `now` (epoch ms), defaulting to
986
- * `Date.now()` — so a test drives TTL eviction with an elapsed clock rather than a real timer
987
- * (AGENTS §16).
1003
+ * `Date.now()` — so a test drives TTL eviction with an elapsed clock rather than a real
1004
+ * timer.
988
1005
  *
989
1006
  * @example
990
1007
  * ```ts
@@ -1074,7 +1091,7 @@ export declare interface MCPSessionInterface {
1074
1091
  * - `clock` — the `() => number` epoch-ms clock {@link import('./middlewares.js').createMCPSession}
1075
1092
  * uses directly for its own session-touch / TTL-sweep bookkeeping; defaults to `Date.now`. The
1076
1093
  * deterministic clock a TTL test advances explicitly instead of racing a real idle window
1077
- * against wall-clock (AGENTS §16). Production never sets it.
1094
+ * against wall-clock. Production never sets it.
1078
1095
  * - `origin` — the same shared origin-validation options supplied to `createMCPRoutes`.
1079
1096
  * Validation is enabled by default, accepts canonical loopback-literal origins, and rejects
1080
1097
  * every other present origin outside its exact list before any session is minted. Set
@@ -1110,16 +1127,16 @@ export declare interface MCPSessionState {
1110
1127
  }
1111
1128
 
1112
1129
  /**
1113
- * Decode a `fetch` Response's Server-Sent-Events body into the JSON-RPC messages it
1130
+ * Decodes a `fetch` Response's Server-Sent-Events body into the JSON-RPC messages it
1114
1131
  * carried — the CLIENT-side inverse of the server's Streamable-HTTP SSE response.
1115
1132
  *
1116
1133
  * @remarks
1117
1134
  * Reads the whole `response.body` stream chunk-by-chunk through a `TextDecoder({
1118
1135
  * stream: true })` (handling a multi-byte char split across reads) and `@orkestrel/sse`'s
1119
1136
  * {@link SSEParserInterface} (handling a partial line / in-progress event split across
1120
- * reads), then narrows each dispatched event's `data` to a {@link JSONRPCMessage} via
1137
+ * reads), then narrows each dispatched event's `data` to a {@link JSONRPCMessage} with
1121
1138
  * `parseJSONRPCMessage` (so a non-message / non-JSON `data:` event is DROPPED, never
1122
- * thrown — total, §14). It reuses the SAME `SSEParser` the server's `openStream` seam
1139
+ * thrown — total). It reuses the SAME `SSEParser` the server's `openStream` seam
1123
1140
  * serializes against, so the wire round-trips. A `null` body (no stream) yields no
1124
1141
  * messages; the {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport}
1125
1142
  * reads a request/response SSE reply (the server sends one `data:` event then ends), so
@@ -1131,7 +1148,7 @@ export declare interface MCPSessionState {
1131
1148
  export declare function readEventStream(response: Response): Promise<readonly JSONRPCMessage[]>;
1132
1149
 
1133
1150
  /**
1134
- * Read the request's `Last-Event-ID` header — the SSE resume cursor a client sends when it
1151
+ * Reads the request's `Last-Event-ID` header — the SSE resume cursor a client sends when it
1135
1152
  * reconnects to the resumable `GET {path}` stream, or `undefined` when absent.
1136
1153
  *
1137
1154
  * @remarks
@@ -1147,7 +1164,7 @@ export declare function readEventStream(response: Response): Promise<readonly JS
1147
1164
  export declare function readLastEventId(request: Request): string | undefined;
1148
1165
 
1149
1166
  /**
1150
- * Read the request's `mcp-session-id` header — the session id a stateful transport
1167
+ * Reads the request's `mcp-session-id` header — the session id a stateful transport
1151
1168
  * validates, or `undefined` when absent.
1152
1169
  *
1153
1170
  * @remarks
@@ -1163,7 +1180,7 @@ export declare function readLastEventId(request: Request): string | undefined;
1163
1180
  export declare function readSessionHeader(request: Request): string | undefined;
1164
1181
 
1165
1182
  /**
1166
- * Build the stateful transport's "unknown session" rejection — an HTTP `404` carrying a
1183
+ * Builds the stateful transport's "unknown session" rejection — an HTTP `404` carrying a
1167
1184
  * JSON-RPC error body.
1168
1185
  *
1169
1186
  * @remarks
@@ -1180,12 +1197,12 @@ export declare function readSessionHeader(request: Request): string | undefined;
1180
1197
  export declare function rejectUnknownSession(): Response;
1181
1198
 
1182
1199
  /**
1183
- * Pump a controlled held-open exchange onto an open SSE stream — one `data:` event per
1200
+ * Pumps a controlled held-open exchange onto an open SSE stream — one `data:` event per
1184
1201
  * notification in order, then the terminating response — and END the exchange however the
1185
1202
  * pump leaves.
1186
1203
  *
1187
1204
  * @remarks
1188
- * The Streamable-HTTP twin of {@link import('@src/core').sendStream}, and it owns exactly what
1205
+ * The Streamable-HTTP twin of {@link import('@orkestrel/mcp').sendStream}, and it owns exactly what
1189
1206
  * that owns. The `finally` releases the exchange on EVERY exit — the normal terminal, a
1190
1207
  * producer that threw, a `write` that threw, and an abort alike — because nothing else will:
1191
1208
  * a request whose client vanished cancels nothing by itself, so an exchange this pump walks
@@ -1193,7 +1210,7 @@ export declare function rejectUnknownSession(): Response;
1193
1210
  * The exchange is released BEFORE the body ends, so the slot is already back when the response
1194
1211
  * completes.
1195
1212
  *
1196
- * Total (§14) — never throws and never rejects. A held-open SSE response has already sent its
1213
+ * Total — never throws and never rejects. A held-open SSE response has already sent its
1197
1214
  * headers and part of its body, so there is no failure the transport could still convert into
1198
1215
  * a different answer; the honest end of a broken stream is a closed one, and the fault itself
1199
1216
  * is already legible on `server.emitter`'s `error` event, which is where a contained fault
@@ -1231,22 +1248,31 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
1231
1248
  * import('./WebSocketClientTransport.js').WebSocketClientTransport}.
1232
1249
  *
1233
1250
  * @remarks
1234
- * - **Spawns the server.** `start()` runs `node:child_process`'s `spawn(options.command,
1235
- * options.args, { env: options.env, stdio: ['pipe', 'pipe', 'inherit'] })` the
1236
- * child's `stdin`/`stdout` are piped for the JSON-RPC channel, its `stderr` inherits
1237
- * the parent's (diagnostics pass through, never parsed as protocol).
1238
- * - **Inbound (`message`).** Each `stdout` chunk is folded through the shared
1239
- * {@link extractLines} line-framing helper (buffering a partial trailing line
1240
- * across reads); every complete line is decoded and delivered via the shared
1241
- * {@link dispatchLines} helper — a well-formed {@link JSONRPCMessage} emits
1242
- * `message`, a malformed line emits `error` (§14, never throws). The child's
1243
- * `close` bridges to this transport's `close`.
1244
- * - **Outbound (`send`).** `send(message)` writes one newline-terminated
1245
- * `JSON.stringify`d line to the child's `stdin`.
1246
- * - **`close()`** kills the child process and fires `close` (idempotent).
1247
- * - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
1251
+ * - **Composes `@orkestrel/process`.** `start()` builds one supervised
1252
+ * {@link import('@orkestrel/process/server').Process} with `writable: true`, so the child's
1253
+ * `stdin`/`stdout` are the JSON-RPC channel and its `stderr` is retained as bounded evidence
1254
+ * rather than parsed as protocol. The supervisor owns spawn, framing, and termination.
1255
+ * - **Inbound (`message`).** Standard output is drained eagerly through the supervisor's
1256
+ * `readline`-framed `lines` iterable, so a multi-byte UTF-8 sequence split across two reads is
1257
+ * decoded whole and a final line written without a trailing newline still arrives. Each framed
1258
+ * line is decoded and delivered through the shared {@link dispatchLines} helper — a well-formed
1259
+ * {@link JSONRPCMessage} emits `message`, a malformed line emits `error` (never throws).
1260
+ * - **Outbound (`send`).** `send(message)` writes one newline-terminated `JSON.stringify`d line
1261
+ * through the supervisor's `send` and AWAITS its answer, so this promise settles only after the
1262
+ * 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.
1272
+ * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
1248
1273
  * emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
1249
- * fault), distinct from the emitter's own listener-error channel.
1274
+ * fault, including the child spawn cause the supervisor surfaces), distinct from the emitter's
1275
+ * own listener-error channel.
1250
1276
  *
1251
1277
  * @example
1252
1278
  * ```ts
@@ -1271,13 +1297,13 @@ export declare class StdioClientTransport implements MCPClientTransportInterface
1271
1297
  * stdio-framed MCP server (newline-delimited JSON-RPC over `stdin`/`stdout`).
1272
1298
  *
1273
1299
  * @remarks
1274
- * - `command` — the executable to spawn (e.g. `'node'`, `'./my-mcp-server'`). REQUIRED.
1300
+ * - `command` — the executable to spawn (for example, `'node'`, `'./my-mcp-server'`). REQUIRED.
1275
1301
  * - `args` — the command-line arguments passed to `command`; defaults to none.
1276
- * - `env` — the environment variables for the spawned child, passed straight to
1277
- * `node:child_process`'s `spawn`; when OMITTED the child inherits the full
1278
- * `process.env` (the `spawn` default), when PROVIDED it REPLACES the inherited
1279
- * environment entirely (`spawn` semantics) a caller wanting to extend rather
1280
- * than replace spreads `process.env` into `env` themselves.
1302
+ * - `env` — environment variable overrides MERGED over the parent `process.env` for the
1303
+ * spawned child (the composed `@orkestrel/process` supervisor's merge semantics): when
1304
+ * OMITTED the child inherits the full `process.env`, when PROVIDED each named key overrides
1305
+ * the inherited value while every unlisted key is still inherited. This transport cannot
1306
+ * REPLACE the inherited environment entirely the supervisor always merges over the parent.
1281
1307
  */
1282
1308
  export declare interface StdioClientTransportOptions {
1283
1309
  readonly command: string;
@@ -1309,22 +1335,31 @@ export declare interface StdioServerOptions {
1309
1335
  * import('./WebSocketServerTransport.js').WebSocketServerTransport}.
1310
1336
  *
1311
1337
  * @remarks
1312
- * - **Reuses `MCPClientTransportInterface` (§21).** The same generic carrier the HTTP
1338
+ * - **Reuses `MCPClientTransportInterface`.** The same generic carrier the HTTP
1313
1339
  * and WebSocket server transports implement — `emitter` (`message` / `close` /
1314
1340
  * `error`), `start`, `send`, `close`. `session` is `undefined` (the stateless v1).
1315
1341
  * - **Inbound (`message`).** `start()` subscribes to `input`'s `data` event; each
1316
1342
  * chunk is folded through the shared {@link extractLines} line-framing helper
1317
1343
  * (buffering a partial trailing line across reads), and every complete line is
1318
- * decoded and delivered via the shared {@link dispatchLines} helper — a
1344
+ * decoded and delivered through the shared {@link dispatchLines} helper — a
1319
1345
  * well-formed {@link JSONRPCMessage} re-emits on `message`, a malformed line
1320
- * emits `error` (§14, never throws). `input`'s `close` bridges to this
1346
+ * emits `error` (never throws). `input`'s `close` bridges to this
1321
1347
  * transport's `close`.
1322
1348
  * - **Outbound (`send`).** `send(message)` writes one newline-terminated
1323
1349
  * `JSON.stringify`d line to `output`.
1324
- * - **`close()`** fires this transport's `close` (idempotent) the injected streams
1325
- * are owned by the caller (typically `process.stdin`/`process.stdout`, which must
1326
- * never be closed out from under the process) and are not torn down here.
1327
- * - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
1350
+ * - **`close()`** removes this transport's input subscriptions and fires its `close`
1351
+ * event (idempotent). It pauses the input only when the caller was not already reading
1352
+ * it at `start` (`readableFlowing !== true`) AND no `data` listener remains once this
1353
+ * transport's own is removed so a process holding `process.stdin` can exit, and a
1354
+ * caller's own flow is never stopped underneath it. The transport preserves flowing versus
1355
+ * non-flowing state and restores every caller-owned listener. A Node stream that had never been
1356
+ * read starts with `readableFlowing === null` and is left non-flowing (`false`), because Node
1357
+ * exposes no public operation that restores `null` after data consumption starts. Attaching a
1358
+ * later `data` listener does not resume that stream; the caller must call `resume()` before the
1359
+ * listener receives data. The injected streams are owned by the caller (typically
1360
+ * `process.stdin`/`process.stdout`), so the transport never destroys, ends, or blanket-clears
1361
+ * them.
1362
+ * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
1328
1363
  * emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
1329
1364
  * fault), distinct from the emitter's own listener-error channel.
1330
1365
  */
@@ -1340,13 +1375,13 @@ export declare class StdioServerTransport implements MCPClientTransportInterface
1340
1375
  }
1341
1376
 
1342
1377
  /**
1343
- * Read the path (without the query string) of a raw `node:http` protocol-upgrade request —
1378
+ * Reads the path (without the query string) of a raw `node:http` protocol-upgrade request —
1344
1379
  * the `createWebSocketServer` upgrade-path match.
1345
1380
  *
1346
1381
  * @remarks
1347
1382
  * A `node:http` {@link import('node:http').IncomingMessage}'s `url` is the request TARGET
1348
- * (`'/mcp?x=1'`), narrowed with `isString` (§14, never `as`) and defaulting to `'/'` for an
1349
- * absent target; it is parsed against a dummy base (only the pathname matters for the upgrade
1383
+ * (`'/mcp?x=1'`), narrowed with `isString` (never `as`) and defaulting to `'/'` for an
1384
+ * absent target; it is parsed against a placeholder base (only the pathname matters for the upgrade
1350
1385
  * decision) and the `pathname` returned. The upgrade handler compares this against its
1351
1386
  * configured `path` to decide whether to claim the socket. Total — never throws on an
1352
1387
  * adversarial / absent target.
@@ -1370,7 +1405,8 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
1370
1405
  * event, and VALIDATES `Sec-WebSocket-Accept === computeWebSocketAccept(key)` (the D2 helper)
1371
1406
  * — a mismatch (or a non-`101` response, or a request error) REJECTS `start()` and the socket
1372
1407
  * is destroyed. On success it wraps the raw upgraded socket in `createNodeWebSocket({ socket,
1373
- * head })` (CLIENT mode — no key → frames are MASKED per §5.3) and bridges its `message`.
1408
+ * head })` (CLIENT mode — no key → frames are MASKED per RFC 6455 §5.3) and bridges its
1409
+ * `message`.
1374
1410
  * - **The arriving socket is RE-ASKED for, never assumed.** `start()` suspends across that
1375
1411
  * connect and upgrade, so it re-checks the transport's state before installing anything: a
1376
1412
  * concurrent `start()` that already installed a socket, or a {@link close} that ended the
@@ -1379,15 +1415,18 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
1379
1415
  * `start()` calls still resolve; exactly one socket is ever bound.
1380
1416
  * - **Inbound (`message`).** Each decoded text frame is `JSON.parse`d (guarded) and narrowed
1381
1417
  * with `parseJSONRPCMessage` — a {@link JSONRPCMessage} re-emits on this transport's `message`
1382
- * event (the reply the {@link import('@src/core').MCPClientInterface} correlates by `id`); a
1383
- * non-JSON / non-message frame surfaces on `error` and is dropped (§14). The socket's `close`
1418
+ * event (the reply the {@link import('@orkestrel/mcp').MCPClientInterface} correlates by `id`); a
1419
+ * non-JSON / non-message frame surfaces on `error` and is dropped. The socket's `close`
1384
1420
  * / `error` bridge to this transport's events.
1385
1421
  * - **Outbound (`send`).** `send(message)` writes one masked text frame.
1386
- * - **`close()`** closes the underlying socket and fires `close` (idempotent).
1422
+ * - **`close()`** unsubscribes from the socket, closes it, and fires `close` (idempotent). An
1423
+ * upgrade still on the wire is DESTROYED, so a `close()` during the handshake ends the
1424
+ * transport at once instead of waiting for a peer that may never answer — the suspended
1425
+ * `start()` resolves, because the close is the outcome its caller asked for.
1387
1426
  * - **URL scheme.** `options.url` accepts a `ws://` / `wss://` URL or an `http://` / `https://`
1388
1427
  * one; a `ws(s)` scheme is converted to `http(s)` for the underlying upgrade request (`wss`
1389
- * → TLS via `node:https`). Either reaches the same endpoint.
1390
- * - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); every emit
1428
+ * → TLS through `node:https`). Either reaches the same endpoint.
1429
+ * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); every emit
1391
1430
  * the emitter isolates a listener throw (a buggy observer never corrupts the transport);
1392
1431
  * `error` is a DOMAIN event (a transport-level fault).
1393
1432
  *
@@ -1418,7 +1457,7 @@ export declare class WebSocketClientTransport implements MCPClientTransportInter
1418
1457
  * `wss://` URL OR an `http://` / `https://` one (a `ws(s)` scheme is converted to `http(s)`
1419
1458
  * for the underlying `node:http(s)` upgrade request; either reaches the same endpoint).
1420
1459
  * REQUIRED.
1421
- * - `headers` — extra request headers merged onto the upgrade `GET` (e.g. an `Authorization`
1460
+ * - `headers` — extra request headers merged onto the upgrade `GET` (for example, an `Authorization`
1422
1461
  * bearer for a guarded server). The transport always sets `Connection: Upgrade`,
1423
1462
  * `Upgrade: websocket`, a random `Sec-WebSocket-Key`, `Sec-WebSocket-Version: 13`, and
1424
1463
  * `Sec-WebSocket-Protocol: mcp`; a header supplied here is merged on top.
@@ -1428,7 +1467,7 @@ export declare class WebSocketClientTransport implements MCPClientTransportInter
1428
1467
  * This face owns its own `node:http(s)` upgrade request, so it can set any header on it. A
1429
1468
  * page cannot — the native `WebSocket` constructor takes a URL and subprotocols and nothing
1430
1469
  * else — so a `headers` key over there would be an option that silently did nothing. Do not
1431
- * "harmonize" the two shapes.
1470
+ * "harmonize" the option shapes.
1432
1471
  */
1433
1472
  export declare interface WebSocketClientTransportOptions {
1434
1473
  readonly url: string;
@@ -1450,10 +1489,9 @@ export declare interface WebSocketClientTransportOptions {
1450
1489
  * {@link import('./constants.js').DEFAULT_MCP_PATH} (`'/mcp'`, the same path the HTTP
1451
1490
  * transport mounts at). A protocol-upgrade request to any OTHER path is DECLINED
1452
1491
  * (the handler returns `false`, so the spine fans it to the next handler or destroys it).
1453
- * - `subprotocol` — the WebSocket subprotocol echoed in the `101` handshake's
1492
+ * - `subprotocol` — the WebSocket subprotocol selected in the `101` handshake's
1454
1493
  * `Sec-WebSocket-Protocol`; defaults to {@link import('./constants.js').MCP_WEBSOCKET_SUBPROTOCOL}
1455
- * (`'mcp'`). It is echoed unconditionally (the client requests it), so an MCP WebSocket
1456
- * endpoint is distinguishable from another WebSocket on the same path.
1494
+ * (`'mcp'`). It is sent only when the client's offer contains that token.
1457
1495
  *
1458
1496
  * Auth / origin policy is deliberately ABSENT: like the HTTP transport, the WebSocket
1459
1497
  * transport is MECHANISM — compose a guard IN FRONT (a `Server.upgrade` handler registered
@@ -1473,7 +1511,7 @@ export declare interface WebSocketServerOptions {
1473
1511
  * {@link import('./WebSocketClientTransport.js').WebSocketClientTransport} reuses.
1474
1512
  *
1475
1513
  * @remarks
1476
- * - **Reuses `MCPClientTransportInterface` (§21).** It IS the same generic carrier the HTTP
1514
+ * - **Reuses `MCPClientTransportInterface`.** It IS the same generic carrier the HTTP
1477
1515
  * client transport implements — `emitter` (`message` / `close` / `error`), `start`,
1478
1516
  * `send`, `close` — so the WebSocket server and client both speak ONE transport contract,
1479
1517
  * no near-duplicate sibling interface. `session` is `undefined` (the stateless v1; a
@@ -1482,16 +1520,18 @@ export declare interface WebSocketServerOptions {
1482
1520
  * - **Inbound (`message`).** `start()` subscribes to the socket's `message` event; each text
1483
1521
  * frame is `JSON.parse`d inside a try/catch and narrowed with `parseJSONRPCMessage` — a
1484
1522
  * well-formed {@link JSONRPCMessage} is re-emitted on this transport's `message` event (the
1485
- * parsed envelope the {@link import('@src/core').MCPServerInterface} pump dispatches), while
1486
- * a non-JSON or non-message frame is surfaced on `error` and DROPPED, never thrown (§14). It
1523
+ * parsed envelope the {@link import('@orkestrel/mcp').MCPServerInterface} pump dispatches), while
1524
+ * a non-JSON or non-message frame is surfaced on `error` and DROPPED, never thrown. It
1487
1525
  * also bridges the socket's `close` → this transport's `close`, and the socket's `error`.
1488
1526
  * - **Outbound (`send`).** `send(message)` writes one text frame
1489
1527
  * (`nodeWs.send(JSON.stringify(message))`); the underlying wrapper no-ops a write on a
1490
1528
  * non-open socket, so a closed connection drops silently rather than throwing.
1491
- * - **`close()`** closes the underlying socket (the RFC 6455 close handshake) and fires the
1492
- * transport's `close` event (idempotent a second `close`, or a socket-driven close, emits
1493
- * once).
1494
- * - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); the emitter
1529
+ * - **`close()`** removes the subscriptions `start()` installed on the socket, closes the
1530
+ * underlying socket (the RFC 6455 close handshake), and fires the transport's `close` event
1531
+ * (idempotent — a second `close`, or a socket-driven close, emits once). A frame that arrives
1532
+ * between that release and the peer's close echo reaches nothing: the socket-driven close path
1533
+ * releases the same way, so a closed transport is never subscribed to a live socket.
1534
+ * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); the emitter
1495
1535
  * isolates a listener throw (a buggy observer never corrupts the bridge). `error` is a
1496
1536
  * DOMAIN event (a transport-level fault), distinct from the emitter's listener-error channel.
1497
1537
  */