@orkestrel/mcp 0.0.28 → 0.0.29

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.
package/README.md CHANGED
@@ -1,15 +1,12 @@
1
1
  # @orkestrel/mcp
2
2
 
3
- A typed [Model Context Protocol](https://modelcontextprotocol.io) client/server
4
- for the `@orkestrel` line, bridging the `@orkestrel/tool` registry to MCP with
5
- pluggable HTTP, WebSocket, and stdio transports. `createMCPServer` exposes a
6
- live `ToolManagerInterface`; `createMCPClient` drives a remote MCP server and
7
- surfaces its tools as local `ToolInterface`s. No agent runtime is required.
8
- The dispatch core is transport- and provider-agnostic
9
- (`src/core` — JSON-RPC 2.0, no HTTP, no `as`); every transport (Streamable
10
- HTTP over `@orkestrel/router` / `@orkestrel/server`, WebSocket over
11
- `@orkestrel/websocket`, and stdio over `@orkestrel/process`) lives one layer
12
- out (`src/server`), each mechanism, not policy. Part of the `@orkestrel` line.
3
+ > The Model Context Protocol layer: a typed JSON-RPC 2.0 client/server pair with pluggable
4
+ > HTTP, WebSocket, stdio, and browser transports.
5
+
6
+ Bridge the `@orkestrel/tool` registry to MCP: `createMCPServer` exposes a live
7
+ `ToolManagerInterface` to any MCP client, and `createMCPClient` drives a remote MCP server
8
+ and surfaces its tools as local `ToolInterface`s. No agent runtime is required. Part of the
9
+ `@orkestrel` line.
13
10
 
14
11
  ## Install
15
12
 
@@ -30,8 +27,6 @@ npm install @orkestrel/mcp
30
27
 
31
28
  ## Usage
32
29
 
33
- Expose a tool registry over MCP, mounted on the HTTP spine:
34
-
35
30
  ```ts
36
31
  import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
37
32
  import { createMCPRoutes } from '@orkestrel/mcp/server'
@@ -46,6 +41,8 @@ const routes = createMCPRoutes(createMCPLegacy(mcp)) // answers `initialize` too
46
41
  router.add(routes)
47
42
  ```
48
43
 
44
+ This example exposes a tool registry over MCP, mounted on the HTTP spine.
45
+
49
46
  Drive a remote MCP server as a client, over the same transport-agnostic core:
50
47
 
51
48
  ```ts
@@ -60,7 +57,7 @@ const tools = await client.tools()
60
57
  const outcome = await client.call('add', { x: 2, y: 5 })
61
58
  ```
62
59
 
63
- The SAME `MCPClient` drives a `createWebSocketClientTransport` or
60
+ The same `MCPClient` drives a `createWebSocketClientTransport` or
64
61
  `createStdioClientTransport` instead — only the injected transport changes.
65
62
 
66
63
  ## Guide
@@ -96,7 +93,7 @@ The publication facts, each with its number. Full detail, plus every
96
93
  protocol-level gap and non-goal, is in
97
94
  [the MCP guide](https://github.com/orkestrel/mcp/blob/main/guides/mcp.md#declared-packaging-limits).
98
95
 
99
- - **No IDE evidence.** See above. The conformance number is about the wire
96
+ - **No IDE evidence.** See the [wire conformance evidence](https://github.com/orkestrel/mcp#proven). The conformance number is about the wire
100
97
  and does not transfer to a host application.
101
98
  - **No top-level `types` field.** Every `exports` subpath carries a `types`
102
99
  condition, so `node16`, `nodenext`, and `bundler` resolution find
@@ -1,18 +1,16 @@
1
- import { EmitterInterface } from '@orkestrel/emitter';
2
- import { HTTPClientTransportOptions } from '@orkestrel/mcp';
3
- import { JSONRPCMessage } from '@orkestrel/mcp';
4
- import { MCPMessageTransportEventMap } from '@orkestrel/mcp';
5
- import { MCPMessageTransportInterface } from '@orkestrel/mcp';
6
- import { MCPMessageTransportInterface as MCPMessageTransportInterface_2 } from '@orkestrel/mcp';
7
- import { MCPServerInterface } from '@orkestrel/mcp';
8
- import { MCPTransportInterface } from '@orkestrel/mcp';
9
- import { MCPTransportInterface as MCPTransportInterface_2 } from '@orkestrel/mcp';
10
- import { ToolManagerInterface } from '@orkestrel/tool';
1
+ import type { EmitterInterface } from '@orkestrel/emitter';
2
+ import type { HTTPClientTransportOptions } from '@orkestrel/mcp';
3
+ import type { JSONRPCMessage } from '@orkestrel/mcp';
4
+ import type { MCPMessageTransportEventMap } from '@orkestrel/mcp';
5
+ import type { MCPMessageTransportInterface } from '@orkestrel/mcp';
6
+ import type { MCPServerInterface } from '@orkestrel/mcp';
7
+ import type { MCPTransportInterface } from '@orkestrel/mcp';
8
+ import type { ToolManagerInterface } from '@orkestrel/tool';
11
9
 
12
10
  /**
13
- * Creates the HTTP CLIENT transport for an
11
+ * Creates the HTTP client transport for an
14
12
  * {@link import('@orkestrel/mcp').MCPClientInterface} — a {@link MCPMessageTransportInterface}
15
- * that drives a REMOTE Streamable-HTTP MCP server over the native `fetch`.
13
+ * that drives a remote Streamable-HTTP MCP server over the native `fetch`.
16
14
  *
17
15
  * @remarks
18
16
  * It returns the core {@link import('@orkestrel/mcp').HTTPClientTransport}, the same class the
@@ -24,18 +22,18 @@ import { ToolManagerInterface } from '@orkestrel/tool';
24
22
  * Hand it to `createMCPClient({ transport })`: each JSON-RPC message the client
25
23
  * sends is `POST`ed to `options.url` with `content-type: application/json` and an
26
24
  * `Accept` of both `application/json` and `text/event-stream` (the server answers
27
- * with EITHER — a plain JSON envelope or a Streamable-HTTP SSE `data:` event,
25
+ * with either — a plain JSON envelope or a Streamable-HTTP SSE `data:` event,
28
26
  * decoded with `@orkestrel/sse`), and the reply is surfaced on the transport's
29
27
  * `message` event for the client's id correlation. Add `options.headers` (for example, an
30
28
  * `Authorization` bearer) to reach a guarded server. `start` / `close` hold no
31
- * connection; against a STATEFUL server it captures the `mcp-session-id` from
29
+ * connection; against a stateful server it captures the `mcp-session-id` from
32
30
  * `initialize` and echoes it on later requests. It also captures the initialize
33
31
  * result's `protocolVersion` and sends `mcp-protocol-version` alone on subsequent
34
32
  * legacy requests. Modern requests instead derive `mcp-protocol-version` and
35
33
  * `mcp-method` from the message, plus `mcp-name` only for `tools/call`, so the
36
34
  * same `MCPClient` passes either era's protocol gates without caller wiring.
37
35
  *
38
- * @param options - `url` (the remote endpoint; REQUIRED), optional `headers` merged
36
+ * @param options - `url` (the remote endpoint; required), optional `headers` merged
39
37
  * onto every request, optional `fetch` (default `globalThis.fetch`), and optional
40
38
  * `timeout` (ms, applied with `AbortSignal.timeout`); see
41
39
  * {@link HTTPClientTransportOptions}
@@ -53,12 +51,12 @@ import { ToolManagerInterface } from '@orkestrel/tool';
53
51
  * const tools = await client.tools()
54
52
  * ```
55
53
  */
56
- export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): MCPMessageTransportInterface_2;
54
+ export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): MCPMessageTransportInterface;
57
55
 
58
56
  /**
59
57
  * Creates the browser-face `MessagePort` transport — a
60
58
  * {@link import('@orkestrel/mcp').MCPTransportInterface} over a native `MessagePort`, the
61
- * SYMMETRIC carrier that works as either a server or a client transport depending on
59
+ * symmetric carrier that works as either a server or a client transport depending on
62
60
  * which binder ({@link import('@orkestrel/mcp').bindServer} or
63
61
  * {@link import('@orkestrel/mcp').bindClient}) it is handed to.
64
62
  *
@@ -68,7 +66,7 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
68
66
  * dropped, never thrown); `messageerror` is ignored (one bad frame does not close the
69
67
  * channel); `close()` closes the port and fires `closed` exactly once.
70
68
  *
71
- * @param options - `port` (the `MessagePort` half to drive; REQUIRED); see
69
+ * @param options - `port` (the `MessagePort` half to drive; required); see
72
70
  * {@link MessagePortTransportOptions}
73
71
  * @returns A working {@link import('@orkestrel/mcp').MCPTransportInterface} over the port
74
72
  *
@@ -86,28 +84,28 @@ export declare function createMessagePortTransport(options: MessagePortTransport
86
84
 
87
85
  /**
88
86
  * Builds {@link createScopeServer}'s `message`-event listener — the unified dispatcher that
89
- * routes EVERY inbound event on a hostable scope, portless or port-bearing, to the right
87
+ * routes every inbound event on a hostable scope, portless or port-bearing, to the right
90
88
  * binding.
91
89
  *
92
90
  * @remarks
93
- * Port-bearing events (`event.ports.length > 0`) are gated by `options.accept` FIRST
91
+ * Port-bearing events (`event.ports.length > 0`) are gated by `options.accept` first
94
92
  * — when the gate returns `false` the event is dropped entirely (no binding, no reply).
95
93
  * Accepted events spawn a fresh `MessagePortTransport` over `event.ports[0]`,
96
94
  * `bindServer` `server` onto it, and record a teardown (`unbind` then `transport.close()`)
97
- * into `teardowns` KEYED BY THAT PORT. A port already present is IGNORED — repeated delivery
95
+ * into `teardowns` keyed by that port. A port already present is ignored — repeated delivery
98
96
  * of the same `MessagePort` would create duplicate bindings over one port (→ duplicated
99
97
  * replies), so a repeat is silently dropped.
100
98
  *
101
- * The key is what makes `teardowns` the ONLY place an accepted port is remembered. A separate
99
+ * The key is what makes `teardowns` the only place an accepted port is remembered. A separate
102
100
  * seen-port set would be a second collection over the same lifetime, and the scope server's
103
101
  * `stop` would have to remember to empty both — so a long-lived scope such as a Service Worker
104
102
  * would retain every port it ever accepted, closed and unbound ones included. Membership
105
103
  * answers "already bound?" and `clear()` drops the binding and the dedup together.
106
104
  *
107
- * This branch fires on EITHER a Service-Worker-shaped scope (its normal per-client
105
+ * This branch fires on either a Service-Worker-shaped scope (its normal per-client
108
106
  * channel) or a dedicated-worker-shaped one that happens to receive a port-bearing event
109
107
  * (the unified design's deliberate cross-case, needing no upfront shape flag). An event
110
- * with NO ports and a STRING `data` is pushed onto `scopeTransport.deliver` (the
108
+ * with no ports and a string `data` is pushed onto `scopeTransport.deliver` (the
111
109
  * implicit, already-bound scope channel); any other event (no ports, non-string data)
112
110
  * is silently dropped — total, never throws.
113
111
  *
@@ -170,7 +168,7 @@ export declare function createScopeServer(options: ScopeServerOptions, scope?: S
170
168
  *
171
169
  * @remarks
172
170
  * `send` writes each outbound string through `scope.postMessage`. `listen`/`closed`
173
- * register the SINGLE handler `deliver` / the underlying close path route through —
171
+ * register the single handler `deliver` / the underlying close path route through —
174
172
  * the scope server's own `scope` `message`-event listener calls `deliver(event.data)`
175
173
  * for every portless, string-payload event (there is no native registration point on
176
174
  * the scope itself for the scope server to hand a `listen` handler to, so `deliver` is
@@ -190,20 +188,20 @@ export declare function createScopeServer(options: ScopeServerOptions, scope?: S
190
188
  export declare function createScopeTransport(scope: ScopeInterface): ScopeTransportInterface;
191
189
 
192
190
  /**
193
- * Creates the browser-face WebSocket CLIENT transport for an
191
+ * Creates the browser-face WebSocket client transport for an
194
192
  * {@link import('@orkestrel/mcp').MCPClientInterface} — a {@link MCPMessageTransportInterface}
195
- * that drives a REMOTE MCP server over the native `WebSocket` global, the browser
196
- * sibling of the Node face's `createWebSocketClientTransport` (`@orkestrel/mcp/server`).
193
+ * that drives a remote MCP server over the native `WebSocket` global. This factory is the
194
+ * browser sibling of the Node face's `createWebSocketClientTransport` (`@orkestrel/mcp/server`).
197
195
  *
198
196
  * @remarks
199
197
  * Hand it to `createMCPClient({ transport })`: `start()` (run by `client.connect()`)
200
198
  * opens `new WebSocket(options.url, options.protocols)` and awaits the native
201
199
  * `'open'` event — the RFC 6455 handshake itself is the browser's concern. Each
202
- * JSON-RPC message the client `send`s before the socket opens is QUEUED and flushed,
200
+ * JSON-RPC message the client `send`s before the socket opens is queued and flushed,
203
201
  * in order, once it does; each decoded reply is surfaced on the transport's
204
202
  * `message` event for the client's id correlation.
205
203
  *
206
- * @param options - `url` (the remote WebSocket endpoint; REQUIRED) and optional
204
+ * @param options - `url` (the remote WebSocket endpoint; required) and optional
207
205
  * `protocols` (the WebSocket subprotocol(s) to request); see
208
206
  * {@link WebSocketClientTransportOptions}
209
207
  * @returns A working {@link MCPMessageTransportInterface} over the native `WebSocket`
@@ -220,7 +218,7 @@ export declare function createScopeTransport(scope: ScopeInterface): ScopeTransp
220
218
  * const tools = await client.tools()
221
219
  * ```
222
220
  */
223
- export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): MCPMessageTransportInterface_2;
221
+ export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): MCPMessageTransportInterface;
224
222
 
225
223
  /** Supplies the default server name `createScopeServer` reports (`initialize`'s `serverInfo.name`) when `options.name` is omitted. */
226
224
  export declare const DEFAULT_MCP_SERVER_NAME = "@orkestrel/mcp";
@@ -237,33 +235,33 @@ export declare const DEFAULT_MCP_SERVER_VERSION = "1.0.0";
237
235
  * - **Symmetric.** Unlike {@link import('./WebSocketClientTransport.js').WebSocketClientTransport}
238
236
  * / {@link import('@orkestrel/mcp').HTTPClientTransport} (CLIENT-only
239
237
  * carriers of `@orkestrel/mcp`'s `MCPMessageTransportInterface`), a `MessagePort` is a
240
- * plain duplex channel — the SAME class implements `@orkestrel/mcp`'s
241
- * `MCPTransportInterface` and is handed to EITHER `bindServer` or
238
+ * plain duplex channel — the same class implements `@orkestrel/mcp`'s
239
+ * `MCPTransportInterface` and is handed to either `bindServer` or
242
240
  * `bindClient`/`createDuplexClientTransport`; which role it plays comes entirely
243
241
  * from the binder it is given to, not from anything this class decides.
244
242
  * - **`start()` at construction — bind synchronously.** `MessagePort.start()` is only
245
- * REQUIRED when listening with `addEventListener` (as opposed to the `onmessage`
243
+ * required when listening with `addEventListener` (as opposed to the `onmessage`
246
244
  * setter, which implies it) — this transport uses `addEventListener`, and
247
245
  * `MCPTransportInterface` has no separate open/connect step for the caller to hook
248
246
  * a start into, so the constructor calls `port.start()` immediately: the port
249
- * begins dispatching QUEUED messages the moment the transport exists. This is safe
247
+ * begins dispatching queued messages the moment the transport exists. This is safe
250
248
  * inside `createScopeServer`'s flow (the transport is synchronously handed to `bindServer`
251
249
  * before control returns to the event loop), but is a **footgun for direct use**:
252
250
  * if you construct `new MessagePortTransport({ port })` and then `await` anything
253
- * before calling `listen`, messages that arrived in the gap are DROPPED. **Bind
251
+ * before calling `listen`, messages that arrived in the gap are dropped. **Bind
254
252
  * synchronously after construction** — do not interleave an `await` between
255
253
  * `new MessagePortTransport(…)` and `bindServer` / `listen`.
256
254
  * - **String payloads only.** `send` posts the message string as-is (`postMessage`
257
255
  * structured-clones it — a string clones to an identical string, so the wire stays
258
256
  * plain JSON-RPC text like every other transport in this package). Inbound: a
259
257
  * non-string `event.data` (a host or a misbehaving peer posting a structured
260
- * object) is IGNORED — dropped silently, never forwarded, never thrown —
258
+ * object) is ignored — dropped silently, never forwarded, never thrown —
261
259
  * because `MCPTransportInterface` carries no `error` channel for this port to
262
260
  * surface a non-string frame on (unlike `MCPMessageTransportInterface`'s `emitter`);
263
261
  * silently ignoring is the total, contract-shaped choice.
264
- * - **`messageerror` is IGNORED, not routed to `closed`.** A `messageerror` event
262
+ * - **`messageerror` is ignored, not routed to `closed`.** A `messageerror` event
265
263
  * (the structured-clone deserialization of an inbound message threw) reports one
266
- * BAD FRAME, not a dead channel — the port itself keeps working and later, well-
264
+ * bad frame, not a dead channel — the port itself keeps working and later, well-
267
265
  * formed messages still arrive. This transport registers no listener for it: an
268
266
  * unhandled `messageerror` on a `MessagePort` neither throws, closes the port, nor
269
267
  * reaches this transport, so one bad frame costs exactly that frame and nothing
@@ -271,14 +269,14 @@ export declare const DEFAULT_MCP_SERVER_VERSION = "1.0.0";
271
269
  * `bindServer`/`bindClient` wiring (and, transitively, every session it carries)
272
270
  * over a single malformed frame.
273
271
  * - **`close()`** is idempotent: it closes the underlying `port` (`MessagePort.close()`
274
- * disconnects it — further `postMessage` calls on EITHER end are silently
272
+ * disconnects it — further `postMessage` calls on either end are silently
275
273
  * undelivered, per the platform contract) and fires the registered `closed`
276
274
  * handler exactly once, whether the caller closes it once or twice. There is no
277
275
  * native "peer closed" signal for a `MessagePort` (unlike a WebSocket's `close`
278
- * event) — `closed` fires ONLY from this transport's own `close()`.
276
+ * event) — `closed` fires only from this transport's own `close()`.
279
277
  * - **Single-handler-replace (the port contract, `@orkestrel/mcp`'s `MCPTransportInterface`
280
278
  * doc).** `listen`/`closed` each hold the one active handler; a
281
- * second call REPLACES the first rather than adding a second subscriber.
279
+ * second call replaces the first rather than adding a second subscriber.
282
280
  *
283
281
  * @example
284
282
  * ```ts
@@ -291,7 +289,7 @@ export declare const DEFAULT_MCP_SERVER_VERSION = "1.0.0";
291
289
  * bindClient(client, clientTransport) // port2 side is the client's carrier
292
290
  * ```
293
291
  */
294
- export declare class MessagePortTransport implements MCPTransportInterface_2 {
292
+ export declare class MessagePortTransport implements MCPTransportInterface {
295
293
  #private;
296
294
  constructor(options: MessagePortTransportOptions);
297
295
  send(message: string): void;
@@ -306,7 +304,7 @@ export declare class MessagePortTransport implements MCPTransportInterface_2 {
306
304
  *
307
305
  * @remarks
308
306
  * `port` — the channel half to drive (for example, one side of a `new MessageChannel()`, or
309
- * the port a `message` event's `ports[0]` carried). REQUIRED. The SAME transport
307
+ * the port a `message` event's `ports[0]` carried). Required. The same transport
310
308
  * works as either a server or a client carrier — the role comes from whether it is
311
309
  * handed to `bindServer` or `bindClient`/`createDuplexClientTransport` (`@orkestrel/mcp`).
312
310
  */
@@ -324,7 +322,7 @@ export declare interface MessagePortTransportOptions {
324
322
  * Only the members the scope server actually touches: `postMessage` (the
325
323
  * dedicated-worker implicit reply channel), and `addEventListener` /
326
324
  * `removeEventListener` for `'message'` (every inbound event, portless or
327
- * port-bearing, arrives through the SAME listener — see {@link ScopeServerOptions}'s
325
+ * port-bearing, arrives through the same listener — see {@link ScopeServerOptions}'s
328
326
  * doc and the factory). A real `self` / `globalThis` inside a worker satisfies this
329
327
  * structurally (it exposes far more, which this narrower shape ignores).
330
328
  */
@@ -354,20 +352,20 @@ export declare interface ScopeServerInterface {
354
352
  * Options for {@link import('./factories.js').createScopeServer} — the live
355
353
  * {@link ToolManagerInterface} to expose plus the optional server identity, mirroring
356
354
  * `createMCPServer`'s `MCPServerOptions` (`@orkestrel/mcp`) but with `name`/`version`
357
- * OPTIONAL (defaulting to {@link import('./constants.js').DEFAULT_MCP_SERVER_NAME} /
355
+ * optional (defaulting to {@link import('./constants.js').DEFAULT_MCP_SERVER_NAME} /
358
356
  * {@link import('./constants.js').DEFAULT_MCP_SERVER_VERSION}).
359
357
  *
360
358
  * @remarks
361
359
  * - `accept` — optional identity gate consulted **before** a port-bearing `message`
362
360
  * event is accepted; return `false` to drop the event (no binding, no reply).
363
- * **`accept` gates ONLY port-bearing events** — portless messages bypass it and
361
+ * **`accept` gates only port-bearing events** — portless messages bypass it and
364
362
  * deliver directly to the implicit scope channel (the tool executes, blind; in a
365
363
  * Service Worker the reply is silently dropped — see `createScopeServer`'s portless note).
366
364
  * Prefer a handshake token in `event.data` as the primary pattern
367
365
  * (for example, `(event) => event.data === token`) — for same-origin worker/MessagePort
368
366
  * messages `event.origin` is frequently the empty string, making origin
369
367
  * allow-listing unreliable; origin checks are meaningful for cross-origin
370
- * `postMessage` only. When omitted, ALL port-bearing events are accepted — every
368
+ * `postMessage` only. When omitted, all port-bearing events are accepted — every
371
369
  * same-origin context that can reach the scope gets full tool-call access.
372
370
  * See `createScopeServer`'s trust-boundary and portless-events notes.
373
371
  */
@@ -379,7 +377,7 @@ export declare interface ScopeServerOptions {
379
377
  }
380
378
 
381
379
  /**
382
- * Adapts a message-event-bearing SCOPE (`self` in a dedicated Web Worker, or any object
380
+ * Adapts a message-event-bearing scope (`self` in a dedicated Web Worker, or any object
383
381
  * shaped the same way) as a duplex {@link MCPTransportInterface} — the
384
382
  * internal carrier `createScopeServer` binds to route the implicit (portless) message
385
383
  * channel, plus the `deliver` entry point the scope's own `message` listener pushes
@@ -392,42 +390,42 @@ export declare interface ScopeTransportInterface extends MCPTransportInterface {
392
390
  }
393
391
 
394
392
  /**
395
- * Drives a REMOTE MCP server over the native `WebSocket` global from the browser face a
396
- * CLIENT {@link MCPMessageTransportInterface}, the browser sibling of the Node face's
397
- * {@link import('@orkestrel/mcp/server').WebSocketClientTransport}.
393
+ * Drives a remote MCP server over the native `WebSocket` global from the browser face, as a
394
+ * client {@link MCPMessageTransportInterface}. This class is the browser sibling of the Node
395
+ * face's {@link import('@orkestrel/mcp/server').WebSocketClientTransport}.
398
396
  *
399
397
  * @remarks
400
398
  * - **Host-performed handshake.** `start()` opens `new WebSocket(url, protocols)` and
401
399
  * waits for the native `'open'` event — the RFC 6455 handshake itself is entirely
402
400
  * the host's concern, so this transport carries none of the Node client's
403
401
  * `node:crypto` / `node:http(s)` machinery. A connection failure (the native
404
- * `'error'` event while not yet `OPEN`) REJECTS `start()`.
402
+ * `'error'` event while not yet `OPEN`) rejects `start()`.
405
403
  * - **Queued sends.** `send` writes each message as one text frame immediately once
406
404
  * the socket is `OPEN`; a `send` issued before `'open'` fires (or before `start()`
407
- * is even called) is QUEUED and flushed, IN ORDER, the moment the socket opens —
408
- * so a caller need not await `start()` before calling `send`. A queue rides ONE
409
- * connection: a close DISCARDS whatever is still in it.
410
- * - **A closed channel REJECTS.** The native socket confirms nothing about a write, so this
405
+ * is even called) is queued and flushed, in order, the moment the socket opens —
406
+ * so a caller need not await `start()` before calling `send`. A queue rides one
407
+ * connection: a close discards whatever is still in it.
408
+ * - **A closed channel rejects.** The native socket confirms nothing about a write, so this
411
409
  * transport answers from its own state: a `send` after `close()`, or on a socket already
412
- * reporting `CLOSING` / `CLOSED`, REJECTS with `WebSocket transport is not connected` rather
410
+ * reporting `CLOSING` / `CLOSED`, rejects with `WebSocket transport is not connected` rather
413
411
  * than resolving on a frame nobody wrote. Only the closed state rejects — a pre-open `send`
414
412
  * still queues.
415
413
  * - **Inbound (`message`).** Each decoded text frame runs through the shared
416
414
  * `deliverMessage` fold (parse, then narrow) — a well-formed {@link JSONRPCMessage}
417
415
  * re-emits on this transport's `message` event; a non-text (binary) frame or a
418
- * non-JSON / non-message text frame surfaces on `error` and is DROPPED (never
416
+ * non-JSON / non-message text frame surfaces on `error` and is dropped (never
419
417
  * throws on adversarial wire input).
420
418
  * - **`close()`** unsubscribes from the underlying socket, closes it, and fires `close`
421
419
  * (idempotent); the socket's native `close` event (a server-initiated close) fires the
422
- * SAME `close` exactly once total — `close()` first flips the guard, so the native event
420
+ * same `close` exactly once total — `close()` first flips the guard, so the native event
423
421
  * never double-emits, and the released socket reports its own close to nobody. Closing before
424
422
  * the socket opens resolves the pending `start()` rather than leaving it pending, matching the
425
- * Node face. A `send` issued after `close()` REJECTS (it is never queued), and the
426
- * pre-open queue is DISCARDED — by `close()` and by the native `close` event alike — so a
423
+ * Node face. A `send` issued after `close()` rejects (it is never queued), and the
424
+ * pre-open queue is discarded — by `close()` and by the native `close` event alike — so a
427
425
  * closed transport delivers nothing until a `start()` opens a new connection, and nothing
428
426
  * the caller handed the abandoned connection rides that one.
429
427
  * - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); every
430
- * emit the emitter isolates a listener throw; `error` is a DOMAIN event (a
428
+ * emit the emitter isolates a listener throw; `error` is a domain event (a
431
429
  * transport-level fault).
432
430
  *
433
431
  * @example
@@ -455,7 +453,7 @@ export declare class WebSocketClientTransport implements MCPMessageTransportInte
455
453
  * @remarks
456
454
  * - `url` — the absolute `ws://` / `wss://` (or `http://` / `https://`, accepted by
457
455
  * the native `WebSocket` constructor the same way) URL of the remote server's
458
- * WebSocket endpoint. REQUIRED.
456
+ * WebSocket endpoint. Required.
459
457
  * - `protocols` — the WebSocket subprotocol(s) to request. **Defaults to
460
458
  * {@link import('@orkestrel/mcp').MCP_WEBSOCKET_SUBPROTOCOL} (`'mcp'`)**, which
461
459
  * `createWebSocketServer` selects when the offer contains it. Per
@@ -471,7 +469,7 @@ export declare class WebSocketClientTransport implements MCPMessageTransportInte
471
469
  * upgrade request header at all — there is no seam for an `Authorization` bearer to reach.
472
470
  * The Node face owns its own `node:http(s)` upgrade request and therefore can, which is why
473
471
  * only that side offers `headers`. Reach a guarded server from a page with a credential the
474
- * platform DOES carry: a cookie the browser attaches to the upgrade, a subprotocol token, or
472
+ * platform does carry: a cookie the browser attaches to the upgrade, a subprotocol token, or
475
473
  * a signed value in the URL. Adding a `headers` key here would be an option that silently
476
474
  * did nothing.
477
475
  */