@orkestrel/mcp 0.0.27 → 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.
@@ -1,11 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _orkestrel_contract = require("@orkestrel/contract");
3
3
  let _orkestrel_codec = require("@orkestrel/codec");
4
+ let _orkestrel_sse = require("@orkestrel/sse");
4
5
  let _orkestrel_emitter = require("@orkestrel/emitter");
5
6
  let _orkestrel_tool = require("@orkestrel/tool");
6
7
  //#region src/core/constants.ts
7
8
  /**
8
- * The revision offered and defaulted to in the legacy `initialize` handshake.
9
+ * Names the revision offered and defaulted to in the legacy `initialize` handshake,
10
+ * `'2025-11-25'`.
9
11
  *
10
12
  * @remarks
11
13
  * This is deliberately a legacy revision, and the newest one supported. 2026-07-28 is stateless
@@ -13,12 +15,15 @@ let _orkestrel_tool = require("@orkestrel/tool");
13
15
  * it is asking to negotiate a revision with no negotiation.
14
16
  */
15
17
  var MCP_HANDSHAKE_VERSION = "2025-11-25";
16
- /** The older legacy revision the optional legacy decorator accepts and an adapter can pin. */
18
+ /**
19
+ * Names the older legacy revision the optional legacy decorator accepts and an adapter can pin,
20
+ * `'2025-06-18'`.
21
+ */
17
22
  var MCP_FALLBACK_VERSION = "2025-06-18";
18
- /** The modern revision offered by an unpinned client during discovery. */
23
+ /** Names the modern revision offered by an unpinned client during discovery, `'2026-07-28'`. */
19
24
  var MCP_MODERN_VERSION = "2026-07-28";
20
25
  /**
21
- * The modern MCP protocol revisions a bare server accepts and advertises.
26
+ * Lists the modern MCP protocol revisions a bare server accepts and advertises, `2026-07-28`.
22
27
  *
23
28
  * @remarks
24
29
  * Frozen in discovery-advertisement order. Legacy revisions are absent because
@@ -26,45 +31,51 @@ var MCP_MODERN_VERSION = "2026-07-28";
26
31
  * decorator own them.
27
32
  */
28
33
  var SUPPORTED_MODERN_PROTOCOL_VERSIONS = Object.freeze([MCP_MODERN_VERSION]);
29
- /** The protocol revisions accepted by the optional legacy decorator. */
34
+ /**
35
+ * Lists the protocol revisions accepted by the optional legacy decorator, `2025-11-25` and
36
+ * `2025-06-18`.
37
+ */
30
38
  var SUPPORTED_LEGACY_PROTOCOL_VERSIONS = Object.freeze([MCP_HANDSHAKE_VERSION, MCP_FALLBACK_VERSION]);
31
- /** The protocol revisions the `isMCPVersion` guard admits, spanning the modern and legacy eras. */
39
+ /**
40
+ * Lists the protocol revisions the `isMCPVersion` guard admits, spanning the modern and legacy
41
+ * eras.
42
+ */
32
43
  var SUPPORTED_MCP_VERSIONS = Object.freeze([...SUPPORTED_MODERN_PROTOCOL_VERSIONS, ...SUPPORTED_LEGACY_PROTOCOL_VERSIONS]);
33
- /** Reserved modern `_meta` key carrying the request's protocol revision. */
44
+ /** Names the reserved modern `_meta` key carrying the request's protocol revision. */
34
45
  var MCP_META_VERSION = "io.modelcontextprotocol/protocolVersion";
35
- /** Reserved modern `_meta` key carrying the client's open capability record. */
46
+ /** Names the reserved modern `_meta` key carrying the client's open capability record. */
36
47
  var MCP_META_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities";
37
- /** Reserved modern `_meta` key carrying the optional client identity. */
48
+ /** Names the reserved modern `_meta` key carrying the optional client identity. */
38
49
  var MCP_META_CLIENT = "io.modelcontextprotocol/clientInfo";
39
- /** Reserved modern `_meta` key carrying the server identity on results. */
50
+ /** Names the reserved modern `_meta` key carrying the server identity on results. */
40
51
  var MCP_META_SERVER = "io.modelcontextprotocol/serverInfo";
41
- /** Reserved modern `_meta` key carrying a `subscriptions/listen` request id. */
52
+ /** Names the reserved modern `_meta` key carrying a `subscriptions/listen` request id. */
42
53
  var MCP_META_SUBSCRIPTION = "io.modelcontextprotocol/subscriptionId";
43
54
  /**
44
- * The reserved extension key identifying the stable Tasks extension.
55
+ * Names the reserved extension key identifying the stable Tasks extension.
45
56
  *
46
57
  * @remarks
47
- * The ONE spelling of it in this package, and the identity of the immutable snapshot dated
48
- * 2026-07-28 this package implements. A client declares it per REQUEST, under
58
+ * The one spelling of it in this package, and the identity of the immutable snapshot dated
59
+ * 2026-07-28 this package implements. A client declares it per request, under
49
60
  * `_meta['io.modelcontextprotocol/clientCapabilities'].extensions`; a server advertises it
50
61
  * under `server/discover`'s `capabilities.extensions`. Both sides carry an empty object —
51
62
  * the extension defines no options, so presence is the entire declaration.
52
63
  */
53
64
  var MCP_EXTENSION_TASKS = "io.modelcontextprotocol/tasks";
54
65
  /**
55
- * The opening marker of the Base64 sentinel a standard MCP header value travels in.
66
+ * Names the opening marker of the Base64 sentinel a standard MCP header value travels in.
56
67
  *
57
68
  * @remarks
58
- * The markers are LOWERCASE and exact, and this constant with {@link MCP_SENTINEL_SUFFIX} is
59
- * their ONE spelling in this package: {@link import('@orkestrel/mcp').encodeSentinel} builds a
69
+ * The markers are lowercase and exact, and this constant with {@link MCP_SENTINEL_SUFFIX} is
70
+ * their one spelling in this package: {@link import('@orkestrel/mcp').encodeSentinel} builds a
60
71
  * sentinel from them and {@link import('@orkestrel/mcp').decodeSentinel} recognizes one by
61
- * them, so the two directions cannot drift apart.
72
+ * them, so the directions cannot drift apart.
62
73
  */
63
74
  var MCP_SENTINEL_PREFIX = "=?base64?";
64
- /** The closing marker of the Base64 sentinel a standard MCP header value travels in. */
75
+ /** Names the closing marker of the Base64 sentinel a standard MCP header value travels in. */
65
76
  var MCP_SENTINEL_SUFFIX = "?=";
66
77
  /**
67
- * The request-header prefix an `x-mcp-header` annotation projects a tool argument onto.
78
+ * Names the request-header prefix an `x-mcp-header` annotation projects a tool argument onto.
68
79
  *
69
80
  * @remarks
70
81
  * The full field name is this prefix followed by the annotation's own value verbatim, so
@@ -74,17 +85,66 @@ var MCP_SENTINEL_SUFFIX = "?=";
74
85
  */
75
86
  var MCP_PARAM_PREFIX = "Mcp-Param-";
76
87
  /**
77
- * The tool-schema annotation key naming the header one parameter projects into.
88
+ * Names the Streamable-HTTP transport header that carries the MCP session id.
89
+ *
90
+ * @remarks
91
+ * A stateful server sends it on the `initialize` reply, and
92
+ * {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport} echoes it as a
93
+ * request header on every subsequent request, so a client passes that server's session
94
+ * validation unchanged.
95
+ */
96
+ var MCP_SESSION_HEADER = "mcp-session-id";
97
+ /**
98
+ * Names the Streamable-HTTP transport header carrying the MCP protocol version.
99
+ *
100
+ * @remarks
101
+ * A modern request derives it from its own `_meta`; a legacy request echoes the revision the
102
+ * `initialize` result negotiated on each subsequent request.
103
+ */
104
+ var MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version";
105
+ /**
106
+ * Names the modern Streamable-HTTP request header carrying the JSON-RPC method.
78
107
  *
79
108
  * @remarks
80
- * It is valid ONLY on a primitive property schema statically reachable from the `inputSchema`
109
+ * It is stamped on every modern request and on no legacy request.
110
+ */
111
+ var MCP_METHOD_HEADER = "mcp-method";
112
+ /**
113
+ * Names the modern Streamable-HTTP request header carrying a named target.
114
+ *
115
+ * @remarks
116
+ * The HTTP client transport stamps it only for `tools/call`, from that request's `params.name`,
117
+ * in the Base64 sentinel form whenever the name cannot ride as plain ASCII.
118
+ */
119
+ var MCP_NAME_HEADER = "mcp-name";
120
+ /**
121
+ * Identifies the tool-schema annotation key naming the header one parameter projects into.
122
+ *
123
+ * @remarks
124
+ * It is valid only on a primitive property schema statically reachable from the `inputSchema`
81
125
  * root through `properties` keys alone. An occurrence anywhere else — under `items`, a
82
126
  * composition or conditional keyword, or a `$ref` target — makes the whole tool definition
83
127
  * invalid, which is what {@link import('@orkestrel/mcp').buildHeaderParameters} decides.
84
128
  */
85
129
  var MCP_HEADER_ANNOTATION = "x-mcp-header";
86
130
  /**
87
- * The `tools/list` pages one modern `tools/call` walks to reach its own annotations.
131
+ * Names the WebSocket subprotocol `createWebSocketClientTransport` requests by default
132
+ * `'mcp'`, which `createWebSocketServer` selects when the client offers it. Per RFC 6455
133
+ * §4.1 a client MUST fail the connection if the server returns
134
+ * a subprotocol it did not request; Node ≥ 22 (undici) enforces this strictly, so the
135
+ * default bakes the correct value in. Override `WebSocketClientTransportOptions.protocols`
136
+ * only when connecting to a foreign server that speaks a different subprotocol (or `[]`
137
+ * for no subprotocol negotiation at all).
138
+ *
139
+ * @remarks
140
+ * The client sends it in `Sec-WebSocket-Protocol` and the server echoes it in its `101`
141
+ * handshake, so an MCP WebSocket endpoint is distinguishable from any other WebSocket on the
142
+ * same path. The default WebSocket upgrade path is the same `'/mcp'` the HTTP transport mounts
143
+ * at — the upgrade is selected by the `Upgrade: websocket` header, not a separate path.
144
+ */
145
+ var MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
146
+ /**
147
+ * Bounds the `tools/list` pages one modern `tools/call` walks to reach its own annotations.
88
148
  *
89
149
  * @remarks
90
150
  * The HTTP POST handler reads a called tool's {@link MCP_HEADER_ANNOTATION} annotations by
@@ -98,13 +158,14 @@ var MCP_HEADER_ANNOTATION = "x-mcp-header";
98
158
  * answer a name no served definition annotates receives.
99
159
  */
100
160
  var MCP_LOOKUP_PAGES = 8;
101
- /** MCP reserved error: required HTTP metadata does not match the request body. */
161
+ /** Names the MCP reserved error for required HTTP metadata that does not match the request body. */
102
162
  var MCP_HEADER_MISMATCH = -32020;
103
163
  /**
104
- * MCP reserved error: an operation needs a client capability that was not declared.
164
+ * Names the MCP reserved error for an operation needing a client capability that was not
165
+ * declared.
105
166
  *
106
167
  * @remarks
107
- * The GENERIC code for the whole condition, not one capability's code. This server answers
168
+ * The generic code for the whole condition, not one capability's code. This server answers
108
169
  * it in more than one place — a `tools/call` that needs `elicitation`, and a `tasks/*` request
109
170
  * whose client never declared `io.modelcontextprotocol/tasks` — and they are told apart by
110
171
  * `error.data.requiredCapabilities` alone (`{ elicitation: {} }` against
@@ -114,10 +175,10 @@ var MCP_HEADER_MISMATCH = -32020;
114
175
  * schema is what a peer implements against.
115
176
  */
116
177
  var MCP_MISSING_CAPABILITY = -32021;
117
- /** MCP reserved error: a request names an unsupported protocol revision. */
178
+ /** Names the MCP reserved error for a request naming an unsupported protocol revision. */
118
179
  var MCP_UNSUPPORTED_VERSION = -32022;
119
180
  /**
120
- * Default modern result freshness lifetime in milliseconds.
181
+ * Sets the default modern result freshness lifetime in milliseconds.
121
182
  *
122
183
  * @remarks
123
184
  * `ttlMs` is required on cacheable results, while zero means immediately stale
@@ -125,7 +186,8 @@ var MCP_UNSUPPORTED_VERSION = -32022;
125
186
  */
126
187
  var DEFAULT_MCP_CACHE_TTL = 6e4;
127
188
  /**
128
- * Secure server bounds used when the matching `limit` option leaf is absent or malformed.
189
+ * Sets the secure server bounds used when the matching `limit` option leaf is absent or
190
+ * malformed.
129
191
  *
130
192
  * @remarks
131
193
  * One MiB admits ordinary JSON-RPC requests and substantial tool arguments; 16 KiB admits
@@ -147,10 +209,10 @@ var DEFAULT_MCP_LIMITS = Object.freeze({
147
209
  depth: 32
148
210
  });
149
211
  /**
150
- * The one empty argument record every argument-less modern `tools/call` runs with.
212
+ * Holds the one empty argument record every argument-less modern `tools/call` runs with.
151
213
  *
152
214
  * @remarks
153
- * Frozen and null-prototype, and SHARED: two calls that name no `arguments` receive the same
215
+ * Frozen and null-prototype, and shared: two calls that name no `arguments` receive the same
154
216
  * reference, so nothing a tool writes into its own `arguments` can survive into the next
155
217
  * call — the write fails instead. That failure is a tool-domain failure like any other: the
156
218
  * registry isolates it into a `success: false` result, which reaches the peer as an
@@ -161,48 +223,55 @@ var DEFAULT_MCP_LIMITS = Object.freeze({
161
223
  * `arguments.constructor` is `undefined` here rather than a function.
162
224
  */
163
225
  var EMPTY_MCP_ARGUMENTS = Object.freeze(Object.create(null));
164
- /** JSON-RPC 2.0 reserved error: invalid JSON was received (the message did not parse). */
226
+ /** Names the JSON-RPC 2.0 reserved error for invalid JSON received (the message did not parse). */
165
227
  var JSONRPC_PARSE_ERROR = -32700;
166
- /** JSON-RPC 2.0 reserved error: the payload was not a valid Request object. */
228
+ /** Names the JSON-RPC 2.0 reserved error for a payload that was not a valid Request object. */
167
229
  var JSONRPC_INVALID_REQUEST = -32600;
168
- /** JSON-RPC 2.0 reserved error: the requested method does not exist. */
230
+ /** Names the JSON-RPC 2.0 reserved error for a requested method that does not exist. */
169
231
  var JSONRPC_METHOD_NOT_FOUND = -32601;
170
- /** JSON-RPC 2.0 reserved error: the method's parameters were invalid. */
232
+ /** Names the JSON-RPC 2.0 reserved error for a method's invalid parameters. */
171
233
  var JSONRPC_INVALID_PARAMS = -32602;
172
234
  /**
173
- * JSON-RPC 2.0 reserved error: the server failed while handling an otherwise valid request.
235
+ * Names the JSON-RPC 2.0 reserved error for a server that failed while handling an otherwise
236
+ * valid request.
174
237
  *
175
238
  * @remarks
176
- * The code every MODERN internal fault answers with — a provider, handler, continuation,
239
+ * The code every modern internal fault answers with — a provider, handler, continuation,
177
240
  * capacity, stream-source, normalization, or serialization failure the server contained.
178
241
  * It is detail-free on the wire: the caught value reaches the application through the
179
242
  * server's `error` event and never through the response.
180
243
  */
181
244
  var JSONRPC_INTERNAL_ERROR = -32603;
182
245
  /**
183
- * JSON-RPC 2.0 implementation-defined server error (the `-32000` to `-32099` range).
246
+ * Names the JSON-RPC 2.0 implementation-defined server error (the `-32000` to `-32099` range).
184
247
  *
185
248
  * @remarks
186
- * Retained for the LEGACY branch alone. A modern fault answers
249
+ * Retained for the legacy branch alone. A modern fault answers
187
250
  * {@link JSONRPC_INTERNAL_ERROR}; this code survives only where an old-wire peer was
188
251
  * already characterized against it.
189
252
  */
190
253
  var JSONRPC_SERVER_ERROR = -32e3;
191
- /** The default client name reported in the MCP `initialize` handshake (`clientInfo.name`). */
192
- var DEFAULT_MCP_CLIENT_NAME = "taverna";
193
- /** The default client version reported in the MCP `initialize` handshake (`clientInfo.version`). */
254
+ /**
255
+ * Supplies the default client name reported in the MCP `initialize` handshake
256
+ * (`clientInfo.name`).
257
+ */
258
+ var DEFAULT_MCP_CLIENT_NAME = "@orkestrel/mcp";
259
+ /**
260
+ * Supplies the default client version reported in the MCP `initialize` handshake
261
+ * (`clientInfo.version`).
262
+ */
194
263
  var DEFAULT_MCP_CLIENT_VERSION = "1.0.0";
195
264
  /**
196
- * The default per-request deadline (ms) an `MCPClient` applies when `options.timeout`
265
+ * Sets the default per-request deadline (ms) an `MCPClient` applies when `options.timeout`
197
266
  * is unset — a request the remote server does not answer within it rejects.
198
267
  */
199
268
  var DEFAULT_MCP_REQUEST_TIMEOUT = 3e4;
200
- /** The default number of subscription frames retained while no client read is parked. */
269
+ /** Sets the default number of subscription frames retained while no client read is parked. */
201
270
  var DEFAULT_MCP_SUBSCRIPTION_CAPACITY = 64;
202
271
  //#endregion
203
272
  //#region src/core/errors.ts
204
273
  /**
205
- * A Model Context Protocol error preserving its machine-readable numeric code and
274
+ * Preserves a Model Context Protocol error's machine-readable numeric code and
206
275
  * optional structured context.
207
276
  *
208
277
  * @remarks
@@ -245,7 +314,7 @@ var MCPError = class extends Error {
245
314
  * Determines whether an unknown value is an {@link MCPError}.
246
315
  *
247
316
  * @param value - The unknown value to inspect
248
- * @returns `true` only when the value is an `MCPError`
317
+ * @returns True if the value is an `MCPError`; false otherwise
249
318
  *
250
319
  * @example
251
320
  * ```ts
@@ -376,19 +445,19 @@ function snapshotToolResult(value, limits) {
376
445
  *
377
446
  * @remarks
378
447
  * Total — a non-message returns `undefined`, never throws. The input must
379
- * ALREADY be `JSON.parse`d: the raw-string parse (which can throw on malformed
448
+ * already be `JSON.parse`d: the raw-string parse (which can throw on malformed
380
449
  * JSON) happens in `MCPServer.handle` inside a try/catch that maps a parse failure
381
450
  * to a `-32700` response.
382
451
  *
383
- * A defined result is an OWNED CANONICAL SNAPSHOT, never the input reference: it is
452
+ * A defined result is an owned canonical snapshot, never the input reference: it is
384
453
  * rebuilt from the canonical text and deeply frozen, so `-0` arrives as `0`. Every record
385
- * was SERIALIZED with its keys sorted, but the rebuilt object enumerates its own keys the
454
+ * was serialized with its keys sorted, but the rebuilt object enumerates its own keys the
386
455
  * way JavaScript does, so an integer-like `'9'` still precedes `'10'`: the result's key
387
456
  * order is neither promised nor generally the canonical one. A caller who needs canonical
388
- * BYTES takes them from `serializeJSON`/`snapshotJSON` rather than re-stringifying this
457
+ * bytes takes them from `serializeJSON`/`snapshotJSON` rather than re-stringifying this
389
458
  * result. Identity is not preserved and is not promised.
390
459
  *
391
- * The parser's sound partner is the COMPOSITE `isJSONRPCMessage(value) &&
460
+ * The parser's sound partner is the composite `isJSONRPCMessage(value) &&
392
461
  * isBoundedJSON(value, limits)`, and against it both halves of the soundness law
393
462
  * hold by construction:
394
463
  *
@@ -396,12 +465,12 @@ function snapshotToolResult(value, limits) {
396
465
  * is applied to the exact frozen reference returned.
397
466
  * - Every input satisfying the composite is admitted rather than rejected, because
398
467
  * `isBoundedJSON` is this parser's own admission test — the same canonical
399
- * serializer under the same `limits` — so the two cannot disagree about the bound.
468
+ * serializer under the same `limits` — so they cannot disagree about the bound.
400
469
  *
401
- * {@link isJSONRPCMessage} ALONE is not that partner. It is clone-backed and so already
470
+ * {@link isJSONRPCMessage} Alone is not that partner. It is clone-backed and so already
402
471
  * exact about shape, but it carries no size or depth bound — so guard-valid values
403
472
  * exist that this parser rejects: a message nested deeper than `limits.depth`, and one
404
- * whose canonical text exceeds `limits.bytes`. Those are named causes, NOT a complete
473
+ * whose canonical text exceeds `limits.bytes`. Those are named causes, not a complete
405
474
  * boundary. Among values `isJSONRPCMessage` already admits, the admitted set is exactly
406
475
  * what canonical serialization accepts under `limits`, so a caller who needs that line
407
476
  * tests it with `isBoundedJSON` rather than inferring it from this list.
@@ -431,7 +500,7 @@ function parseJSONRPCMessage(value, limits = {
431
500
  * This is the validity step after {@link isModernRequest}: a defined result can
432
501
  * only come from a guard-positive request, while a guard-positive request returns
433
502
  * `undefined` when its required modern metadata is malformed — and also when the
434
- * request falls outside the bound this parser INHERITS by routing through
503
+ * request falls outside the bound this parser inherits by routing through
435
504
  * {@link parseJSONRPCMessage} under the same `limits`. The version
436
505
  * must be a string but need not be supported; unsupported strings belong to the
437
506
  * dedicated protocol-version error path. Client identity is optional, but when
@@ -480,11 +549,11 @@ function parseRequestContext(value, limits = {
480
549
  * @remarks
481
550
  * This parser does not open the opaque continuation carrier; the configured
482
551
  * continuation port performs that boundary first. The protected
483
- * payload binds the authenticated principal, absolute expiry, ORIGINAL request id, version,
552
+ * payload binds the authenticated principal, absolute expiry, original request id, version,
484
553
  * method, the exact round that was issued, tool name, argument digest, and optional
485
554
  * application state. Every member is required except application state: a payload missing its
486
555
  * round cannot have the client's answers enforced, so it is refused rather than admitted
487
- * unenforced. An EMPTY round is refused for the same reason — a retry against it would answer
556
+ * unenforced. An empty round is refused for the same reason — a retry against it would answer
488
557
  * no question at all. Total over malformed or hostile input.
489
558
  *
490
559
  * @param value - The opened canonical continuation value to parse
@@ -498,7 +567,7 @@ function parseRequestContext(value, limits = {
498
567
  function parseMCPInputState(value) {
499
568
  try {
500
569
  if (!(0, _orkestrel_contract.isString)(value)) return void 0;
501
- const parsed = JSON.parse(value);
570
+ const parsed = (0, _orkestrel_contract.parseJSON)(value);
502
571
  if (!(0, _orkestrel_contract.isRecord)(parsed)) return void 0;
503
572
  const principal = parsed["principal"];
504
573
  const expiry = parsed["expiry"];
@@ -540,15 +609,15 @@ function parseMCPInputState(value) {
540
609
  * does not authorize a form request. Total over hostile input.
541
610
  *
542
611
  * @param value - The client capability record to inspect
543
- * @returns `true` when form-mode elicitation is declared
612
+ * @returns True if form-mode elicitation is declared; false otherwise
544
613
  *
545
614
  * @example
546
615
  * ```ts
547
- * isFormElicitationSupported({ elicitation: {} }) // true — implicit form mode
548
- * isFormElicitationSupported({ elicitation: { url: {} } }) // false
616
+ * supportsFormElicitation({ elicitation: {} }) // true — implicit form mode
617
+ * supportsFormElicitation({ elicitation: { url: {} } }) // false
549
618
  * ```
550
619
  */
551
- function isFormElicitationSupported(value) {
620
+ function supportsFormElicitation(value) {
552
621
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
553
622
  if (!owned.success) return false;
554
623
  try {
@@ -564,19 +633,19 @@ function isFormElicitationSupported(value) {
564
633
  * Computes the capabilities one round of input requests needs and the client did not declare.
565
634
  *
566
635
  * @remarks
567
- * The protocol's rule is about SENDING: a server never issues a request kind the client's
636
+ * The protocol's rule is about sending: a server never issues a request kind the client's
568
637
  * declared capabilities exclude. So this reads the round rather than the method, and it
569
638
  * answers with the refusal's own payload — the `requiredCapabilities` record a
570
639
  * `MissingRequiredClientCapability` error carries, keyed by each missing capability, in the
571
640
  * `ClientCapabilities` shape the schema defines rather than as a list of names.
572
641
  *
573
642
  * Each kind maps to one declaration: `sampling/createMessage` to `sampling`, `roots/list` to
574
- * `roots`, a form elicitation to what {@link isFormElicitationSupported} accepts, and a
643
+ * `roots`, a form elicitation to what {@link supportsFormElicitation} accepts, and a
575
644
  * URL-mode elicitation to a record-valued `elicitation.url`. A request this package cannot
576
645
  * recognize needs nothing, because {@link import('./validators.js').isMCPInputRequestMap}
577
646
  * has already refused the round it would have travelled in. Total over hostile input.
578
647
  *
579
- * The `elicitation` value names the ARM the round needs, so a client can act on the refusal
648
+ * The `elicitation` value names the arm the round needs, so a client can act on the refusal
580
649
  * by declaring exactly what the payload asks for. A missing URL arm answers `{ url: {} }`, a
581
650
  * missing form arm answers the empty record this package reads as form-only, and a round
582
651
  * needing both answers `{ form: {}, url: {} }`. An empty record for a URL round would name
@@ -612,7 +681,7 @@ function computeMissingCapabilities(requests, capabilities) {
612
681
  if (!(0, _orkestrel_contract.isRecord)(elicitation) || !(0, _orkestrel_contract.isRecord)(elicitation["url"])) urlUndeclared = true;
613
682
  continue;
614
683
  }
615
- if (!isFormElicitationSupported(declared)) formUndeclared = true;
684
+ if (!supportsFormElicitation(declared)) formUndeclared = true;
616
685
  }
617
686
  if (formUndeclared && !urlUndeclared) missing["elicitation"] = {};
618
687
  if (urlUndeclared && !formUndeclared) missing["elicitation"] = { url: {} };
@@ -627,28 +696,28 @@ function computeMissingCapabilities(requests, capabilities) {
627
696
  *
628
697
  * @remarks
629
698
  * The declaration lives at `extensions['io.modelcontextprotocol/tasks']` and the schema
630
- * types its value EXACTLY EMPTY — `Record<string, never>`, an object with no additional
699
+ * types its value exactly empty — `Record<string, never>`, an object with no additional
631
700
  * properties. So the key's presence is the whole declaration, and the value carries the
632
701
  * whole of the check: a `true` or a string there is a client speaking a different protocol
633
702
  * rather than a shorthand, and a member inside the object is a client declaring an option
634
703
  * this extension does not define. Both are refused, because a server that accepted either
635
704
  * would be reading a shape no peer can produce from the snapshot's own schema.
636
705
  *
637
- * A client declares this PER REQUEST. Nothing here consults a session, because the modern
706
+ * A client declares this per request. Nothing here consults a session, because the modern
638
707
  * revision is stateless and a capability declared once at connect time says nothing about
639
708
  * the request in hand. Total over hostile input.
640
709
  *
641
710
  * @param value - The client capability record to inspect
642
- * @returns `true` when the tasks extension is declared as the schema's empty object
711
+ * @returns True if the tasks extension is declared as the schema's empty object; false otherwise
643
712
  *
644
713
  * @example
645
714
  * ```ts
646
- * isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
647
- * isTaskSupported({ extensions: {} }) // false — the key is the declaration
648
- * isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': { on: true } } }) // false
715
+ * supportsTask({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
716
+ * supportsTask({ extensions: {} }) // false — the key is the declaration
717
+ * supportsTask({ extensions: { 'io.modelcontextprotocol/tasks': { on: true } } }) // false
649
718
  * ```
650
719
  */
651
- function isTaskSupported(value) {
720
+ function supportsTask(value) {
652
721
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
653
722
  if (!owned.success) return false;
654
723
  try {
@@ -915,7 +984,7 @@ function buildProgressNotification(token, progress) {
915
984
  * Builds one official cancellation notification for a request already sent.
916
985
  *
917
986
  * @remarks
918
- * `requestId` and `reason` are WIRE SPELLINGS carried verbatim from the dated schema's
987
+ * `requestId` and `reason` are wire spellings carried verbatim from the dated schema's
919
988
  * `CancelledNotificationParams`, and so is the `cancelled` in the method name — this
920
989
  * package's own vocabulary says `abort`, but the method is the protocol's and does not
921
990
  * change. The notification is FIRE-AND-FORGET in the strongest sense: it carries no id,
@@ -925,7 +994,7 @@ function buildProgressNotification(token, progress) {
925
994
  * rather than as a violation.
926
995
  *
927
996
  * Only write one on a carrier that accepts a client-initiated notification — see
928
- * {@link import('./types.js').MCPClientTransportInterface.duplex}. On Streamable HTTP the
997
+ * {@link import('./types.js').MCPMessageTransportInterface.duplex}. On Streamable HTTP the
929
998
  * dated revision defines no such frame, and closing the response stream is the
930
999
  * cancellation signal instead.
931
1000
  *
@@ -953,7 +1022,7 @@ function buildCancelledNotification(id, reason) {
953
1022
  * Determines whether one method may answer with a given modern `resultType`.
954
1023
  *
955
1024
  * @remarks
956
- * The dated protocol lets a `tools/call` answer in more than one way — it COMPLETED, it became a
1025
+ * The dated protocol lets a `tools/call` answer in more than one way — it completed, it became a
957
1026
  * durable task, or it needs another round trip — while every other method this client
958
1027
  * issues has exactly one legal answer. So the arm a peer chose is only meaningful beside
959
1028
  * the method it answers, and this is the one place that pairing is decided.
@@ -964,7 +1033,7 @@ function buildCancelledNotification(id, reason) {
964
1033
  *
965
1034
  * @param method - The method the pending request was issued for
966
1035
  * @param resultType - The unknown `resultType` the peer answered with
967
- * @returns Whether that method may legally answer with that `resultType`
1036
+ * @returns True if that method may legally answer with that `resultType`; false otherwise
968
1037
  *
969
1038
  * @example
970
1039
  * ```ts
@@ -982,9 +1051,9 @@ function matchesResultType(method, resultType) {
982
1051
  * Concatenates an MCP tool-call result's text content blocks into one string.
983
1052
  *
984
1053
  * @remarks
985
- * The inverse of a server splitting a value into text block(s), and TOTAL: a non-record
1054
+ * The inverse of a server splitting a value into text block(s), and total: a non-record
986
1055
  * result, a non-array `content`, or a non-string `text` contributes nothing rather than
987
- * throwing. What it returns is a RENDERING — the prose a model reads — and not the tool's
1056
+ * throwing. What it returns is a rendering — the prose a model reads — and not the tool's
988
1057
  * value, which travels as `structuredContent` whenever the peer sent one.
989
1058
  *
990
1059
  * @param result - The unknown result payload to read content blocks from
@@ -1011,12 +1080,12 @@ function extractContentText(result) {
1011
1080
  * the arms the protocol gives a shape to, and deriving the tool's value from the one it
1012
1081
  * does not:
1013
1082
  *
1014
- * - A peer's `structuredContent` is PREFERRED over the content blocks, because it is the
1083
+ * - A peer's `structuredContent` is preferred over the content blocks, because it is the
1015
1084
  * tool's value in its original structure while the blocks are a rendering beside it. Its
1016
1085
  * mere presence decides — an explicit `null` is a value the tool returned, not an absence.
1017
1086
  * - With no structured value the legacy shape applies: the value was JSON-serialized into
1018
1087
  * the text block(s), so parse them and fall back to the raw string when they are not JSON.
1019
- * - A remote tool FAILURE (`isError: true`) THROWS the error text, so an agent's tool
1088
+ * - A remote tool failure (`isError: true`) throws the error text, so an agent's tool
1020
1089
  * registry isolates it into a failure result exactly as it would a local throw.
1021
1090
  *
1022
1091
  * @param name - The tool's name, used only to describe a failure that carried no text
@@ -1098,14 +1167,14 @@ function buildJSONRPCResult(id, result) {
1098
1167
  * as an `error` object.
1099
1168
  *
1100
1169
  * @remarks
1101
- * An `undefined` `id` is OMITTED from the envelope rather than serialized as `null`:
1170
+ * An `undefined` `id` is omitted from the envelope rather than serialized as `null`:
1102
1171
  * MCP overrides the base specification here, so a peer that could not have its id
1103
1172
  * read receives a response with no `id` member at all.
1104
1173
  *
1105
1174
  * @param id - The failed request's id, or `undefined` when none could be read
1106
1175
  * @param code - One of the reserved JSON-RPC codes (see `./constants.js`)
1107
1176
  * @param message - A short human description of the failure
1108
- * @param data - An OPTIONAL machine-readable payload (omitted from the envelope when absent)
1177
+ * @param data - An optional machine-readable payload (omitted from the envelope when absent)
1109
1178
  * @returns The error response envelope
1110
1179
  */
1111
1180
  function buildJSONRPCError(id, code, message, data) {
@@ -1127,11 +1196,11 @@ function buildJSONRPCError(id, code, message, data) {
1127
1196
  * receives.
1128
1197
  *
1129
1198
  * @remarks
1130
- * The ONE place a cancellation signal is resolved. A caller may have no signal to
1199
+ * The one place a cancellation signal is resolved. A caller may have no signal to
1131
1200
  * offer; a dispatched method always has one to observe, so a missing signal becomes
1132
1201
  * a real signal rather than an absence every downstream handler would have to case on.
1133
1202
  *
1134
- * The resolved signal is the request's LIFETIME, which is strictly wider than the
1203
+ * The resolved signal is the request's lifetime, which is strictly wider than the
1135
1204
  * caller's: it composes the caller's signal, when there is one, with the `lifetime`
1136
1205
  * dispatch owns and aborts once the answer this request produced is finished. That is
1137
1206
  * what wakes a stream producer parked on an event that will never arrive after its
@@ -1319,7 +1388,7 @@ function buildSubscriptionFilter(requested, supported, enabled = false) {
1319
1388
  *
1320
1389
  * @param notification - The server notification offered by the configured producer
1321
1390
  * @param filter - The filter acknowledged to the client
1322
- * @returns `true` when the notification belongs on this subscription stream
1391
+ * @returns True if the notification belongs on this subscription stream; false otherwise
1323
1392
  */
1324
1393
  function matchesSubscriptionNotification(notification, filter) {
1325
1394
  if (notification.method === "notifications/tools/list_changed") return filter.toolsListChanged === true;
@@ -1384,7 +1453,7 @@ function buildSubscriptionResult(id, identity) {
1384
1453
  * `capabilities.resources` and `capabilities.prompts` appear only for servers with their
1385
1454
  * respective managers and derive notification flags from the configured subscription filter.
1386
1455
  * `capabilities.completions` is independent and appears only with a completion provider.
1387
- * `capabilities.extensions` appears only for a server that CONFIGURED the extension it
1456
+ * `capabilities.extensions` appears only for a server that configured the extension it
1388
1457
  * would name. An advertisement is a promise a client is entitled to act on, so a server
1389
1458
  * with no `task` policy omits the member entirely rather than advertising an empty
1390
1459
  * record — and its discovery answer stays byte-for-byte what it was before the extension
@@ -1440,8 +1509,8 @@ function buildInitializeResult(name, version, requested) {
1440
1509
  * before it hands the string on.
1441
1510
  *
1442
1511
  * @remarks
1443
- * The bound is checked FIRST, against the raw string, so an oversized message is never
1444
- * `JSON.parse`d at all: a decoder that parses before it measures has already spent the work
1512
+ * The bound is checked first, against the raw string, so an oversized message is never
1513
+ * parsed at all: a decoder that parses before it measures has already spent the work
1445
1514
  * the bound exists to refuse. A message over the bound, malformed JSON, and a well-formed
1446
1515
  * value that is not a JSON-RPC message are one answer — `undefined` — because a binder does
1447
1516
  * exactly the same thing with each of them: nothing, and let
@@ -1461,8 +1530,129 @@ function buildInitializeResult(name, version, requested) {
1461
1530
  */
1462
1531
  function decodeBoundedMessage(message, limits) {
1463
1532
  if (!isBoundedString(message, limits.bytes)) return void 0;
1464
- const parsed = (0, _orkestrel_contract.attempt)(() => JSON.parse(message));
1465
- return parsed.success ? parseJSONRPCMessage(parsed.value, limits) : void 0;
1533
+ return parseJSONRPCMessage((0, _orkestrel_contract.parseJSON)(message), limits);
1534
+ }
1535
+ /**
1536
+ * Decodes one inbound frame and delivers it onto a transport emitter as `message` or `error`.
1537
+ *
1538
+ * @remarks
1539
+ * The one inbound fold every message-carrying transport in this package runs: parse the frame,
1540
+ * narrow it with `parseJSONRPCMessage`, emit `message` for a well-formed
1541
+ * {@link JSONRPCMessage}, and emit `error` for anything else. Total — an adversarial frame
1542
+ * produces an `error` emission and never a throw.
1543
+ *
1544
+ * The failures report differently on purpose. Unparsable text emits the caught parse
1545
+ * error, which names the offending position; well-formed JSON that is not a JSON-RPC message
1546
+ * has no caught value to report, so it emits `fault` — the carrier's own wording, passed in
1547
+ * rather than forked into a second copy of this body.
1548
+ *
1549
+ * @param emitter - The transport's emitter to deliver onto
1550
+ * @param text - One inbound frame's raw text
1551
+ * @param fault - The message for the error emitted when the frame parses but is not JSON-RPC
1552
+ *
1553
+ * @example
1554
+ * ```ts
1555
+ * deliverMessage(transport.emitter, frame, 'non-JSON-RPC WebSocket frame')
1556
+ * ```
1557
+ */
1558
+ function deliverMessage(emitter, text, fault) {
1559
+ let parsed;
1560
+ try {
1561
+ parsed = JSON.parse(text);
1562
+ } catch (error) {
1563
+ emitter.emit("error", error);
1564
+ return;
1565
+ }
1566
+ const message = parseJSONRPCMessage(parsed);
1567
+ if (message === void 0) {
1568
+ emitter.emit("error", new Error(fault));
1569
+ return;
1570
+ }
1571
+ emitter.emit("message", message);
1572
+ }
1573
+ /**
1574
+ * Decodes one SSE event's `data` string into a {@link JSONRPCMessage}, or `undefined`
1575
+ * when it is not one — the per-event step {@link readEventStream} folds over.
1576
+ *
1577
+ * @remarks
1578
+ * Parses the `data` (a peer serializes the JSON-RPC envelope as the event's `data`) with
1579
+ * `@orkestrel/contract`'s `parseJSON` — the declared JSON boundary, which answers `undefined`
1580
+ * instead of throwing — and narrows the parsed value with `parseJSONRPCMessage`. Total:
1581
+ * malformed JSON or a non-message value yields `undefined`, never throws.
1582
+ *
1583
+ * @param data - One SSE event's `data` payload
1584
+ * @returns The decoded {@link JSONRPCMessage}, or `undefined`
1585
+ *
1586
+ * @example
1587
+ * ```ts
1588
+ * decodeEvent('{"jsonrpc":"2.0","id":1,"result":{}}') // the decoded response
1589
+ * ```
1590
+ */
1591
+ function decodeEvent(data) {
1592
+ return parseJSONRPCMessage((0, _orkestrel_contract.parseJSON)(data));
1593
+ }
1594
+ /**
1595
+ * Decodes a `fetch` Response's Server-Sent-Events body into the JSON-RPC messages it
1596
+ * carried — the client-side inverse of a server's Streamable-HTTP SSE response.
1597
+ *
1598
+ * @remarks
1599
+ * Reads the whole `response.body` stream chunk-by-chunk through a `TextDecoder({ stream: true
1600
+ * })` (handling a multi-byte character split across reads) and `@orkestrel/sse`'s
1601
+ * {@link SSEParserInterface} (handling a partial line or in-progress event split across
1602
+ * reads), then narrows each dispatched event's `data` to a {@link JSONRPCMessage} through
1603
+ * {@link decodeEvent} (so a non-message or non-JSON `data:` event is dropped, never thrown —
1604
+ * total). It reuses the same `SSEParser` a server's `createStream` seam serializes against, so
1605
+ * the wire round-trips. A `null` body (no stream) yields no messages;
1606
+ * {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport} reads a
1607
+ * request/response SSE reply (the server sends one `data:` event then ends), so this drains to
1608
+ * completion.
1609
+ *
1610
+ * @param response - The SSE `fetch` Response to decode (its `body` is read to completion)
1611
+ * @returns Every {@link JSONRPCMessage} the stream carried, in order
1612
+ *
1613
+ * @example
1614
+ * ```ts
1615
+ * const messages = await readEventStream(await fetch(url, { method: 'POST', body }))
1616
+ * ```
1617
+ */
1618
+ async function readEventStream(response) {
1619
+ const body = response.body;
1620
+ if (body === null) return [];
1621
+ const reader = body.getReader();
1622
+ const decoder = new TextDecoder();
1623
+ const parser = (0, _orkestrel_sse.createSSEParser)();
1624
+ const messages = [];
1625
+ try {
1626
+ for (;;) {
1627
+ const { done, value } = await reader.read();
1628
+ if (done) break;
1629
+ for (const event of parser.parse(decoder.decode(value, { stream: true }))) {
1630
+ const message = decodeEvent(event.data);
1631
+ if (message !== void 0) messages.push(message);
1632
+ }
1633
+ }
1634
+ } finally {
1635
+ reader.releaseLock();
1636
+ }
1637
+ return messages;
1638
+ }
1639
+ /**
1640
+ * Builds the error for a non-success HTTP response that carried no JSON-RPC message.
1641
+ *
1642
+ * @param response - The response whose status is reported
1643
+ * @param type - The response's content type, or an empty string when absent
1644
+ * @returns An error naming the HTTP status and unsupported response shape
1645
+ *
1646
+ * @example
1647
+ * ```ts
1648
+ * const error = buildResponseError(new Response('', { status: 500 }), '')
1649
+ * ```
1650
+ */
1651
+ function buildResponseError(response, type) {
1652
+ if (type.includes("application/json")) return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained an application/json body that was not a JSON-RPC message`);
1653
+ if (type.includes("text/event-stream")) return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained a text/event-stream body without a JSON-RPC message`);
1654
+ const shape = type === "" ? "a body without a content type" : `an unsupported '${type}' body`;
1655
+ return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained ${shape}`);
1466
1656
  }
1467
1657
  /**
1468
1658
  * Reads the value one standard MCP request header carries, decoding the Base64 sentinel.
@@ -1478,7 +1668,7 @@ function decodeBoundedMessage(message, limits) {
1478
1668
  * second spelling of a byte, so it is refused: `=?base64?QR==?=` reaches for the byte
1479
1669
  * `=?base64?QQ==?=` spells canonically, and only the canonical spelling decodes. A malformed
1480
1670
  * payload answers `undefined` rather than falling back to the literal, because the protocol
1481
- * requires a server to REJECT invalid characters, and a fallback would admit the very value
1671
+ * requires a server to reject invalid characters, and a fallback would admit the very value
1482
1672
  * the rule exists to refuse. A value missing either marker is a literal and comes back
1483
1673
  * unchanged.
1484
1674
  *
@@ -1524,7 +1714,7 @@ function decodeSentinel(value) {
1524
1714
  *
1525
1715
  * @remarks
1526
1716
  * The exact inverse of {@link decodeSentinel}, and its membership rule is stated as that
1527
- * inverse rather than as a second list that could drift: a value travels LITERALLY when it is
1717
+ * inverse rather than as a second list that could drift: a value travels literally when it is
1528
1718
  * plain printable ASCII — every code point in `U+0020`–`U+007E`, the RFC 9110 field-value
1529
1719
  * range this package admits — and {@link decodeSentinel} gives it back unchanged. Every other
1530
1720
  * value travels wrapped in {@link MCP_SENTINEL_PREFIX} and {@link MCP_SENTINEL_SUFFIX}, the
@@ -1564,7 +1754,7 @@ function encodeSentinel(value) {
1564
1754
  *
1565
1755
  * @remarks
1566
1756
  * The companion of {@link extractHeaderAnnotations}, which reads only the annotations a
1567
- * `properties` chain reaches. Comparing the two answers is how
1757
+ * `properties` chain reaches. Comparing the answers is how
1568
1758
  * {@link buildHeaderParameters} decides reachability without a second walk that would have
1569
1759
  * to re-state which JSON Schema keywords are traversable: an annotation the reachable walk
1570
1760
  * did not read is one sitting under `items`, a composition or conditional keyword, a `$ref`
@@ -1608,12 +1798,12 @@ function countHeaderAnnotations(value) {
1608
1798
  * Reachability is the protocol's own rule: an annotation counts only where a chain of
1609
1799
  * `properties` keys leads to it from the `inputSchema` root, so `path` is both the schema
1610
1800
  * position and the position the call's `arguments` carry the value at. A property named
1611
- * `items` is reachable like any other, because the chain is read by key POSITION rather than
1801
+ * `items` is reachable like any other, because the chain is read by key position rather than
1612
1802
  * by key name.
1613
1803
  *
1614
1804
  * `undefined` means the definition is invalid rather than empty: a reachable annotation whose
1615
1805
  * value is not an {@link import('./validators.js').isFieldToken} token, one sitting on the
1616
- * schema ROOT (which is no property), one on a leaf whose declared type is not an
1806
+ * schema root (which is no property), one on a leaf whose declared type is not an
1617
1807
  * {@link import('./validators.js').isMCPHeaderPrimitive} primitive, or a chain deeper than
1618
1808
  * `DEFAULT_MCP_LIMITS.depth` — which is also what makes a self-referential schema terminate.
1619
1809
  * A node that is not a record carries nothing and answers an empty list, because a leaf the
@@ -1657,8 +1847,8 @@ function extractHeaderAnnotations(schema, path) {
1657
1847
  *
1658
1848
  * @remarks
1659
1849
  * The single decision both sides of the protocol make about an annotated tool: an HTTP
1660
- * CLIENT excludes a definition this refuses from the `tools/list` result it delivers, and a
1661
- * SERVER recognizes exactly the `Mcp-Param-*` names this returns for its own definitions.
1850
+ * client excludes a definition this refuses from the `tools/list` result it delivers, and a
1851
+ * server recognizes exactly the `Mcp-Param-*` names this returns for its own definitions.
1662
1852
  *
1663
1853
  * `undefined` means the definition is invalid, and every rule the protocol states produces
1664
1854
  * it: a value that is not an RFC 9110 token, a non-primitive or untyped annotated leaf, a
@@ -1695,11 +1885,11 @@ function buildHeaderParameters(schema) {
1695
1885
  * Renders one projected argument as the text its `Mcp-Param-*` header carries.
1696
1886
  *
1697
1887
  * @remarks
1698
- * The protocol's conversion table, and the ONE place it is stated: a string travels as
1888
+ * The protocol's conversion table, and the one place it is stated: a string travels as
1699
1889
  * itself, an integer in decimal, and a boolean as lowercase `true` or `false`. The value's
1700
1890
  * runtime shape must match the leaf's declared type, so a schema that declares `integer` and
1701
1891
  * an argument that supplies a string, a fraction, or a magnitude outside the IEEE 754 safe
1702
- * range carries NOTHING — a header that cannot round-trip the body value is worse than an
1892
+ * range carries nothing — a header that cannot round-trip the body value is worse than an
1703
1893
  * absent one, and the tool's own argument validation owns the disagreement.
1704
1894
  *
1705
1895
  * @param value - The argument value read at the parameter's path
@@ -1722,7 +1912,7 @@ function renderHeaderValue(value, primitive) {
1722
1912
  *
1723
1913
  * @remarks
1724
1914
  * The projection SEP-2243 requires of an HTTP client, and the same derivation a server runs
1725
- * to know what the request should have carried. Each parameter's value is read at its exact
1915
+ * to know what the request must carry. Each parameter's value is read at its exact
1726
1916
  * property path in the call's own `arguments`; an absent or `null` value omits its header
1727
1917
  * entirely, which is the protocol's distinction between "not supplied" and "supplied empty".
1728
1918
  * The rendered text then travels through {@link encodeSentinel}, so a value carrying
@@ -1784,7 +1974,7 @@ function extractToolSchema(response, name) {
1784
1974
  * {@link buildCancelledNotification}.
1785
1975
  *
1786
1976
  * @remarks
1787
- * `requestId` is the WIRE SPELLING carried verbatim from the dated schema, and it must be a
1977
+ * `requestId` is the wire spelling carried verbatim from the dated schema, and it must be a
1788
1978
  * real {@link JSONRPCId}: `null` is not one, and neither is an absent member, so a
1789
1979
  * malformed frame reads as "cancels nothing" rather than as an error. Anything that is not a
1790
1980
  * `notifications/cancelled` notification — a response, a request that happens to use the
@@ -1805,14 +1995,14 @@ function readCancelledId(message) {
1805
1995
  }
1806
1996
  /**
1807
1997
  * Pumps a controlled serialized exchange onto a transport — every notification in order, then
1808
- * the terminating response — and END the exchange however the pump leaves.
1998
+ * the terminating response — and end the exchange however the pump leaves.
1809
1999
  *
1810
2000
  * @remarks
1811
2001
  * The generator's `return` value is a message like any other on the wire: it is sent
1812
- * LAST and closes the exchange. Sends are awaited one at a time so the transport
2002
+ * last and closes the exchange. Sends are awaited one at a time so the transport
1813
2003
  * receives the sequence in the order the method produced it.
1814
2004
  *
1815
- * The first parameter is the CONTROLLED arm rather than a bare
2005
+ * The first parameter is the controlled arm rather than a bare
1816
2006
  * {@link import('./types.js').MCPTextStream}, and that is the whole point of it: this pump is
1817
2007
  * an owner, and an owner needs a lifecycle member to discharge its obligation with. A bare
1818
2008
  * generator has none, so an exit where nothing was cancelled — a `send` that threw two
@@ -1822,7 +2012,7 @@ function readCancelledId(message) {
1822
2012
  * is a no-op for an exchange that already ended on its terminal.
1823
2013
  *
1824
2014
  * The `finally` is spelled explicitly rather than with `await using` because this package's
1825
- * declared Node floor cannot PARSE `await using` — `target: ESNext` emits the declaration
2015
+ * declared Node floor cannot parse `await using` — `target: ESNext` emits the declaration
1826
2016
  * verbatim, and a floor engine rejects the whole module at load. The obligation discharged is
1827
2017
  * identical either way.
1828
2018
  *
@@ -1857,36 +2047,36 @@ async function sendStream(stream, transport) {
1857
2047
  * `server.handle` already turns a malformed message into a serialized `-32700` /
1858
2048
  * `-32600` reply and a notification into `undefined` (no reply), so this binder parses
1859
2049
  * nothing the server would parse differently: it decodes each inbound message through
1860
- * {@link decodeBoundedMessage} under `server.limit`, the SERVER'S OWN bound, so a message
2050
+ * {@link decodeBoundedMessage} under `server.limit`, the server's own bound, so a message
1861
2051
  * the server would refuse is never parsed here either and still receives its `-32700` from
1862
- * the one place that words it. A HELD-OPEN reply arrives as an
2052
+ * the one place that words it. A held-open reply arrives as an
1863
2053
  * {@link import('./types.js').MCPTextStreamControllerInterface} instead of a string: this is
1864
2054
  * the one place that pumps it, writing each notification in order and then the generator's
1865
2055
  * returned terminating response ({@link sendStream}). A `transport.send` throw or rejection —
1866
2056
  * mid-stream included — is caught and routed
1867
2057
  * to `server.emitter`'s `error` event (never rethrown, never an unhandled rejection);
1868
2058
  * a listener on that event that itself throws is swallowed (the end of the line —
1869
- * the caller's own bug, never this binder's). A fault raised AFTER its own request was
2059
+ * the caller's own bug, never this binder's). A fault raised after its own request was
1870
2060
  * cancelled reports nothing, because a cancellation is not a fault.
1871
2061
  *
1872
- * **This binder OWNS every exchange it starts, and ends each one on every exit.** It holds one
2062
+ * **This binder owns every exchange it starts, and ends each one on every exit.** It holds one
1873
2063
  * `AbortController` per live request, keyed by the request's id and deleted whenever that
1874
2064
  * request leaves — normally, by a throw, or by cancellation — and it supplies that signal to
1875
2065
  * `handle` as {@link import('./types.js').MCPDispatchOptions}. These consequences follow.
1876
- * An inbound `notifications/cancelled` ABORTS the request it names, which is how the message-
2066
+ * An inbound `notifications/cancelled` aborts the request it names, which is how the message-
1877
2067
  * based cancellation path reaches a tool on the carriers that have one (stdio, WebSocket,
1878
- * `MessagePort`); a cancelled request writes NO response, because a peer that asked for a call
2068
+ * `MessagePort`); a cancelled request writes no response, because a peer that asked for a call
1879
2069
  * to stop is not answered by it; and the transport's `closed` signal aborts every request
1880
2070
  * still in flight, so an exchange being pumped when the carrier dies ends with it instead of
1881
2071
  * writing into a socket nobody is holding.
1882
2072
  *
1883
- * `listen`/`closed` are REPLACE semantics (§ port contract): the returned unbind
1884
- * DETACHES by replacing this binder's own handlers with no-ops, so a subsequent
1885
- * `bindServer` call on the SAME transport is never double-dispatched by a stale
2073
+ * `listen`/`closed` are replace semantics (§ port contract): the returned unbind
2074
+ * detaches by replacing this binder's own handlers with no-ops, so a subsequent
2075
+ * `bindServer` call on the same transport is never double-dispatched by a stale
1886
2076
  * subscription left behind — an unbind→rebind cycle yields exactly one reply per
1887
2077
  * request. Unbinding is itself an owner exit: it aborts and retires every request still in
1888
2078
  * flight before detaching, so `unbind()` then `close()` and `close()` then `unbind()` end the
1889
- * same exchanges. It does NOT close the transport; that remains the caller's decision.
2079
+ * same exchanges. It does not close the transport; that remains the caller's decision.
1890
2080
  *
1891
2081
  * @param server - The transport-agnostic server to dispatch inbound messages over
1892
2082
  * @param transport - The duplex channel to pipe the server over
@@ -1946,38 +2136,38 @@ function bindServer(server, transport) {
1946
2136
  }
1947
2137
  /**
1948
2138
  * Pipes an {@link MCPTransportInterface} into an {@link MCPClientInterface} — every
1949
- * inbound message is decoded and delivered onto the client's OWN transport
2139
+ * inbound message is decoded and delivered onto the client's own transport
1950
2140
  * (`client.transport.emitter`'s `message` / `close` events), resolving/rejecting the
1951
2141
  * client's correlated pending requests exactly as a direct reply would.
1952
2142
  *
1953
2143
  * @remarks
1954
2144
  * The client's outbound writes flow through `client.transport.send` — its existing,
1955
2145
  * unmodified request/response correlation — so `client` must have been constructed
1956
- * with a {@link import('./types.js').MCPClientTransportInterface} that itself carries
1957
- * the SAME `transport` (see {@link import('./factories.js').createDuplexClientTransport},
2146
+ * with a {@link import('./types.js').MCPMessageTransportInterface} that itself carries
2147
+ * the same `transport` (see {@link import('./factories.js').createDuplexClientTransport},
1958
2148
  * the additive factory that adapts an {@link MCPTransportInterface} into that shape);
1959
2149
  * this binder then completes the inbound half by decoding each message and pushing it
1960
2150
  * onto `client.transport.emitter` (an {@link import('@orkestrel/emitter').EmitterInterface}
1961
2151
  * exposes `emit`, so no client modification is needed). A malformed / non-JSON-RPC
1962
- * inbound message is DROPPED (total — never throws); a delivery fault is routed to
2152
+ * inbound message is dropped (total — never throws); a delivery fault is routed to
1963
2153
  * `client.transport.emitter`'s `error` event (never rethrown). The returned unbind
1964
- * DETACHES this binder (further inbound messages and the transport's `closed` signal are
1965
- * ignored) WITHOUT closing the transport.
2154
+ * detaches this binder (further inbound messages and the transport's `closed` signal are
2155
+ * ignored) without closing the transport.
1966
2156
  *
1967
- * `listen`/`closed` are REPLACE semantics (§ port contract): the returned unbind
1968
- * DETACHES by replacing this binder's own handlers with no-ops, so a subsequent
1969
- * `bindClient` call on the SAME transport is never double-dispatched by a stale
2157
+ * `listen`/`closed` are replace semantics (§ port contract): the returned unbind
2158
+ * detaches by replacing this binder's own handlers with no-ops, so a subsequent
2159
+ * `bindClient` call on the same transport is never double-dispatched by a stale
1970
2160
  * subscription left behind — an unbind→rebind cycle delivers exactly one `message`
1971
2161
  * emit per inbound reply.
1972
2162
  *
1973
2163
  * **This binder needs no live-request registry, and the asymmetry with {@link bindServer} is
1974
- * real rather than an omission.** A server binder holds the lifetime of work it STARTED, so an
2164
+ * real rather than an omission.** A server binder holds the lifetime of work it started, so an
1975
2165
  * inbound `notifications/cancelled` has something to reach; a client binder starts no work —
1976
2166
  * `MCPClient` already owns its pending entries and already writes the cancellation frame
1977
2167
  * itself when a caller's `signal` aborts, on a carrier declaring `duplex`. Adding a registry
1978
- * here would be a second correlation table for ids the client is already correlating, and two
1979
- * tables for one fact drift. The one obligation this binder does carry is delivery: a
1980
- * malformed / non-JSON-RPC inbound message is DROPPED (total — never throws).
2168
+ * here would be a second correlation table for ids the client is already correlating, and a
2169
+ * pair of tables for one fact drift. The one obligation this binder does carry is delivery: a
2170
+ * malformed / non-JSON-RPC inbound message is dropped (total — never throws).
1981
2171
  *
1982
2172
  * @param client - The transport-agnostic client whose transport to deliver messages onto
1983
2173
  * @param transport - The duplex channel to pipe the client over
@@ -2061,12 +2251,12 @@ function isMCPResultMetaObject(value) {
2061
2251
  * subscription id.
2062
2252
  *
2063
2253
  * @remarks
2064
- * The reserved key is OPTIONAL, so a frame delivered outside a `subscriptions/listen`
2065
- * stream passes with no stamp at all. When the key IS present its value must be a valid
2254
+ * The reserved key is optional, so a frame delivered outside a `subscriptions/listen`
2255
+ * stream passes with no stamp at all. When the key is present its value must be a valid
2066
2256
  * {@link JSONRPCId}, because a stamp naming nothing addressable is worse than no stamp.
2067
2257
  *
2068
2258
  * @param value - The unknown value to inspect
2069
- * @returns `true` when the value is exact metadata whose subscription stamp, if present, is valid
2259
+ * @returns True if the value is exact metadata whose subscription stamp, if present, is valid; false otherwise
2070
2260
  *
2071
2261
  * @example
2072
2262
  * ```ts
@@ -2089,7 +2279,7 @@ function isMCPLoggingLevel(value) {
2089
2279
  * Determines whether a value is standard padded base64 as required by JSON Schema `byte` format.
2090
2280
  *
2091
2281
  * @param value - The unknown value to inspect
2092
- * @returns Whether the value is an empty or completely padded standard base64 encoding
2282
+ * @returns True if the value is an empty or completely padded standard base64 encoding; false otherwise
2093
2283
  */
2094
2284
  function isStandardBase64(value) {
2095
2285
  return (0, _orkestrel_contract.isString)(value) && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
@@ -2105,7 +2295,7 @@ function isStandardBase64(value) {
2105
2295
  * {@link MCP_PARAM_PREFIX} and must survive as an HTTP field name.
2106
2296
  *
2107
2297
  * @param value - The unknown value to inspect
2108
- * @returns Whether the value is a non-empty RFC 9110 token
2298
+ * @returns True if the value is a non-empty RFC 9110 token; false otherwise
2109
2299
  *
2110
2300
  * @example
2111
2301
  * ```ts
@@ -2125,7 +2315,7 @@ function isFieldToken(value) {
2125
2315
  * exactly, and the server compares it numerically.
2126
2316
  *
2127
2317
  * @param value - The unknown value to inspect
2128
- * @returns Whether the value is one of `'string'`, `'integer'`, or `'boolean'`
2318
+ * @returns True if the value is one of `'string'`, `'integer'`, or `'boolean'`; false otherwise
2129
2319
  *
2130
2320
  * @example
2131
2321
  * ```ts
@@ -2144,7 +2334,7 @@ function isMCPHeaderPrimitive(value) {
2144
2334
  * scheme allowlist. Component scanning is bounded by the input length.
2145
2335
  *
2146
2336
  * @param value - The unknown value to inspect
2147
- * @returns Whether the value is an RFC 3986 URI rather than a relative reference
2337
+ * @returns True if the value is an RFC 3986 URI rather than a relative reference; false otherwise
2148
2338
  */
2149
2339
  function isAbsoluteURI(value) {
2150
2340
  if (!(0, _orkestrel_contract.isString)(value) || value.length === 0) return false;
@@ -2229,7 +2419,7 @@ function isAbsoluteURI(value) {
2229
2419
  * Determines whether a value is one RFC 3339 `full-date` naming a real calendar day.
2230
2420
  *
2231
2421
  * @remarks
2232
- * RFC 3339 §5.6 defines `date-mday` as `01-28`, `29`, `30`, or `31` BASED ON the month and
2422
+ * RFC 3339 §5.6 defines `date-mday` as `01-28`, `29`, `30`, or `31` based on the month and
2233
2423
  * year, so the grammar is not satisfied by shape alone: `2026-02-30` and `2025-02-29` are
2234
2424
  * well-formed triples that name no day, and a downstream `new Date` rolls each of them
2235
2425
  * silently onto a different date rather than refusing it. February's length follows the
@@ -2237,10 +2427,10 @@ function isAbsoluteURI(value) {
2237
2427
  *
2238
2428
  * The check is pure integer arithmetic on the matched fields and never constructs a `Date`,
2239
2429
  * because `Date` is exactly the component that performs the rollover this guard exists to
2240
- * refuse. It is a SYNTAX guard: no time zone, locale, calendar era, or leap second applies.
2430
+ * refuse. It is a syntax guard: no time zone, locale, calendar era, or leap second applies.
2241
2431
  *
2242
2432
  * @param value - The unknown value to inspect
2243
- * @returns Whether the value is an RFC 3339 `full-date` for a day that exists
2433
+ * @returns True if the value is an RFC 3339 `full-date` for a day that exists; false otherwise
2244
2434
  *
2245
2435
  * @example
2246
2436
  * ```ts
@@ -2273,7 +2463,7 @@ function isRFC3339Date(value) {
2273
2463
  * second.
2274
2464
  *
2275
2465
  * @param value - The unknown value to inspect
2276
- * @returns Whether the value is an RFC 3339 `date-time` for a day that exists
2466
+ * @returns True if the value is an RFC 3339 `date-time` for a day that exists; false otherwise
2277
2467
  *
2278
2468
  * @example
2279
2469
  * ```ts
@@ -2291,7 +2481,7 @@ function isRFC3339DateTime(value) {
2291
2481
  * Determines whether a value is one exact finite MCP progress payload.
2292
2482
  *
2293
2483
  * @param value - The unknown value to inspect
2294
- * @returns Whether required progress and optional total/message fields match the dated schema
2484
+ * @returns True if required progress and optional total/message fields match the dated schema; false otherwise
2295
2485
  */
2296
2486
  function isMCPProgress(value) {
2297
2487
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2310,7 +2500,7 @@ function isMCPProgress(value) {
2310
2500
  * Determines whether a value carries valid dated-schema MCP content annotations.
2311
2501
  *
2312
2502
  * @param value - The unknown value to inspect
2313
- * @returns Whether the value is valid MCP annotations
2503
+ * @returns True if the value is valid MCP annotations; false otherwise
2314
2504
  */
2315
2505
  function isMCPAnnotations(value) {
2316
2506
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2331,7 +2521,7 @@ function isMCPAnnotations(value) {
2331
2521
  * Determines whether a value is one exact dated-schema MCP icon.
2332
2522
  *
2333
2523
  * @param value - The unknown value to inspect
2334
- * @returns Whether the value is a valid MCP icon
2524
+ * @returns True if the value is a valid MCP icon; false otherwise
2335
2525
  */
2336
2526
  function isMCPIcon(value) {
2337
2527
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2422,7 +2612,7 @@ function isMCPServerCapabilities(value) {
2422
2612
  * Determines whether a value is embedded textual MCP resource contents.
2423
2613
  *
2424
2614
  * @param value - The unknown value to inspect
2425
- * @returns Whether the value is embedded textual resource contents
2615
+ * @returns True if the value is embedded textual resource contents; false otherwise
2426
2616
  */
2427
2617
  function isMCPTextResource(value) {
2428
2618
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2441,7 +2631,7 @@ function isMCPTextResource(value) {
2441
2631
  * Determines whether a value is embedded blob MCP resource contents.
2442
2632
  *
2443
2633
  * @param value - The unknown value to inspect
2444
- * @returns Whether the value is embedded blob resource contents
2634
+ * @returns True if the value is embedded blob resource contents; false otherwise
2445
2635
  */
2446
2636
  function isMCPBlobResource(value) {
2447
2637
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2460,7 +2650,7 @@ function isMCPBlobResource(value) {
2460
2650
  * Determines whether a value is one `resources/list` descriptor.
2461
2651
  *
2462
2652
  * @param value - The unknown value to inspect
2463
- * @returns Whether the value is a valid resource descriptor
2653
+ * @returns True if the value is a valid resource descriptor; false otherwise
2464
2654
  */
2465
2655
  function isMCPResource(value) {
2466
2656
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2482,7 +2672,7 @@ function isMCPResource(value) {
2482
2672
  * level belong to the consumer-supplied resource manager; this package projects the string.
2483
2673
  *
2484
2674
  * @param value - The unknown value to inspect
2485
- * @returns Whether the value is a valid resource-template descriptor
2675
+ * @returns True if the value is a valid resource-template descriptor; false otherwise
2486
2676
  */
2487
2677
  function isMCPResourceTemplate(value) {
2488
2678
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2499,7 +2689,7 @@ function isMCPResourceTemplate(value) {
2499
2689
  * Determines whether a value is structurally discriminated resource contents.
2500
2690
  *
2501
2691
  * @param value - The unknown value to inspect
2502
- * @returns Whether exactly one of `text` and `blob` is present and valid
2692
+ * @returns True if exactly one of `text` and `blob` is present and valid; false otherwise
2503
2693
  */
2504
2694
  function isMCPResourceContents(value) {
2505
2695
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2513,7 +2703,7 @@ function isMCPResourceContents(value) {
2513
2703
  * Determines whether a value carries the shared optional pagination cursor.
2514
2704
  *
2515
2705
  * @param value - The unknown value to inspect
2516
- * @returns Whether a present `cursor` is a string
2706
+ * @returns True if a present `cursor` is a string; false otherwise
2517
2707
  */
2518
2708
  function isMCPPaginationParams(value) {
2519
2709
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2523,7 +2713,7 @@ function isMCPPaginationParams(value) {
2523
2713
  * Determines whether a value is one consumer-owned resource page.
2524
2714
  *
2525
2715
  * @param value - The unknown value to inspect
2526
- * @returns Whether the resources and optional following cursor are valid
2716
+ * @returns True if the resources and optional following cursor are valid; false otherwise
2527
2717
  */
2528
2718
  function isMCPResourcePage(value) {
2529
2719
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2536,7 +2726,7 @@ function isMCPResourcePage(value) {
2536
2726
  * Determines whether a value is one consumer-owned resource-template page.
2537
2727
  *
2538
2728
  * @param value - The unknown value to inspect
2539
- * @returns Whether the templates and optional following cursor are valid
2729
+ * @returns True if the templates and optional following cursor are valid; false otherwise
2540
2730
  */
2541
2731
  function isMCPResourceTemplatePage(value) {
2542
2732
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2549,7 +2739,7 @@ function isMCPResourceTemplatePage(value) {
2549
2739
  * Determines whether a value is a string-valued MCP argument record.
2550
2740
  *
2551
2741
  * @param value - The unknown value to inspect
2552
- * @returns Whether every own argument value is a string
2742
+ * @returns True if every own argument value is a string; false otherwise
2553
2743
  */
2554
2744
  function isMCPStringArguments(value) {
2555
2745
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2559,7 +2749,7 @@ function isMCPStringArguments(value) {
2559
2749
  * Determines whether a value is one prompt argument descriptor.
2560
2750
  *
2561
2751
  * @param value - The unknown value to inspect
2562
- * @returns Whether the prompt argument descriptor is valid
2752
+ * @returns True if the prompt argument descriptor is valid; false otherwise
2563
2753
  */
2564
2754
  function isMCPPromptArgument(value) {
2565
2755
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2571,7 +2761,7 @@ function isMCPPromptArgument(value) {
2571
2761
  * Determines whether a value is one `prompts/list` descriptor.
2572
2762
  *
2573
2763
  * @param value - The unknown value to inspect
2574
- * @returns Whether the prompt descriptor is valid
2764
+ * @returns True if the prompt descriptor is valid; false otherwise
2575
2765
  */
2576
2766
  function isMCPPrompt(value) {
2577
2767
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2585,7 +2775,7 @@ function isMCPPrompt(value) {
2585
2775
  * Determines whether a value is one prompt message with existing rich content.
2586
2776
  *
2587
2777
  * @param value - The unknown value to inspect
2588
- * @returns Whether the role and content are valid
2778
+ * @returns True if the role and content are valid; false otherwise
2589
2779
  */
2590
2780
  function isMCPPromptMessage(value) {
2591
2781
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2595,7 +2785,7 @@ function isMCPPromptMessage(value) {
2595
2785
  * Determines whether a value is one consumer-owned prompt page.
2596
2786
  *
2597
2787
  * @param value - The unknown value to inspect
2598
- * @returns Whether the prompts and optional following cursor are valid
2788
+ * @returns True if the prompts and optional following cursor are valid; false otherwise
2599
2789
  */
2600
2790
  function isMCPPromptPage(value) {
2601
2791
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2608,7 +2798,7 @@ function isMCPPromptPage(value) {
2608
2798
  * Determines whether a value is one complete `prompts/get` result.
2609
2799
  *
2610
2800
  * @param value - The unknown value to inspect
2611
- * @returns Whether the prompt result and all messages are valid
2801
+ * @returns True if the prompt result and all messages are valid; false otherwise
2612
2802
  */
2613
2803
  function isMCPPromptGetResult(value) {
2614
2804
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2621,7 +2811,7 @@ function isMCPPromptGetResult(value) {
2621
2811
  * Determines whether a value is a prompt or resource-template completion reference.
2622
2812
  *
2623
2813
  * @param value - The unknown value to inspect
2624
- * @returns Whether the discriminated reference is valid
2814
+ * @returns True if the discriminated reference is valid; false otherwise
2625
2815
  */
2626
2816
  function isMCPCompletionReference(value) {
2627
2817
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2633,7 +2823,7 @@ function isMCPCompletionReference(value) {
2633
2823
  * Determines whether a value is one `completion/complete` parameter object.
2634
2824
  *
2635
2825
  * @param value - The unknown value to inspect
2636
- * @returns Whether its reference, fragment, and optional string context are valid
2826
+ * @returns True if its reference, fragment, and optional string context are valid; false otherwise
2637
2827
  */
2638
2828
  function isMCPCompletionParams(value) {
2639
2829
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2652,7 +2842,7 @@ function isMCPCompletionParams(value) {
2652
2842
  * Determines whether a value is one host-produced completion candidate set.
2653
2843
  *
2654
2844
  * @param value - The unknown value to inspect
2655
- * @returns Whether its candidates and optional result facts are valid
2845
+ * @returns True if its candidates and optional result facts are valid; false otherwise
2656
2846
  */
2657
2847
  function isMCPCompletion(value) {
2658
2848
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2666,7 +2856,7 @@ function isMCPCompletion(value) {
2666
2856
  * Determines whether a value is one complete, capped `completion/complete` result.
2667
2857
  *
2668
2858
  * @param value - The unknown value to inspect
2669
- * @returns Whether the result is complete and carries at most 100 candidates
2859
+ * @returns True if the result is complete and carries at most 100 candidates; false otherwise
2670
2860
  */
2671
2861
  function isMCPCompletionResult(value) {
2672
2862
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2678,7 +2868,7 @@ function isMCPCompletionResult(value) {
2678
2868
  * Determines whether a value is one exact dated-schema MCP tool content block.
2679
2869
  *
2680
2870
  * @param value - The unknown value to inspect
2681
- * @returns Whether the value is valid MCP content
2871
+ * @returns True if the value is valid MCP content; false otherwise
2682
2872
  */
2683
2873
  function isMCPContent(value) {
2684
2874
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2711,7 +2901,7 @@ function isMCPContent(value) {
2711
2901
  *
2712
2902
  * @remarks
2713
2903
  * The open contract's guard: a record carrying a string `resultType` and, when
2714
- * present, exact result metadata. It deliberately does NOT narrow `resultType` to a
2904
+ * present, exact result metadata. It deliberately does not narrow `resultType` to a
2715
2905
  * known value, because the dated schema keeps adding them — a caller that needs a
2716
2906
  * specific result uses that result's own guard, which narrows to its literal.
2717
2907
  * Mutually exclusive with {@link isMCPLegacyResult} on every input: this one needs
@@ -2719,7 +2909,7 @@ function isMCPContent(value) {
2719
2909
  * input.
2720
2910
  *
2721
2911
  * @param value - The unknown value to inspect
2722
- * @returns Whether the value is a modern result
2912
+ * @returns True if the value is a modern result; false otherwise
2723
2913
  *
2724
2914
  * @example
2725
2915
  * ```ts
@@ -2745,7 +2935,7 @@ function isMCPResult(value) {
2745
2935
  * hostile input.
2746
2936
  *
2747
2937
  * @param value - The unknown value to inspect
2748
- * @returns Whether the value is a legacy result
2938
+ * @returns True if the value is a legacy result; false otherwise
2749
2939
  *
2750
2940
  * @example
2751
2941
  * ```ts
@@ -2761,7 +2951,7 @@ function isMCPLegacyResult(value) {
2761
2951
  * Determines whether a value is a complete modern MCP tool result.
2762
2952
  *
2763
2953
  * @param value - The unknown value to inspect
2764
- * @returns Whether the value is a complete MCP call result
2954
+ * @returns True if the value is a complete MCP call result; false otherwise
2765
2955
  */
2766
2956
  function isMCPCallResult(value) {
2767
2957
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -2786,10 +2976,10 @@ function isMCPCallResult(value) {
2786
2976
  * proof: this is what stands between a manager that answers a numeric `taskId` and a
2787
2977
  * client that would receive one. `ttlMs` accepts `null` because the schema uses it to
2788
2978
  * mean "no expiry", which is distinct from an absent field, and both durations must be
2789
- * INTEGER milliseconds because the schema formats them `int`.
2979
+ * integer milliseconds because the schema formats them `int`.
2790
2980
  *
2791
2981
  * @param value - The unknown value to inspect
2792
- * @returns Whether the value is a well-formed `resultType: 'task'` result
2982
+ * @returns True if the value is a well-formed `resultType: 'task'` result; false otherwise
2793
2983
  *
2794
2984
  * @example
2795
2985
  * ```ts
@@ -2816,7 +3006,7 @@ function isMCPTaskResult(value) {
2816
3006
  * Determines whether a value is one of the extension's task lifecycle states.
2817
3007
  *
2818
3008
  * @param value - The unknown value to inspect
2819
- * @returns Whether the value is an {@link MCPTaskStatus}
3009
+ * @returns True if the value is an {@link MCPTaskStatus}; false otherwise
2820
3010
  *
2821
3011
  * @example
2822
3012
  * ```ts
@@ -2837,7 +3027,7 @@ function isMCPTaskStatus(value) {
2837
3027
  * the requests to answer, `completed` owns the deferred call's result, `failed` owns the
2838
3028
  * JSON-RPC error that ended it, and `working` / `cancelled` own nothing further.
2839
3029
  *
2840
- * A `completed` task's `result` is checked as an OBJECT and no further. The schema declares
3030
+ * A `completed` task's `result` is checked as an object and no further. The schema declares
2841
3031
  * it an open record, so its contents belong to whichever method was deferred; a guard that
2842
3032
  * demanded a protocol result here would refuse payloads the extension permits.
2843
3033
  * `ttlMs` and `pollIntervalMs` are integer milliseconds, per the schema's `int` formats.
@@ -2847,7 +3037,7 @@ function isMCPTaskStatus(value) {
2847
3037
  * What is checked is what this package publishes as the contract.
2848
3038
  *
2849
3039
  * @param value - The unknown value to inspect
2850
- * @returns Whether the value is a well-formed {@link MCPTaskDetail}
3040
+ * @returns True if the value is a well-formed {@link MCPTaskDetail}; false otherwise
2851
3041
  *
2852
3042
  * @example
2853
3043
  * ```ts
@@ -2879,18 +3069,18 @@ function isMCPTaskDetail(value) {
2879
3069
  * Determines whether a value is the wire answer to `tasks/get`.
2880
3070
  *
2881
3071
  * @remarks
2882
- * {@link isMCPTaskDetail} plus the stamp the METHOD owes. The schema types a `tasks/get`
3072
+ * {@link isMCPTaskDetail} plus the stamp the method owes. The schema types a `tasks/get`
2883
3073
  * reply as the detail intersected with the standard result, so `resultType: 'complete'` is
2884
3074
  * part of the answer rather than decoration on it — and an unstamped payload, or one
2885
3075
  * carrying the creation answer's `resultType: 'task'`, is a peer answering some other
2886
- * shape. Use this guard wherever a `tasks/get` REPLY is read; use
3076
+ * shape. Use this guard wherever a `tasks/get` reply is read; use
2887
3077
  * {@link isMCPTaskDetail} wherever a consumer's manager answers directly.
2888
3078
  *
2889
3079
  * `_meta` is checked only when present, and only as result metadata: the server identity a
2890
3080
  * peer stamps there is the peer's to write.
2891
3081
  *
2892
3082
  * @param value - The unknown value to inspect
2893
- * @returns Whether the value is a well-formed {@link MCPTaskDetailResult}
3083
+ * @returns True if the value is a well-formed {@link MCPTaskDetailResult}; false otherwise
2894
3084
  *
2895
3085
  * @example
2896
3086
  * ```ts
@@ -2913,18 +3103,18 @@ function isMCPTaskDetailResult(value) {
2913
3103
  * Determines whether a value is a `notifications/tasks` frame carrying a task snapshot.
2914
3104
  *
2915
3105
  * @remarks
2916
- * The ADMISSION guard for a task transition: a subscription producer is consumer-written,
3106
+ * The admission guard for a task transition: a subscription producer is consumer-written,
2917
3107
  * so the frame it hands over is foreign input, and this is what stands between a mutated
2918
3108
  * or half-built snapshot and a subscribed client. Both halves are checked — the method
2919
3109
  * literal the extension fixes, and params that hold together as an
2920
3110
  * {@link MCPTaskDetail} — because either alone admits a frame the other rejects.
2921
3111
  *
2922
- * `_meta` is checked for SHAPE WHEN PRESENT and nothing more. The reserved subscription
2923
- * stamp is the SERVER'S to write, after this guard admits the frame and the matcher agrees
3112
+ * `_meta` is checked for shape when present and nothing more. The reserved subscription
3113
+ * stamp is the server's to write, after this guard admits the frame and the matcher agrees
2924
3114
  * to it, so a guard that demanded the stamp would refuse every frame a producer emits.
2925
3115
  *
2926
3116
  * @param value - The unknown value to inspect
2927
- * @returns Whether the value is a well-formed `notifications/tasks` notification
3117
+ * @returns True if the value is a well-formed `notifications/tasks` notification; false otherwise
2928
3118
  *
2929
3119
  * @example
2930
3120
  * ```ts
@@ -2952,7 +3142,7 @@ function isMCPTaskNotification(value) {
2952
3142
  *
2953
3143
  * @param value - The unknown value to inspect
2954
3144
  * @param bytes - The maximum accepted encoded bytes
2955
- * @returns `true` only for a string whose UTF-8 representation fits the bound
3145
+ * @returns True if `value` is a string whose UTF-8 representation fits the bound; false otherwise
2956
3146
  *
2957
3147
  * @example
2958
3148
  * ```ts
@@ -2988,7 +3178,7 @@ function isBoundedString(value, bytes) {
2988
3178
  *
2989
3179
  * @param value - The unknown value to inspect
2990
3180
  * @param limits - Serialized byte, optional key, and nesting-depth bounds
2991
- * @returns `true` only for safe JSON satisfying every bound
3181
+ * @returns True if `value` is safe JSON satisfying every bound; false otherwise
2992
3182
  *
2993
3183
  * @example
2994
3184
  * ```ts
@@ -3010,7 +3200,7 @@ function isBoundedJSON(value, limits) {
3010
3200
  * no minimum length. Total: any other input returns `false`.
3011
3201
  *
3012
3202
  * @param value - The already-parsed value to test
3013
- * @returns `true` when `value` is a string or a finite integer
3203
+ * @returns True if `value` is a string or a finite integer; false otherwise
3014
3204
  *
3015
3205
  * @example
3016
3206
  * ```ts
@@ -3028,7 +3218,7 @@ function isJSONRPCId(value) {
3028
3218
  * Determines whether a value is a supported {@link MCPVersion}.
3029
3219
  *
3030
3220
  * @param value - The unknown value to inspect
3031
- * @returns `true` when the value is one of {@link SUPPORTED_MCP_VERSIONS}
3221
+ * @returns True if the value is one of {@link SUPPORTED_MCP_VERSIONS}; false otherwise
3032
3222
  */
3033
3223
  function isMCPVersion(value) {
3034
3224
  return (0, _orkestrel_contract.isString)(value) && SUPPORTED_MCP_VERSIONS.some((version) => version === value);
@@ -3037,7 +3227,7 @@ function isMCPVersion(value) {
3037
3227
  * Determines whether a value is a modern protocol revision accepted by a bare server.
3038
3228
  *
3039
3229
  * @param value - The unknown value to inspect
3040
- * @returns `true` when the value is one of {@link SUPPORTED_MODERN_PROTOCOL_VERSIONS}
3230
+ * @returns True if the value is one of {@link SUPPORTED_MODERN_PROTOCOL_VERSIONS}; false otherwise
3041
3231
  */
3042
3232
  function isMCPModernVersion(value) {
3043
3233
  return (0, _orkestrel_contract.isString)(value) && SUPPORTED_MODERN_PROTOCOL_VERSIONS.some((version) => version === value);
@@ -3046,7 +3236,7 @@ function isMCPModernVersion(value) {
3046
3236
  * Determines whether a value is a revision accepted by the optional legacy decorator.
3047
3237
  *
3048
3238
  * @param value - The unknown value to inspect
3049
- * @returns `true` when the value is one of {@link SUPPORTED_LEGACY_PROTOCOL_VERSIONS}
3239
+ * @returns True if the value is one of {@link SUPPORTED_LEGACY_PROTOCOL_VERSIONS}; false otherwise
3050
3240
  */
3051
3241
  function isMCPLegacyVersion(value) {
3052
3242
  return (0, _orkestrel_contract.isString)(value) && SUPPORTED_LEGACY_PROTOCOL_VERSIONS.some((version) => version === value);
@@ -3065,7 +3255,7 @@ function isMCPLegacyVersion(value) {
3065
3255
  * the caller asked for.
3066
3256
  *
3067
3257
  * @param value - The unknown value to inspect
3068
- * @returns `true` when every recognized filter field has its protocol shape
3258
+ * @returns True if every recognized filter field has its protocol shape; false otherwise
3069
3259
  */
3070
3260
  function isMCPSubscriptionFilter(value) {
3071
3261
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -3086,7 +3276,7 @@ function isMCPSubscriptionFilter(value) {
3086
3276
  * Determines whether a value is a graceful `subscriptions/listen` result.
3087
3277
  *
3088
3278
  * @param value - The unknown value to inspect
3089
- * @returns `true` when the result is complete and carries a valid subscription id
3279
+ * @returns True if the result is complete and carries a valid subscription id; false otherwise
3090
3280
  */
3091
3281
  function isMCPSubscriptionResult(value) {
3092
3282
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -3098,7 +3288,7 @@ function isMCPSubscriptionResult(value) {
3098
3288
  * Determines whether a value is one restricted primitive form-elicitation schema.
3099
3289
  *
3100
3290
  * @param value - The unknown value to inspect
3101
- * @returns `true` for a supported boolean, numeric, string, or string-array schema
3291
+ * @returns True if `value` is a supported boolean, numeric, string, or string-array schema; false otherwise
3102
3292
  *
3103
3293
  * @example
3104
3294
  * ```ts
@@ -3161,7 +3351,7 @@ function isMCPElicitFieldSchema(value) {
3161
3351
  * an unrecognized top-level annotation is data rather than a rejection.
3162
3352
  *
3163
3353
  * @param value - The unknown value to inspect
3164
- * @returns `true` when `value` is a restricted object schema of supported field schemas
3354
+ * @returns True if `value` is a restricted object schema of supported field schemas; false otherwise
3165
3355
  *
3166
3356
  * @example
3167
3357
  * ```ts
@@ -3188,7 +3378,7 @@ function isMCPElicitSchema(value) {
3188
3378
  * Determines whether a value is a form-mode elicitation parameter object.
3189
3379
  *
3190
3380
  * @param value - The unknown value to inspect
3191
- * @returns `true` when `value` has the restricted form elicitation shape
3381
+ * @returns True if `value` has the restricted form elicitation shape; false otherwise
3192
3382
  *
3193
3383
  * @example
3194
3384
  * ```ts
@@ -3214,7 +3404,7 @@ function isMCPElicitForm(value) {
3214
3404
  * Determines whether a value is a URL-mode elicitation parameter object.
3215
3405
  *
3216
3406
  * @param value - The unknown value to inspect
3217
- * @returns `true` when `value` has the URL elicitation shape
3407
+ * @returns True if `value` has the URL elicitation shape; false otherwise
3218
3408
  *
3219
3409
  * @example
3220
3410
  * ```ts
@@ -3235,7 +3425,7 @@ function isMCPElicitURL(value) {
3235
3425
  * Determines whether a value is an embedded `elicitation/create` request.
3236
3426
  *
3237
3427
  * @param value - The unknown value to inspect
3238
- * @returns `true` when `value` is a form- or URL-mode elicitation request
3428
+ * @returns True if `value` is a form- or URL-mode elicitation request; false otherwise
3239
3429
  *
3240
3430
  * @example
3241
3431
  * ```ts
@@ -3260,7 +3450,7 @@ function isMCPElicitRequest(value) {
3260
3450
  * Determines whether a value is one legal embedded multi-round-trip request.
3261
3451
  *
3262
3452
  * @param value - The unknown value to inspect
3263
- * @returns `true` for an embedded elicitation, sampling, or roots request
3453
+ * @returns True if `value` is an embedded elicitation, sampling, or roots request; false otherwise
3264
3454
  *
3265
3455
  * @example
3266
3456
  * ```ts
@@ -3284,7 +3474,7 @@ function isMCPInputRequest(value) {
3284
3474
  * Determines whether a value is a consumer-keyed map of embedded input requests.
3285
3475
  *
3286
3476
  * @param value - The unknown value to inspect
3287
- * @returns `true` when every own value is a legal {@link MCPInputRequest}
3477
+ * @returns True if every own value is a legal {@link MCPInputRequest}; false otherwise
3288
3478
  *
3289
3479
  * @example
3290
3480
  * ```ts
@@ -3304,7 +3494,7 @@ function isMCPInputRequestMap(value) {
3304
3494
  * Determines whether a value is one elicitation response.
3305
3495
  *
3306
3496
  * @param value - The unknown value to inspect
3307
- * @returns `true` when action/content have the protocol shape
3497
+ * @returns True if action/content have the protocol shape; false otherwise
3308
3498
  *
3309
3499
  * @example
3310
3500
  * ```ts
@@ -3331,13 +3521,13 @@ function isMCPElicitResult(value) {
3331
3521
  * Determines whether accepted elicitation content satisfies the exact schema that was issued.
3332
3522
  *
3333
3523
  * @remarks
3334
- * {@link isMCPElicitResult} says a response has the SHAPE of a response; this says the
3335
- * response answers the QUESTION that was asked. A server that protects the schema it issued
3524
+ * {@link isMCPElicitResult} says a response has the shape of a response; this says the
3525
+ * response answers the question that was asked. A server that protects the schema it issued
3336
3526
  * and then never enforces it has bought nothing, so this guard closes that gap: it is what
3337
3527
  * turns a bound schema into a checked one.
3338
3528
  *
3339
3529
  * Every own value must be one {@link MCPElicitValue} — a string, a finite number, a boolean,
3340
- * or an array of strings. A value whose name is DECLARED in `schema.properties` must in
3530
+ * or an array of strings. A value whose name is declared in `schema.properties` must in
3341
3531
  * addition satisfy that field's schema: `integer` rejects a fraction, `minimum` / `maximum`
3342
3532
  * bound a number, `minLength` / `maxLength` bound a string by code points, `enum` and `oneOf`
3343
3533
  * bound it to a declared member, `format` is enforced (`uri` by {@link isAbsoluteURI}, `email`
@@ -3346,15 +3536,15 @@ function isMCPElicitResult(value) {
3346
3536
  * `maxItems` with every entry drawn from its `items.enum` or `items.anyOf`. Every name listed
3347
3537
  * in `schema.required` must be present.
3348
3538
  *
3349
- * An UNDECLARED property remains valid: the restricted schema is open by default, so a client
3539
+ * An undeclared property remains valid: the restricted schema is open by default, so a client
3350
3540
  * that answers more than it was asked is not refused for it. A `schema` that is not itself a
3351
- * valid {@link MCPElicitSchema} admits NOTHING — an unenforceable schema is never a permissive
3541
+ * valid {@link MCPElicitSchema} admits nothing — an unenforceable schema is never a permissive
3352
3542
  * one — which is why `schema` is accepted as `unknown` and checked rather than trusted. Total
3353
3543
  * over hostile content and hostile schemas alike.
3354
3544
  *
3355
3545
  * @param value - The accepted response content to check
3356
3546
  * @param schema - The exact {@link MCPElicitSchema} that was issued with the elicitation
3357
- * @returns `true` when every declared and undeclared value is legal under `schema`
3547
+ * @returns True if every declared and undeclared value is legal under `schema`; false otherwise
3358
3548
  *
3359
3549
  * @example
3360
3550
  * ```ts
@@ -3443,7 +3633,7 @@ function isElicitContent(value, schema) {
3443
3633
  * including a URL-mode elicitation's `url`. Total over hostile input.
3444
3634
  *
3445
3635
  * @param value - The unknown value to inspect
3446
- * @returns `true` when `value` carries an absolute `uri` and an optional string `name`
3636
+ * @returns True if `value` carries an absolute `uri` and an optional string `name`; false otherwise
3447
3637
  *
3448
3638
  * @example
3449
3639
  * ```ts
@@ -3473,7 +3663,7 @@ function isMCPRoot(value) {
3473
3663
  * {@link isMCPRoot}. Total over hostile input.
3474
3664
  *
3475
3665
  * @param value - The unknown value to inspect
3476
- * @returns `true` when `value` carries an array of valid roots
3666
+ * @returns True if `value` carries an array of valid roots; false otherwise
3477
3667
  *
3478
3668
  * @example
3479
3669
  * ```ts
@@ -3506,7 +3696,7 @@ function isMCPRootResult(value) {
3506
3696
  * input.
3507
3697
  *
3508
3698
  * @param value - The unknown value to inspect
3509
- * @returns `true` when `value` is one legal sampling content block
3699
+ * @returns True if `value` is one legal sampling content block; false otherwise
3510
3700
  *
3511
3701
  * @example
3512
3702
  * ```ts
@@ -3541,13 +3731,14 @@ function isMCPSampleContent(value) {
3541
3731
  *
3542
3732
  * @remarks
3543
3733
  * The schema's `CreateMessageResult` types `content` as an `anyOf` over one
3544
- * {@link isMCPSampleContent} block or an ARRAY of them, so both are admitted here: a
3734
+ * {@link isMCPSampleContent} block or an array of them, so both are admitted here: a
3545
3735
  * tool-using model answers with `tool_use` and `tool_result` blocks, and a model answering in
3546
3736
  * several parts answers with the array. `stopReason` stays an open string because the schema
3547
- * names four values and permits any other a provider reports. Total over hostile input.
3737
+ * names `endTurn`, `stopSequence`, `maxTokens`, and `toolUse` and permits any other a provider
3738
+ * reports. Total over hostile input.
3548
3739
  *
3549
3740
  * @param value - The unknown value to inspect
3550
- * @returns `true` when `value` has the sampling-completion shape
3741
+ * @returns True if `value` has the sampling-completion shape; false otherwise
3551
3742
  *
3552
3743
  * @example
3553
3744
  * ```ts
@@ -3585,17 +3776,17 @@ function isMCPSampleResult(value) {
3585
3776
  * Determines whether a response answers the exact embedded request that was issued.
3586
3777
  *
3587
3778
  * @remarks
3588
- * A response carries no `method` of its own, so the ISSUED request selects which arm applies
3779
+ * A response carries no `method` of its own, so the issued request selects which arm applies
3589
3780
  * — the same way {@link isElicitContent} takes the issued schema rather than trusting the
3590
3781
  * content to describe itself. A form elicitation is checked twice: once for the response
3591
3782
  * shape and once, on `accept`, for the content against the schema that round issued. A
3592
3783
  * URL-mode elicitation issues no schema, so only the shape is checked. A request this
3593
- * package cannot recognize admits NOTHING, because an unrecognized question has no correct
3784
+ * package cannot recognize admits nothing, because an unrecognized question has no correct
3594
3785
  * answer. Total over hostile responses and hostile requests alike.
3595
3786
  *
3596
3787
  * @param value - The client's answer to check
3597
3788
  * @param request - The exact {@link MCPInputRequest} that was issued under the same key
3598
- * @returns `true` when the answer is legal for that request
3789
+ * @returns True if the answer is legal for that request; false otherwise
3599
3790
  *
3600
3791
  * @example
3601
3792
  * ```ts
@@ -3623,7 +3814,7 @@ function isMCPInputResponse(value, request) {
3623
3814
  * both must be present and valid. Total over hostile input.
3624
3815
  *
3625
3816
  * @param value - The unknown value to inspect
3626
- * @returns `true` when `value` is a valid input-required result
3817
+ * @returns True if `value` is a valid input-required result; false otherwise
3627
3818
  *
3628
3819
  * @example
3629
3820
  * ```ts
@@ -3653,14 +3844,14 @@ function isMCPInputResult(value) {
3653
3844
  *
3654
3845
  * @remarks
3655
3846
  * A request is a record with `jsonrpc === '2.0'`, a string `method`, and an `id`
3656
- * that {@link isJSONRPCId} accepts. An id-less call is NOT a request — it is a
3847
+ * that {@link isJSONRPCId} accepts. An id-less call is not a request — it is a
3657
3848
  * {@link JSONRPCNotification}, which {@link isJSONRPCNotification} answers for. The
3658
3849
  * guards are mutually exclusive on every input: this one requires a valid `id`
3659
3850
  * value, that one requires no own `id` member at all. `params`, when present, must
3660
3851
  * be a record. Total: any other input returns `false`.
3661
3852
  *
3662
3853
  * @param value - The already-parsed value to test
3663
- * @returns `true` when `value` is a valid JSON-RPC request
3854
+ * @returns True if `value` is a valid JSON-RPC request; false otherwise
3664
3855
  *
3665
3856
  * @example
3666
3857
  * ```ts
@@ -3682,12 +3873,12 @@ function isJSONRPCRequest(value) {
3682
3873
  * Determines whether a parsed value is a {@link JSONRPCNotification}.
3683
3874
  *
3684
3875
  * @remarks
3685
- * A notification is a request-shaped call carrying NO `id` member — the protocol
3876
+ * A notification is a request-shaped call carrying no `id` member — the protocol
3686
3877
  * forbids one, because nothing answers a notification. `params`, when present, must
3687
3878
  * be a record. Total: any other input returns `false`.
3688
3879
  *
3689
3880
  * @param value - The already-parsed value to test
3690
- * @returns `true` when `value` is a valid JSON-RPC notification
3881
+ * @returns True if `value` is a valid JSON-RPC notification; false otherwise
3691
3882
  *
3692
3883
  * @example
3693
3884
  * ```ts
@@ -3713,7 +3904,7 @@ function isJSONRPCNotification(value) {
3713
3904
  * mutually exclusive, so a positive answer names exactly one arm. Total.
3714
3905
  *
3715
3906
  * @param value - The already-parsed value to test
3716
- * @returns `true` when `value` is a valid JSON-RPC request or notification
3907
+ * @returns True if `value` is a valid JSON-RPC request or notification; false otherwise
3717
3908
  */
3718
3909
  function isJSONRPCInvocation(value) {
3719
3910
  return isJSONRPCRequest(value) || isJSONRPCNotification(value);
@@ -3723,15 +3914,15 @@ function isJSONRPCInvocation(value) {
3723
3914
  * arm of a response.
3724
3915
  *
3725
3916
  * @remarks
3726
- * A result answers a request, so `id` is REQUIRED and must be a valid
3727
- * {@link isJSONRPCId}. The envelope must own a `result` and must NOT own an `error`,
3917
+ * A result answers a request, so `id` is required and must be a valid
3918
+ * {@link isJSONRPCId}. The envelope must own a `result` and must not own an `error`,
3728
3919
  * which is what makes this guard and {@link isJSONRPCErrorResponse} mutually
3729
3920
  * exclusive on every input. `result` itself must be an object: either a modern
3730
3921
  * {@link isMCPResult} or a legacy {@link isMCPLegacyResult}, never a bare primitive.
3731
3922
  * Total.
3732
3923
  *
3733
3924
  * @param value - The already-parsed value to test
3734
- * @returns `true` when `value` is a valid JSON-RPC result response
3925
+ * @returns True if `value` is a valid JSON-RPC result response; false otherwise
3735
3926
  *
3736
3927
  * @example
3737
3928
  * ```ts
@@ -3752,22 +3943,22 @@ function isJSONRPCResultResponse(value) {
3752
3943
  * Determines whether a value is one JSON-RPC `error` member.
3753
3944
  *
3754
3945
  * @remarks
3755
- * The failure OBJECT, not the envelope carrying it — the shape a failed response owns
3946
+ * The failure object, not the envelope carrying it — the shape a failed response owns
3756
3947
  * under `error`, and the shape a `failed` {@link MCPTaskDetail} owns under the same name,
3757
3948
  * which is why it is one guard rather than the same checks written twice.
3758
3949
  *
3759
- * It is deliberately STRUCTURAL rather than exact-JSON: `data` is declared `unknown`, so
3950
+ * It is deliberately structural rather than exact-JSON: `data` is declared `unknown`, so
3760
3951
  * requiring the whole object to survive a JSON clone would refuse a legal error that
3761
3952
  * carried a non-JSON payload. Both callers here hand it an already-owned value.
3762
3953
  *
3763
3954
  * That choice is why the key reads are guarded. Every sibling guard clones first, and a
3764
3955
  * clone reads each key once behind a boundary that already owns totality; this one is the
3765
- * family's only DIRECT reader, so it meets `code` and `message` exactly as the value defines
3956
+ * family's only direct reader, so it meets `code` and `message` exactly as the value defines
3766
3957
  * them — including as accessors that throw. Reading a named key off an unowned value is
3767
3958
  * itself the hostile step, and it is bounded here rather than allowed to escape. Total.
3768
3959
  *
3769
3960
  * @param value - The already-parsed value to test
3770
- * @returns `true` when `value` carries an integer `code` and a string `message`
3961
+ * @returns True if `value` carries an integer `code` and a string `message`; false otherwise
3771
3962
  *
3772
3963
  * @example
3773
3964
  * ```ts
@@ -3785,13 +3976,13 @@ function isJSONRPCError(value) {
3785
3976
  * arm of a response.
3786
3977
  *
3787
3978
  * @remarks
3788
- * `id` is OPTIONAL here and only here: a peer that could not read the failed
3789
- * request's id OMITS the member rather than sending `null`, so an absent `id` is
3790
- * valid and a `null` one is not. The envelope must own an `error` and must NOT own a
3979
+ * `id` is optional here and only here: a peer that could not read the failed
3980
+ * request's id omits the member rather than sending `null`, so an absent `id` is
3981
+ * valid and a `null` one is not. The envelope must own an `error` and must not own a
3791
3982
  * `result`. `error` carries an integer `code` and a string `message`. Total.
3792
3983
  *
3793
3984
  * @param value - The already-parsed value to test
3794
- * @returns `true` when `value` is a valid JSON-RPC error response
3985
+ * @returns True if `value` is a valid JSON-RPC error response; false otherwise
3795
3986
  *
3796
3987
  * @example
3797
3988
  * ```ts
@@ -3815,7 +4006,7 @@ function isJSONRPCErrorResponse(value) {
3815
4006
  * The union of the mutually exclusive arms. Total.
3816
4007
  *
3817
4008
  * @param value - The already-parsed value to test
3818
- * @returns `true` when `value` is a valid JSON-RPC response
4009
+ * @returns True if `value` is a valid JSON-RPC response; false otherwise
3819
4010
  */
3820
4011
  function isJSONRPCResponse(value) {
3821
4012
  return isJSONRPCResultResponse(value) || isJSONRPCErrorResponse(value);
@@ -3828,7 +4019,7 @@ function isJSONRPCResponse(value) {
3828
4019
  * The union of {@link isJSONRPCInvocation} and {@link isJSONRPCResponse}. Total.
3829
4020
  *
3830
4021
  * @param value - The already-parsed value to test
3831
- * @returns `true` when `value` is a valid JSON-RPC message
4022
+ * @returns True if `value` is a valid JSON-RPC message; false otherwise
3832
4023
  */
3833
4024
  function isJSONRPCMessage(value) {
3834
4025
  return isJSONRPCInvocation(value) || isJSONRPCResponse(value);
@@ -3837,7 +4028,7 @@ function isJSONRPCMessage(value) {
3837
4028
  * Determines whether a parsed value is an MCP `initialize` invocation.
3838
4029
  *
3839
4030
  * @param value - The already-parsed value to test
3840
- * @returns `true` when `value` is a valid `initialize` request or notification
4031
+ * @returns True if `value` is a valid `initialize` request or notification; false otherwise
3841
4032
  *
3842
4033
  * @example
3843
4034
  * ```ts
@@ -3860,7 +4051,7 @@ function isInitializeRequest(value) {
3860
4051
  * legacy dispatch. Total over hostile and malformed input.
3861
4052
  *
3862
4053
  * @param value - The already-parsed value to inspect
3863
- * @returns `true` when the value is an invocation carrying the reserved version key
4054
+ * @returns True if the value is an invocation carrying the reserved version key; false otherwise
3864
4055
  */
3865
4056
  function isModernRequest(value) {
3866
4057
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
@@ -3877,9 +4068,9 @@ function isModernRequest(value) {
3877
4068
  * Infers the wire era for an MCP protocol revision.
3878
4069
  *
3879
4070
  * @remarks
3880
- * The era is READ from the two era guards rather than restated here, so a revision added
4071
+ * The era is read from the era guards rather than restated here, so a revision added
3881
4072
  * to {@link SUPPORTED_MODERN_PROTOCOL_VERSIONS} or {@link SUPPORTED_LEGACY_PROTOCOL_VERSIONS}
3882
- * carries its era with it and no third list can disagree with those two.
4073
+ * carries its era with it and no further list can disagree with them.
3883
4074
  *
3884
4075
  * @param version - The protocol revision to classify
3885
4076
  * @returns `'modern'` for a revision a bare server accepts, `'legacy'` for a revision the
@@ -3890,6 +4081,27 @@ function inferEra(version) {
3890
4081
  if (isMCPLegacyVersion(version)) return "legacy";
3891
4082
  }
3892
4083
  /**
4084
+ * Infers the wire era one invocation's own structure selects.
4085
+ *
4086
+ * @remarks
4087
+ * The structural read, distinct from {@link inferEra}'s read of a revision string: era is fixed
4088
+ * by the reserved modern metadata a request carries, so this answers for a message whose
4089
+ * revision has not been read and cannot answer `undefined` — every invocation took one of the
4090
+ * published wire shapes. It is what an observation surface reports and what an ingress
4091
+ * routes on, so both derive it here rather than each spelling the ternary out.
4092
+ *
4093
+ * @param invocation - The invocation whose structure selects the era
4094
+ * @returns `'modern'` when the invocation carries the modern request shape, `'legacy'` otherwise
4095
+ *
4096
+ * @example
4097
+ * ```ts
4098
+ * inferRequestEra({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: { _meta: meta } })
4099
+ * ```
4100
+ */
4101
+ function inferRequestEra(invocation) {
4102
+ return isModernRequest(invocation) ? "modern" : "legacy";
4103
+ }
4104
+ /**
3893
4105
  * Infers the newest supported modern protocol revision present in a peer's offer.
3894
4106
  *
3895
4107
  * @param offered - The protocol revisions offered by the peer
@@ -3899,15 +4111,15 @@ function inferVersion(offered) {
3899
4111
  for (const version of SUPPORTED_MODERN_PROTOCOL_VERSIONS) if (offered.includes(version)) return version;
3900
4112
  }
3901
4113
  /**
3902
- * Infers the protocol version an outbound message announces itself with — the ONE
4114
+ * Infers the protocol version an outbound message announces itself with — the one
3903
4115
  * projection every HTTP client transport stamps `mcp-protocol-version` from.
3904
4116
  *
3905
4117
  * @remarks
3906
- * This is deliberately the SAME read the server's own expectation performs
4118
+ * This is deliberately the same read the server's own expectation performs
3907
4119
  * ({@link import('@orkestrel/mcp/server').inferHeaderIssue}): a modern request's reserved
3908
- * `_meta` version, accepted whenever it is a string. It is NOT
4120
+ * `_meta` version, accepted whenever it is a string. It is not
3909
4121
  * {@link import('./parsers.js').parseRequestContext}, and the difference is the whole
3910
- * point. That parser answers a different question — is the modern metadata WELL FORMED
4122
+ * point. That parser answers a different question — is the modern metadata well formed
3911
4123
  * and refuses a request whose capability declaration or logging level is malformed. Such a
3912
4124
  * request is still modern (era is fixed by key presence) and the server still demands the
3913
4125
  * header for it, so projecting through the parser withholds a header the peer requires and
@@ -3916,7 +4128,7 @@ function inferVersion(offered) {
3916
4128
  * A non-modern message projects nothing: a legacy request's version comes from the
3917
4129
  * `initialize` handshake the transport captured, not from the message.
3918
4130
  *
3919
- * Header NAMES stay with the transports that own the wire (see `constants.ts`); core owns
4131
+ * Header names stay with the transports that own the wire (see `constants.ts`); core owns
3920
4132
  * the value this projection derives, which is the part the browser and Node faces disagreed about.
3921
4133
  *
3922
4134
  * @param message - The outbound message about to be written
@@ -3935,16 +4147,16 @@ function inferRequestVersion(message) {
3935
4147
  //#endregion
3936
4148
  //#region src/core/MCPMethodManager.ts
3937
4149
  /**
3938
- * The modern method registry an {@link import('./types.js').MCPServerInterface}
4150
+ * Holds the modern methods an {@link import('./types.js').MCPServerInterface}
3939
4151
  * dispatches through — a name-keyed store of {@link MCPMethodHandler}s that owns its
3940
4152
  * map rather than exposing one.
3941
4153
  *
3942
4154
  * @remarks
3943
4155
  * - **One seam.** The server registers its built-in modern methods here at construction
3944
- * and resolves EVERY modern method from here, so a consumer's method and a built-in
4156
+ * and resolves every modern method from here, so a consumer's method and a built-in
3945
4157
  * are the same kind of thing on the same path.
3946
4158
  * - **Registration is a write, not a merge.** `add` under a name already present
3947
- * REPLACES it, which is how a consumer overrides a built-in; there is no precedence
4159
+ * replaces it, which is how a consumer overrides a built-in; there is no precedence
3948
4160
  * rule to remember.
3949
4161
  * - **A narrower contract than a `Map`.** Callers register and resolve; they cannot
3950
4162
  * iterate, clear, or otherwise reach the server's internal state through it.
@@ -3969,7 +4181,7 @@ var MCPMethodManager = class {
3969
4181
  //#endregion
3970
4182
  //#region src/core/MCPProgressReporter.ts
3971
4183
  /**
3972
- * A bounded, request-scoped progress handoff between one producer and one serial consumer.
4184
+ * Hands bounded, request-scoped progress from one producer to one serial consumer.
3973
4185
  *
3974
4186
  * The reporter holds at most one owned progress item. {@link report} applies backpressure until
3975
4187
  * {@link take} consumes that slot. It has no replay, queue, concurrent-consumer coordination,
@@ -4093,19 +4305,20 @@ var MCPProgressReporter = class {
4093
4305
  //#endregion
4094
4306
  //#region src/core/MCPStreamController.ts
4095
4307
  /**
4096
- * The one cancellation engine every modern held-open result leaves `MCPServer` through.
4308
+ * Provides the one cancellation engine every modern held-open result leaves `MCPServer`
4309
+ * through.
4097
4310
  *
4098
4311
  * @remarks
4099
- * A native async generator decides cancellation with a QUEUE: `return()` and `throw()` wait
4312
+ * A native async generator decides cancellation with a queue: `return()` and `throw()` wait
4100
4313
  * behind a `next()` the producer has not answered, so a consumer abandoning a source parked
4101
4314
  * on an event that will never arrive waits forever for its own cancellation. This class
4102
- * arbitrates instead of queueing. It keeps at most ONE read outstanding against the source,
4103
- * settles the consumer's read itself, aborts the request's lifetime BEFORE it delegates
4315
+ * arbitrates instead of queueing. It keeps at most one read outstanding against the source,
4316
+ * settles the consumer's read itself, aborts the request's lifetime before it delegates
4104
4317
  * cleanup to the producer — so a cooperating producer is woken rather than waited on —
4105
4318
  * contains every promise the producer settles late, and makes every closure path idempotent.
4106
4319
  *
4107
4320
  * The closures are deliberately different answers: the source's own return is the
4108
- * terminal RESPONSE, `return(value)` is the consumer saying it has the answer already, and
4321
+ * terminal response, `return(value)` is the consumer saying it has the answer already, and
4109
4322
  * {@link stop} is an owner saying there will be no answer at all. Only the source's own
4110
4323
  * return is a message a peer ever sees.
4111
4324
  *
@@ -4113,7 +4326,7 @@ var MCPProgressReporter = class {
4113
4326
  * generator is suspended inside, so the signal is how an uncooperative producer is asked to
4114
4327
  * finish, and this controller never blocks its consumer on the answer.
4115
4328
  *
4116
- * **What this class does NOT have is an owner of last resort.** No finalizer, no timer, no
4329
+ * **What this class does not have is an owner of last resort.** No finalizer, no timer, no
4117
4330
  * timeout ends an exchange nobody released. That absence is the design: an exchange holds a
4118
4331
  * producer, a request lifetime and a live server slot, so a silent background release would
4119
4332
  * turn "a pump forgot its obligation" from a reproducible defect into a nondeterministic one,
@@ -4290,19 +4503,19 @@ var MCPStreamController = class {
4290
4503
  //#endregion
4291
4504
  //#region src/core/MCPTextStreamController.ts
4292
4505
  /**
4293
- * The string-boundary mirror of a controlled held-open result — the same exchange, already
4506
+ * Mirrors a controlled held-open result at the string boundary — the same exchange, already
4294
4507
  * serialized.
4295
4508
  *
4296
4509
  * @remarks
4297
- * A TRANSLATION boundary and deliberately nothing else. It serializes each message and the
4510
+ * A translation boundary and deliberately nothing else. It serializes each message and the
4298
4511
  * terminating response, and every lifecycle decision — return, throw, dispose, stop — ends
4299
4512
  * the typed exchange beneath it rather than this face. That is the whole design constraint: a
4300
- * serialized face implemented as its own async generator would add a SECOND operation queue,
4513
+ * serialized face implemented as its own async generator would add a second operation queue,
4301
4514
  * and the queue is exactly the defect the typed controller exists to remove — a `return()`
4302
4515
  * promptly settled at the text face and left queued at the typed one cancels nothing.
4303
4516
  *
4304
4517
  * One member is a narrowing rather than a pass-through, and it is worth knowing before it
4305
- * surprises a producer. `return` receives a STRING; it cannot rebuild the typed
4518
+ * surprises a producer. `return` receives a string; it cannot rebuild the typed
4306
4519
  * `JSONRPCResponse` the typed face would close on, and inventing one by parsing the
4307
4520
  * argument back would make this face decide what the exchange ended with. So it ends the
4308
4521
  * typed exchange with {@link MCPStreamControllerInterface.stop} — no terminal — and answers
@@ -4311,11 +4524,11 @@ var MCPStreamController = class {
4311
4524
  * the honest translation of "the consumer already has its answer" when the answer is opaque
4312
4525
  * text, not a downgrade to work around.
4313
4526
  *
4314
- * It accepts only a CONTROLLED typed stream. A raw generator would have no lifecycle to
4527
+ * It accepts only a controlled typed stream. A raw generator would have no lifecycle to
4315
4528
  * delegate to, and this class refuses to grow one of its own.
4316
4529
  *
4317
4530
  * Delegation is total and it is what makes the ownership obligation transitive: `return`,
4318
- * `throw`, `stop`, and dispose each end the TYPED exchange, so a pump holding only this
4531
+ * `throw`, `stop`, and dispose each end the typed exchange, so a pump holding only this
4319
4532
  * serialized face still releases the producer, the request lifetime, and the live server slot
4320
4533
  * behind it. There is no owner of last resort here either, for the same reason there is none
4321
4534
  * on the typed face.
@@ -4362,7 +4575,7 @@ var MCPTextStreamController = class {
4362
4575
  * @remarks
4363
4576
  * The typed exchange ends with no terminal, because a string is not a
4364
4577
  * `JSONRPCResponse` and this face never parses one back out of its argument. The
4365
- * supplied text is the answer to THIS consumer, and a cooperating producer sees its
4578
+ * supplied text is the answer to this consumer, and a cooperating producer sees its
4366
4579
  * cancellation path rather than its normal return.
4367
4580
  *
4368
4581
  * @param value - The serialized terminal the consumer is ending on
@@ -4399,7 +4612,7 @@ var MCPTextStreamController = class {
4399
4612
  *
4400
4613
  * @remarks
4401
4614
  * Delegates downward exactly as {@link stop} does — disposing the serialized arm is
4402
- * disposing the exchange, never just this adapter.
4615
+ * disposing the exchange, never this adapter alone.
4403
4616
  *
4404
4617
  * @returns Resolves once the typed exchange has ended
4405
4618
  */
@@ -4452,15 +4665,11 @@ var MCPLegacy = class {
4452
4665
  }
4453
4666
  async handle(message, options) {
4454
4667
  if (!isBoundedString(message, this.limit.message)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request"));
4455
- let parsed;
4456
- try {
4457
- parsed = JSON.parse(message);
4458
- } catch {
4459
- return this.#options.dispatcher.handle(message, options);
4460
- }
4668
+ const parsed = (0, _orkestrel_contract.parseJSON)(message);
4461
4669
  if (isModernRequest(parsed) || !isJSONRPCInvocation(parsed)) return this.#options.dispatcher.handle(message, options);
4462
4670
  const answer = await this.#legacy(parsed, options);
4463
- return answer === void 0 ? void 0 : JSON.stringify(answer);
4671
+ if (answer === void 0) return void 0;
4672
+ return Symbol.asyncIterator in answer ? new MCPTextStreamController(answer) : JSON.stringify(answer);
4464
4673
  }
4465
4674
  async #legacy(invocation, options) {
4466
4675
  if (invocation.id === void 0) return void 0;
@@ -4484,13 +4693,42 @@ var MCPLegacy = class {
4484
4693
  }
4485
4694
  async #forward(request, options) {
4486
4695
  const translated = legacyInvocationToModern(request);
4487
- const answer = await this.#options.dispatcher.dispatch(translated, options);
4488
- if (Symbol.asyncIterator in answer) {
4489
- answer.stop();
4490
- await answer[Symbol.asyncDispose]();
4491
- return this.#unsupported(request.id, "stream");
4696
+ const metadata = request.method === "tools/call" ? request.params?.["_meta"] : void 0;
4697
+ const candidate = (0, _orkestrel_contract.isRecord)(metadata) ? metadata["progressToken"] : void 0;
4698
+ const token = (0, _orkestrel_contract.isString)(candidate) || (0, _orkestrel_contract.isInteger)(candidate) ? candidate : void 0;
4699
+ if (token === void 0) {
4700
+ const answer = await this.#options.dispatcher.dispatch(translated, options);
4701
+ if (Symbol.asyncIterator in answer) {
4702
+ answer.stop();
4703
+ await answer[Symbol.asyncDispose]();
4704
+ return this.#unsupported(request.id, "stream");
4705
+ }
4706
+ return this.#project(answer, request.id);
4707
+ }
4708
+ const closure = new AbortController();
4709
+ const resolved = buildMethodOptions(options ?? {}, closure.signal);
4710
+ try {
4711
+ const answer = await this.#options.dispatcher.dispatch(translated, resolved);
4712
+ if (Symbol.asyncIterator in answer) return new MCPStreamController(this.#progress(answer, request.id, token), resolved.signal, closure);
4713
+ closure.abort();
4714
+ return this.#project(answer, request.id);
4715
+ } catch (error) {
4716
+ closure.abort(error);
4717
+ throw error;
4718
+ }
4719
+ }
4720
+ async *#progress(stream, id, token) {
4721
+ try {
4722
+ while (true) {
4723
+ const frame = await stream.next();
4724
+ if (frame.done === true) return this.#project(frame.value, id);
4725
+ const params = frame.value.params;
4726
+ if (frame.value.method !== "notifications/progress" || !(0, _orkestrel_contract.isRecord)(params) || params["progressToken"] !== token || !isMCPProgress(params)) return this.#unsupported(id, "stream");
4727
+ yield frame.value;
4728
+ }
4729
+ } finally {
4730
+ await stream[Symbol.asyncDispose]();
4492
4731
  }
4493
- return this.#project(answer, request.id);
4494
4732
  }
4495
4733
  #project(answer, id) {
4496
4734
  if (answer.error !== void 0) return answer.error.code === -32021 ? this.#unsupported(id, this.#capability(answer)) : answer;
@@ -4544,7 +4782,7 @@ var MCPLegacyClientTransport = class {
4544
4782
  if (requested !== void 0 && !isMCPLegacyVersion(requested)) throw new MCPError("Unsupported legacy protocol version", MCP_UNSUPPORTED_VERSION, { requested });
4545
4783
  this.#transport = transport;
4546
4784
  this.#client = options?.identity ?? {
4547
- name: "taverna",
4785
+ name: "@orkestrel/mcp",
4548
4786
  version: "1.0.0"
4549
4787
  };
4550
4788
  this.#capabilities = options?.capabilities ?? {};
@@ -4736,8 +4974,8 @@ var MCPLegacyClientTransport = class {
4736
4974
  //#endregion
4737
4975
  //#region src/core/MCPServer.ts
4738
4976
  /**
4739
- * A transport-agnostic Model Context Protocol server dispatches JSON-RPC 2.0
4740
- * requests over a live {@link ToolManagerInterface}, with NO transport coupling.
4977
+ * Dispatches JSON-RPC 2.0 requests over a live {@link ToolManagerInterface}, with no
4978
+ * transport coupling.
4741
4979
  *
4742
4980
  * @remarks
4743
4981
  * - **`dispatch` and `handle`.** `dispatch(invocation)` runs an already-parsed invocation and
@@ -4746,7 +4984,7 @@ var MCPLegacyClientTransport = class {
4746
4984
  * `handle(message)` is the string boundary: it
4747
4985
  * `JSON.parse`s the raw message (a failure → a `-32700` response), narrows it to
4748
4986
  * an invocation (a non-invocation → a `-32600` response, with the unreadable `id`
4749
- * OMITTED rather than nulled), dispatches, and serializes the
4987
+ * omitted rather than nulled), dispatches, and serializes the
4750
4988
  * response back to a string (`undefined` for a notification).
4751
4989
  * - **One modern seam.** `server/discover`, `tools/list`, `tools/call`, and
4752
4990
  * `subscriptions/listen` are always registered; `resources/*`, `prompts/*`, and
@@ -4814,8 +5052,21 @@ var MCPServer = class {
4814
5052
  if (decoded === void 0 || !("method" in decoded)) return buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request");
4815
5053
  return this.#dispatch(decoded, options);
4816
5054
  }
5055
+ async handle(message, options) {
5056
+ if (!isBoundedString(message, this.#limits.message)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
5057
+ const parsed = (0, _orkestrel_contract.parseJSON)(message);
5058
+ if (parsed === void 0) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
5059
+ const decoded = parseJSONRPCMessage(parsed, {
5060
+ bytes: this.#limits.message,
5061
+ depth: this.#limits.depth
5062
+ });
5063
+ if (decoded === void 0 || !("method" in decoded)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request"));
5064
+ const answer = await this.#dispatch(decoded, options ?? {});
5065
+ if (answer === void 0) return void 0;
5066
+ return Symbol.asyncIterator in answer ? new MCPTextStreamController(answer) : JSON.stringify(answer);
5067
+ }
4817
5068
  async #dispatch(invocation, options) {
4818
- this.#emitter.emit("request", invocation.method, invocation.id, "modern");
5069
+ this.#emitter.emit("request", invocation.method, invocation.id, inferRequestEra(invocation));
4819
5070
  if (invocation.id === void 0) return;
4820
5071
  const id = invocation.id;
4821
5072
  const metadata = invocation.params?.["_meta"];
@@ -4833,26 +5084,9 @@ var MCPServer = class {
4833
5084
  return this.#contain(error, id);
4834
5085
  }
4835
5086
  }
4836
- async handle(message, options) {
4837
- if (!isBoundedString(message, this.#limits.message)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
4838
- let parsed;
4839
- try {
4840
- parsed = JSON.parse(message);
4841
- } catch {
4842
- return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
4843
- }
4844
- const decoded = parseJSONRPCMessage(parsed, {
4845
- bytes: this.#limits.message,
4846
- depth: this.#limits.depth
4847
- });
4848
- if (decoded === void 0 || !("method" in decoded)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request"));
4849
- const answer = await this.#dispatch(decoded, options ?? {});
4850
- if (answer === void 0) return void 0;
4851
- return Symbol.asyncIterator in answer ? new MCPTextStreamController(answer) : JSON.stringify(answer);
4852
- }
4853
5087
  #register() {
4854
- this.#methods.add("server/discover", async (request, _options) => this.#discover(request));
4855
- this.#methods.add("tools/list", async (request, _options) => this.#list(request));
5088
+ this.#methods.add("server/discover", async (request) => this.#discover(request));
5089
+ this.#methods.add("tools/list", async (request) => this.#list(request));
4856
5090
  this.#methods.add("tools/call", async (request, options) => this.#call(request, options));
4857
5091
  this.#methods.add("subscriptions/listen", async (request, options) => this.#subscribe(request, options));
4858
5092
  const resources = this.#options.resources;
@@ -5066,20 +5300,20 @@ var MCPServer = class {
5066
5300
  async #defer(request, call, options) {
5067
5301
  const configured = this.#options.task;
5068
5302
  if (configured === void 0) return void 0;
5069
- const deferral = {
5303
+ const deferred = {
5070
5304
  request,
5071
5305
  call,
5072
5306
  tools: this.#options.tools
5073
5307
  };
5074
- const key = await configured.defer(deferral, options);
5308
+ const key = await configured.deferral(deferred, options);
5075
5309
  if ((0, _orkestrel_contract.isUndefined)(key)) return void 0;
5076
5310
  if (!(0, _orkestrel_contract.isString)(key) || key.length === 0) return buildJSONRPCError(request.id, JSONRPC_INTERNAL_ERROR, "Server execution returned an invalid task key");
5077
5311
  const context = parseRequestContext(request, {
5078
5312
  bytes: this.#limits.message,
5079
5313
  depth: this.#limits.depth
5080
5314
  });
5081
- if (context === void 0 || !isTaskSupported(context.capabilities)) return buildJSONRPCError(request.id, MCP_MISSING_CAPABILITY, "Client does not support the required Tasks extension", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5082
- const created = await configured.tasks.start(key, deferral, options);
5315
+ if (context === void 0 || !supportsTask(context.capabilities)) return buildJSONRPCError(request.id, MCP_MISSING_CAPABILITY, "Client does not support the required Tasks extension", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5316
+ const created = await configured.tasks.start(key, deferred, options);
5083
5317
  const captured = snapshotJSON({
5084
5318
  resultType: "task",
5085
5319
  taskId: created.taskId,
@@ -5163,7 +5397,7 @@ var MCPServer = class {
5163
5397
  arguments: args
5164
5398
  }, options);
5165
5399
  if (selected === void 0) return void 0;
5166
- const round = this.#round(selected);
5400
+ const round = this.#ownRound(selected);
5167
5401
  const context = parseRequestContext(request, {
5168
5402
  bytes: this.#limits.message,
5169
5403
  depth: this.#limits.depth
@@ -5216,7 +5450,7 @@ var MCPServer = class {
5216
5450
  const state = parseMCPInputState(verified);
5217
5451
  if (state === void 0) return this.#contain(/* @__PURE__ */ new Error("Continuation port opened a malformed protected payload"), id);
5218
5452
  if (state.expiry <= Date.now() || state.id === id || state.version !== context.version || state.method !== request.method || state.name !== name || state.digest !== digest) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
5219
- const responses = this.#answers(state.requests, inputResponses);
5453
+ const responses = this.#checkAnswers(state.requests, inputResponses);
5220
5454
  if (responses === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: an input response is missing or malformed");
5221
5455
  const principal = await configured.principal(request, options);
5222
5456
  if (!(0, _orkestrel_contract.isString)(principal) || principal.length === 0 || state.principal !== principal) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
@@ -5229,13 +5463,13 @@ var MCPServer = class {
5229
5463
  }, options);
5230
5464
  if (state.expiry <= Date.now()) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
5231
5465
  if (selected === void 0) return void 0;
5232
- const round = this.#round(selected);
5466
+ const round = this.#ownRound(selected);
5233
5467
  if (round === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: input policy returned an invalid round or continuation context");
5234
5468
  const refusal = this.#gate(round, context, id);
5235
5469
  if (refusal !== void 0) return refusal;
5236
5470
  return this.#required(request, name, digest, round, principal, state.id, state.expiry);
5237
5471
  }
5238
- #answers(requests, responses) {
5472
+ #checkAnswers(requests, responses) {
5239
5473
  const answered = {};
5240
5474
  for (const [key, issued] of Object.entries(requests)) {
5241
5475
  const response = responses[key];
@@ -5244,7 +5478,7 @@ var MCPServer = class {
5244
5478
  }
5245
5479
  return Object.freeze(answered);
5246
5480
  }
5247
- #round(round) {
5481
+ #ownRound(round) {
5248
5482
  const owned = snapshotJSON(round, {
5249
5483
  bytes: this.#limits.content,
5250
5484
  keys: this.#limits.keys,
@@ -5332,7 +5566,7 @@ var MCPServer = class {
5332
5566
  }
5333
5567
  yield buildSubscriptionAcknowledgement(notifications, id);
5334
5568
  if (configured !== void 0) {
5335
- const iterator = (await configured.listen(notifications, options))[Symbol.asyncIterator]();
5569
+ const iterator = (await configured.producer(notifications, options))[Symbol.asyncIterator]();
5336
5570
  options.signal.addEventListener("abort", () => void iterator.return?.(void 0)?.catch(() => void 0), { once: true });
5337
5571
  for (let next = await iterator.next(); next.done !== true; next = await iterator.next()) {
5338
5572
  const owned = parseJSONRPCMessage(next.value, {
@@ -5351,22 +5585,22 @@ var MCPServer = class {
5351
5585
  slot.abort();
5352
5586
  }
5353
5587
  }
5354
- #named(request) {
5588
+ #readTaskId(request) {
5355
5589
  const id = request.id;
5356
5590
  const context = parseRequestContext(request, {
5357
5591
  bytes: this.#limits.message,
5358
5592
  depth: this.#limits.depth
5359
5593
  });
5360
- if (context === void 0 || !isTaskSupported(context.capabilities)) return buildJSONRPCError(id, MCP_MISSING_CAPABILITY, "Server requires the tasks extension capability for this request", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5594
+ if (context === void 0 || !supportsTask(context.capabilities)) return buildJSONRPCError(id, MCP_MISSING_CAPABILITY, "Server requires the tasks extension capability for this request", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5361
5595
  const taskId = request.params?.["taskId"];
5362
5596
  if (!isBoundedString(taskId, this.#limits.state) || taskId.length === 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: a bounded string `taskId` is required");
5363
5597
  return taskId;
5364
5598
  }
5365
5599
  async #task(request, tasks, options) {
5366
5600
  const id = request.id;
5367
- const named = this.#named(request);
5368
- if (!(0, _orkestrel_contract.isString)(named)) return named;
5369
- const found = await tasks.task(named, options);
5601
+ const taskId = this.#readTaskId(request);
5602
+ if (!(0, _orkestrel_contract.isString)(taskId)) return taskId;
5603
+ const found = await tasks.task(taskId, options);
5370
5604
  if (found === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5371
5605
  const owned = snapshotJSON(found, {
5372
5606
  bytes: this.#limits.content,
@@ -5378,20 +5612,20 @@ var MCPServer = class {
5378
5612
  }
5379
5613
  async #update(request, tasks, options) {
5380
5614
  const id = request.id;
5381
- const named = this.#named(request);
5382
- if (!(0, _orkestrel_contract.isString)(named)) return named;
5615
+ const taskId = this.#readTaskId(request);
5616
+ if (!(0, _orkestrel_contract.isString)(taskId)) return taskId;
5383
5617
  const responses = request.params?.["inputResponses"];
5384
5618
  if (!(0, _orkestrel_contract.isRecord)(responses)) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: an `inputResponses` object is required");
5385
- if (!isMCPTaskDetail(await tasks.task(named, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5386
- await tasks.update(named, responses, options);
5619
+ if (!isMCPTaskDetail(await tasks.task(taskId, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5620
+ await tasks.update(taskId, responses, options);
5387
5621
  return buildJSONRPCResult(id, buildModernResult({}, this.#options.identity));
5388
5622
  }
5389
5623
  async #abort(request, tasks, options) {
5390
5624
  const id = request.id;
5391
- const named = this.#named(request);
5392
- if (!(0, _orkestrel_contract.isString)(named)) return named;
5393
- if (!isMCPTaskDetail(await tasks.task(named, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5394
- await tasks.abort(named, options);
5625
+ const taskId = this.#readTaskId(request);
5626
+ if (!(0, _orkestrel_contract.isString)(taskId)) return taskId;
5627
+ if (!isMCPTaskDetail(await tasks.task(taskId, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5628
+ await tasks.abort(taskId, options);
5395
5629
  return buildJSONRPCResult(id, buildModernResult({}, this.#options.identity));
5396
5630
  }
5397
5631
  #contain(error, id) {
@@ -5441,19 +5675,19 @@ var MCPServer = class {
5441
5675
  //#endregion
5442
5676
  //#region src/core/MCPTaskClient.ts
5443
5677
  /**
5444
- * The CLIENT half of the stable Tasks extension — the `tasks/*` methods over one
5445
- * correlated-request door, exposed as an {@link import('./types.js').MCPClientInterface}'s
5678
+ * Issues the `tasks/*` methods over one correlated-request door — the client half of the
5679
+ * stable Tasks extension, exposed as an {@link import('./types.js').MCPClientInterface}'s
5446
5680
  * `tasks`.
5447
5681
  *
5448
5682
  * @remarks
5449
5683
  * - **The mirror of the server-side port, minus `start`.** An
5450
5684
  * {@link import('./types.js').MCPTaskManagerInterface} is the consumer's durable store the
5451
- * SERVER creates tasks in; this is the client's read/answer/stop access to the tasks a peer
5685
+ * server creates tasks in; this is the client's read/answer/stop access to the tasks a peer
5452
5686
  * already created. Creation is missing on purpose: the extension gives a client no flag and
5453
5687
  * no parameter to ask for a task, so `start` has no wire method to be.
5454
5688
  * - **No plural accessor, no loop, no cache.** MCP defines no `tasks/list`, so nothing here
5455
5689
  * enumerates. A task snapshot's `pollIntervalMs` is carried untouched and a one-shot read
5456
- * sits beside it; the SCHEDULE is the consumer's, because this package has no durable place
5690
+ * sits beside it; the schedule is the consumer's, because this package has no durable place
5457
5691
  * to keep a task, no idea when the application still cares, and no lifetime to hang a timer
5458
5692
  * on that outlives the request it was born from. An instance left alone writes nothing.
5459
5693
  * - **One channel.** Every request goes through the injected
@@ -5501,20 +5735,20 @@ var MCPTaskClient = class {
5501
5735
  //#endregion
5502
5736
  //#region src/core/MCPClient.ts
5503
5737
  /**
5504
- * A transport-agnostic Model Context Protocol CLIENT connects to a REMOTE MCP server
5505
- * over an injected {@link MCPClientTransportInterface}, negotiates the modern revision, and
5506
- * exposes the server's tools as local {@link ToolInterface}s an agent can run.
5738
+ * Connects to a remote MCP server over any injected {@link MCPMessageTransportInterface},
5739
+ * negotiates the modern revision, and exposes the server's tools as local
5740
+ * {@link ToolInterface}s an agent can run.
5507
5741
  *
5508
5742
  * @remarks
5509
- * - **The mirror of `MCPServer`.** The server DISPATCHES requests over a tool registry;
5510
- * this client ISSUES them over a transport. `connect` probes `server/discover` and exposes
5743
+ * - **The mirror of `MCPServer`.** The server dispatches requests over a tool registry;
5744
+ * this client issues them over a transport. `connect` probes `server/discover` and exposes
5511
5745
  * the negotiated `version`; a legacy peer requires an explicit transport adapter.
5512
5746
  * `tools()` lists the remote tools and wraps each as a
5513
5747
  * local {@link ToolInterface} whose `execute` calls back through `call`; `call` runs a
5514
5748
  * remote `tools/call` and reports the arm the peer answered with — a value, a durable
5515
5749
  * task, or a request for more input (a remote `isError: true` throws locally, so an
5516
5750
  * agent's {@link import('@orkestrel/tool').ToolManagerInterface} isolates it into a
5517
- * `success: false` result just like a local throw). A wrapped tool cannot hand an agent
5751
+ * `success: false` result exactly like a local throw). A wrapped tool cannot hand an agent
5518
5752
  * a deferred answer, so a non-`'complete'` arm throws there.
5519
5753
  * - **Request↔response correlation.** Each request is tagged with a monotonic numeric
5520
5754
  * `id` ({@link #nextId}); a single transport `message` subscription resolves / rejects
@@ -5522,9 +5756,9 @@ var MCPTaskClient = class {
5522
5756
  * every pending request because the peer could not identify which request failed. A
5523
5757
  * server-initiated message is re-surfaced on the `notification` event, except a progress
5524
5758
  * frame claimed by the request that asked
5525
- * for it; a RESPONSE correlating to nothing pending is discarded, because the request it
5759
+ * for it; a response correlating to nothing pending is discarded, because the request it
5526
5760
  * answers has already settled.
5527
- * - **Per-request cancellation.** `call`'s `options.signal` withdraws ONE caller from ONE
5761
+ * - **Per-request cancellation.** `call`'s `options.signal` withdraws one caller from one
5528
5762
  * request: the pending entry rejects on every carrier, and `notifications/cancelled` goes
5529
5763
  * out only where the transport declares itself duplex — the dated revision defines no
5530
5764
  * client-to-server notification over Streamable HTTP, where closing the response stream
@@ -5541,7 +5775,7 @@ var MCPTaskClient = class {
5541
5775
  * discovery probe uses that same configured deadline, so a silent peer cannot hold
5542
5776
  * negotiation indefinitely.
5543
5777
  * `AbortSignal.timeout` (never a raw `setTimeout`) rejects only that pending request, and the
5544
- * same deadline bounds the WAIT on the transport's `close`, the one wait no drain and no signal
5778
+ * same deadline bounds the wait on the transport's `close`, the one wait no drain and no signal
5545
5779
  * can reach. It bounds the wait rather than the close, which keeps running, so a retry joins it
5546
5780
  * instead of shutting one connection down twice.
5547
5781
  * - **Transport-agnostic.** Imports only core siblings (JSON-RPC + the tool vocabulary);
@@ -5594,7 +5828,7 @@ var MCPClient = class {
5594
5828
  });
5595
5829
  this.#transport = options.transport;
5596
5830
  this.#identity = options.identity ?? {
5597
- name: "taverna",
5831
+ name: "@orkestrel/mcp",
5598
5832
  version: "1.0.0"
5599
5833
  };
5600
5834
  this.#capabilities = options.capabilities ?? {};
@@ -5735,18 +5969,19 @@ var MCPClient = class {
5735
5969
  } }
5736
5970
  }
5737
5971
  };
5738
- const subscription = {
5739
- queue: [],
5740
- capacity
5741
- };
5742
5972
  const abort = this.#abortSubscription.bind(this, id, signal);
5743
5973
  signal.addEventListener("abort", abort, { once: true });
5744
5974
  this.#pending.set(id, {
5745
5975
  method,
5746
5976
  signal,
5747
5977
  abort,
5748
- subscription
5978
+ subscription: {
5979
+ queue: [],
5980
+ capacity
5981
+ }
5749
5982
  });
5983
+ const subscription = this.#pending.get(id)?.subscription;
5984
+ if (subscription === void 0) throw new Error("MCP subscription state is missing");
5750
5985
  this.#transport.send(request).catch((error) => this.#settle(id, error, true));
5751
5986
  try {
5752
5987
  for (;;) {
@@ -6072,13 +6307,222 @@ var MCPClient = class {
6072
6307
  }
6073
6308
  };
6074
6309
  //#endregion
6310
+ //#region src/core/transports/HTTPClientTransport.ts
6311
+ /**
6312
+ * Drives a remote Streamable-HTTP MCP server over `fetch` — a client
6313
+ * {@link MCPMessageTransportInterface} for the Model Context Protocol, the egress mirror of
6314
+ * the server's `createMCPRoutes`.
6315
+ *
6316
+ * @remarks
6317
+ * - **One class, both faces.** It touches `fetch`, `Response`, `AbortController`,
6318
+ * `AbortSignal`, and `WeakMap` alone, so it is host-independent and lives in core. Each
6319
+ * environment face publishes its own `createHTTPClientTransport` over it —
6320
+ * `@orkestrel/mcp/browser` and `@orkestrel/mcp/server` — and both factories return this
6321
+ * class, so a reply reaches a page and a Node process through the same decode.
6322
+ * - **Request/response over `fetch`.** `send(message)` POSTs the JSON-serialized
6323
+ * message to `options.url` with `content-type: application/json` and an
6324
+ * `Accept` of both `application/json` and `text/event-stream` (so the server may
6325
+ * answer with either framing) — plus any `options.headers` (for example, an `Authorization`
6326
+ * bearer). It then decodes the reply and emits each decoded {@link JSONRPCMessage} on
6327
+ * the `message` event the {@link import('@orkestrel/mcp').MCPClientInterface} subscribes
6328
+ * to.
6329
+ * - **Both reply framings.** A `200` with an `application/json` body is parsed with
6330
+ * `parseJSONRPCMessage`; a `200` with a `text/event-stream` body is decoded with the
6331
+ * `@orkestrel/sse` {@link import('@orkestrel/sse').SSEParserInterface} ({@link
6332
+ * readEventStream}) — the inverse of the server's `createStream` seam, so the wire
6333
+ * round-trips. A `202`
6334
+ * Accepted (a notification) carries no body and emits nothing.
6335
+ * - **Session and protocol headers.** `start()` is a no-op (a
6336
+ * request/response transport opens no long-lived connection). The
6337
+ * `mcp-session-id` response header, when a stateful server sends one (on
6338
+ * `initialize`), is captured into `session` and then echoed as the
6339
+ * `mcp-session-id` request header on every subsequent request — so an
6340
+ * `MCPClient` passes a stateful server's session validation. The
6341
+ * initialize result's `protocolVersion` is likewise captured, but only
6342
+ * when it is a supported value, and echoed as `mcp-protocol-version` alone on
6343
+ * subsequent legacy requests. Modern requests instead derive protocol and method
6344
+ * headers from the message, plus the name header only for `tools/call` — carried in the
6345
+ * protocol's Base64 sentinel form whenever the tool name cannot ride as plain ASCII.
6346
+ * Before initialize returns, neither captured legacy header is sent.
6347
+ * `close()` clears the captured protocol so a reconnect's `initialize`
6348
+ * POST is headerless; the captured `session` persists across `close()`.
6349
+ * - **`close()` releases what is in flight.** Every `fetch` this transport still has open is
6350
+ * aborted, which cancels the response body a `send` is reading — an SSE reply the server
6351
+ * never ends would otherwise outlive the transport, with nothing left able to reach it. The
6352
+ * aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
6353
+ * idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
6354
+ * - **Total at the boundary, and a non-success reply rejects.** Every reply is narrowed
6355
+ * (`parseJSONRPCMessage`, the SSE decoder). A non-message success reply is dropped, never
6356
+ * asserted. A non-success reply that carries no valid JSON-RPC message rejects `send` with
6357
+ * an error naming its HTTP status and body shape — the peer answered, and answering the
6358
+ * caller's request with silence would leave it waiting out its own deadline for a failure
6359
+ * the transport already read. A valid JSON-RPC error body is emitted at any HTTP status,
6360
+ * because the protocol carries that outcome in band. A `fetch` or decode failure on a
6361
+ * success response surfaces on the `error` event rather than escaping `send`.
6362
+ * - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); fires
6363
+ * `message` per decoded reply, `error` on a fault, and `close` on `close()`.
6364
+ *
6365
+ * @example
6366
+ * ```ts
6367
+ * const transport = new HTTPClientTransport({ url: 'http://localhost:3000/mcp' })
6368
+ * const client = new MCPClient({ transport })
6369
+ * await client.connect()
6370
+ * ```
6371
+ */
6372
+ var HTTPClientTransport = class {
6373
+ #emitter;
6374
+ #url;
6375
+ #headers;
6376
+ #fetch;
6377
+ #timeout;
6378
+ #pending = /* @__PURE__ */ new Set();
6379
+ #parameters = /* @__PURE__ */ new Map();
6380
+ #stamps = /* @__PURE__ */ new WeakMap();
6381
+ #session = void 0;
6382
+ #protocol = void 0;
6383
+ #generation = 0;
6384
+ #closed = false;
6385
+ constructor(options) {
6386
+ this.#emitter = new _orkestrel_emitter.Emitter();
6387
+ this.#url = options.url;
6388
+ this.#headers = options.headers ?? {};
6389
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
6390
+ this.#timeout = options.timeout;
6391
+ }
6392
+ get emitter() {
6393
+ return this.#emitter;
6394
+ }
6395
+ get session() {
6396
+ return this.#session;
6397
+ }
6398
+ get duplex() {
6399
+ return false;
6400
+ }
6401
+ async start() {
6402
+ this.#closed = false;
6403
+ }
6404
+ async send(message) {
6405
+ this.#stamp(message);
6406
+ const request = new AbortController();
6407
+ this.#pending.add(request);
6408
+ try {
6409
+ await this.#exchange(message, request.signal);
6410
+ } finally {
6411
+ this.#pending.delete(request);
6412
+ }
6413
+ }
6414
+ async close() {
6415
+ if (this.#closed) return;
6416
+ this.#closed = true;
6417
+ for (const request of this.#pending) request.abort();
6418
+ this.#pending.clear();
6419
+ this.#protocol = void 0;
6420
+ this.#emitter.emit("close");
6421
+ }
6422
+ #stamp(message) {
6423
+ if (!isModernRequest(message) || message.method !== "tools/list") return;
6424
+ if (message.params?.["cursor"] === void 0) this.#generation += 1;
6425
+ this.#stamps.set(message, this.#generation);
6426
+ }
6427
+ async #exchange(message, signal) {
6428
+ let response;
6429
+ try {
6430
+ response = await this.#fetch(this.#url, {
6431
+ method: "POST",
6432
+ headers: {
6433
+ "content-type": "application/json",
6434
+ accept: "application/json, text/event-stream",
6435
+ ...this.#session === void 0 ? {} : { [MCP_SESSION_HEADER]: this.#session },
6436
+ ...this.#buildHeaders(message),
6437
+ ...this.#headers
6438
+ },
6439
+ body: JSON.stringify(message),
6440
+ signal: this.#timeout === void 0 ? signal : AbortSignal.any([signal, AbortSignal.timeout(this.#timeout)])
6441
+ });
6442
+ } catch (error) {
6443
+ this.#emitter.emit("error", error);
6444
+ return;
6445
+ }
6446
+ const session = response.headers.get(MCP_SESSION_HEADER);
6447
+ if (session !== null) this.#session = session;
6448
+ await this.#deliver(response, message);
6449
+ }
6450
+ #buildHeaders(message) {
6451
+ if (isModernRequest(message)) {
6452
+ const version = inferRequestVersion(message);
6453
+ const name = message.params?.["name"];
6454
+ return {
6455
+ ...version === void 0 ? {} : { [MCP_PROTOCOL_VERSION_HEADER]: version },
6456
+ [MCP_METHOD_HEADER]: message.method,
6457
+ ...message.method === "tools/call" && (0, _orkestrel_contract.isString)(name) ? {
6458
+ [MCP_NAME_HEADER]: encodeSentinel(name),
6459
+ ...buildHeaderProjection(this.#parameters.get(name) ?? [], message.params?.["arguments"])
6460
+ } : {}
6461
+ };
6462
+ }
6463
+ return this.#protocol === void 0 ? {} : { [MCP_PROTOCOL_VERSION_HEADER]: this.#protocol };
6464
+ }
6465
+ async #deliver(response, sent) {
6466
+ if (response.status === 202) return;
6467
+ const type = response.headers.get("content-type") ?? "";
6468
+ let messages = [];
6469
+ let failure;
6470
+ try {
6471
+ if (type.includes("text/event-stream")) messages = await readEventStream(response);
6472
+ else if (type.includes("application/json")) {
6473
+ const message = parseJSONRPCMessage(await response.json());
6474
+ if (message !== void 0) messages = [message];
6475
+ }
6476
+ } catch (error) {
6477
+ failure = { error };
6478
+ }
6479
+ for (const message of messages) this.#capture(message, sent);
6480
+ if (!response.ok && messages.length === 0) throw buildResponseError(response, type);
6481
+ if (failure !== void 0) this.#emitter.emit("error", failure.error);
6482
+ }
6483
+ #capture(message, sent) {
6484
+ if (isJSONRPCResponse(message) && (0, _orkestrel_contract.isRecord)(message.result) && isMCPVersion(message.result["protocolVersion"])) this.#protocol = message.result["protocolVersion"];
6485
+ this.#emitter.emit("message", this.#select(message, sent));
6486
+ }
6487
+ #select(message, sent) {
6488
+ if (!isModernRequest(sent) || sent.method !== "tools/list") return message;
6489
+ if (!isJSONRPCResponse(message) || message.error !== void 0) return message;
6490
+ const result = message.result;
6491
+ const listed = (0, _orkestrel_contract.isRecord)(result) ? result["tools"] : void 0;
6492
+ if (!(0, _orkestrel_contract.isRecord)(result) || !(0, _orkestrel_contract.isArray)(listed)) return message;
6493
+ const current = this.#stamps.get(sent) === this.#generation;
6494
+ if (current && sent.params?.["cursor"] === void 0) this.#parameters.clear();
6495
+ const kept = [];
6496
+ for (const tool of listed) {
6497
+ if (!(0, _orkestrel_contract.isRecord)(tool) || !(0, _orkestrel_contract.isString)(tool["name"])) {
6498
+ kept.push(tool);
6499
+ continue;
6500
+ }
6501
+ const parameters = buildHeaderParameters(tool["inputSchema"]);
6502
+ if (parameters === void 0) {
6503
+ this.#emitter.emit("error", /* @__PURE__ */ new Error(`MCP tool '${tool["name"]}' is excluded from tools/list: its inputSchema carries an invalid x-mcp-header annotation`));
6504
+ continue;
6505
+ }
6506
+ if (current) this.#parameters.set(tool["name"], parameters);
6507
+ kept.push(tool);
6508
+ }
6509
+ return {
6510
+ ...message,
6511
+ result: {
6512
+ ...result,
6513
+ tools: kept
6514
+ }
6515
+ };
6516
+ }
6517
+ };
6518
+ //#endregion
6075
6519
  //#region src/core/factories.ts
6076
6520
  /**
6077
6521
  * Creates a transport-agnostic Model Context Protocol server — exposes a live
6078
6522
  * {@link import('@orkestrel/tool').ToolManagerInterface} and an optional
6079
6523
  * {@link import('./types.js').MCPResourceManagerInterface},
6080
6524
  * {@link import('./types.js').MCPPromptManagerInterface}, and
6081
- * {@link import('./types.js').MCPCompletionManagerInterface} over JSON-RPC 2.0.
6525
+ * {@link import('./types.js').MCPCompletionInterface} over JSON-RPC 2.0.
6082
6526
  *
6083
6527
  * @remarks
6084
6528
  * Pump raw message strings through `handle` (parse → dispatch → serialize) from a
@@ -6097,20 +6541,35 @@ var MCPClient = class {
6097
6541
  * {@link import('@orkestrel/emitter').EmitterHooks} (see {@link MCPServerOptions})
6098
6542
  * @returns A working {@link MCPServerInterface}
6099
6543
  *
6100
- * @example
6544
+ * @example Expose a tool registry over MCP
6101
6545
  * ```ts
6102
6546
  * import { createMCPServer } from '@orkestrel/mcp'
6103
6547
  * import { createTool, createToolManager } from '@orkestrel/tool'
6104
6548
  *
6105
6549
  * const tools = createToolManager()
6550
+ * tools.add(
6551
+ * createTool({
6552
+ * name: 'search',
6553
+ * description: 'Search the docs',
6554
+ * execute: (a) => find(String(a.query)),
6555
+ * }),
6556
+ * )
6106
6557
  * tools.add(createTool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
6107
6558
  *
6108
- * const server = createMCPServer({ identity: { name: 'calculator', version: '1.0.0' }, tools })
6559
+ * const server = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools })
6109
6560
  * server.emitter.on('request', (method, id) => log(method, id))
6110
6561
  *
6111
- * // A transport pumps message strings through `handle`:
6112
- * const reply = await server.handle('{"jsonrpc":"2.0","method":"tools/list","id":1,"params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}')
6113
- * // reply '{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"add","inputSchema":{"type":"object"}}],"resultType":"complete","ttlMs":60000,"cacheScope":"private","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"calculator","version":"1.0.0"}}}}'
6562
+ * // A transport reads a framed message string and writes the reply:
6563
+ * for await (const message of transport) {
6564
+ * const reply = await server.handle(message)
6565
+ * if (reply !== undefined) await transport.send(reply) // a notification has no reply
6566
+ * }
6567
+ *
6568
+ * // `handle` also answers one message string on its own:
6569
+ * const listed = await server.handle(
6570
+ * '{"jsonrpc":"2.0","method":"tools/list","id":1,"params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}',
6571
+ * )
6572
+ * // listed → '{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"search","inputSchema":{"type":"object"},"description":"Search the docs"},{"name":"add","inputSchema":{"type":"object"}}],"resultType":"complete","ttlMs":60000,"cacheScope":"private","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"docs","version":"1.0.0"}}}}'
6114
6573
  * ```
6115
6574
  */
6116
6575
  function createMCPServer(options) {
@@ -6119,6 +6578,10 @@ function createMCPServer(options) {
6119
6578
  /**
6120
6579
  * Decorates one MCP server with the fixed legacy method translation.
6121
6580
  *
6581
+ * @remarks
6582
+ * Adds support for the `2025-11-25` and `2025-06-18` legacy revisions. Removing this one
6583
+ * decorator removes that legacy surface while leaving the modern dispatcher unchanged.
6584
+ *
6122
6585
  * @param server - The sole modern dispatcher and handshake identity source
6123
6586
  * @returns A dispatcher accepting both modern and legacy invocations
6124
6587
  */
@@ -6129,14 +6592,14 @@ function createMCPLegacy(server) {
6129
6592
  });
6130
6593
  }
6131
6594
  /**
6132
- * Creates a transport-agnostic Model Context Protocol CLIENT — connects to a REMOTE
6133
- * MCP server over an injected {@link import('./types.js').MCPClientTransportInterface},
6595
+ * Creates a transport-agnostic Model Context Protocol client — connects to a remote
6596
+ * MCP server over an injected {@link import('./types.js').MCPMessageTransportInterface},
6134
6597
  * negotiates the modern revision through `server/discover`, and exposes the server's tools as local
6135
6598
  * {@link import('@orkestrel/tool').ToolInterface}s an agent can run.
6136
6599
  *
6137
6600
  * @remarks
6138
6601
  * The egress mirror of {@link createMCPServer}: where the server exposes a local tool
6139
- * registry over MCP, the client USES a remote server's tools. `connect()` discovers,
6602
+ * registry over MCP, the client uses a remote server's tools. `connect()` discovers,
6140
6603
  * validates, and exposes the negotiated modern protocol; a legacy peer requires
6141
6604
  * {@link createMCPLegacyClientTransport}. `tools()` lists + wraps the remote
6142
6605
  * tools (each `execute` calls back over the wire),
@@ -6146,7 +6609,7 @@ function createMCPLegacy(server) {
6146
6609
  * `fetch`) lives in the published server environment; the client itself is provider-agnostic. Subscribe
6147
6610
  * to `connect` / `disconnect` / `notification` through `client.emitter.on(...)`.
6148
6611
  *
6149
- * @param options - `transport` (the carrier; REQUIRED), an optional `identity`
6612
+ * @param options - `transport` (the carrier; required), an optional `identity`
6150
6613
  * (the client identity), `timeout` (the per-request deadline), and the reserved `on`
6151
6614
  * {@link import('@orkestrel/emitter').EmitterHooks} (see {@link MCPClientOptions})
6152
6615
  * @returns A working {@link MCPClientInterface}
@@ -6161,7 +6624,7 @@ function createMCPLegacy(server) {
6161
6624
  * })
6162
6625
  * await client.connect()
6163
6626
  * agent.context.tools.add(await client.tools()) // give the agent the remote tools
6164
- * const value = await client.call('search', { query: 'mcp' })
6627
+ * const outcome = await client.call('search', { query: 'mcp' })
6165
6628
  * ```
6166
6629
  */
6167
6630
  function createMCPClient(options) {
@@ -6187,12 +6650,12 @@ function createMCPLegacyClientTransport(transport, options) {
6187
6650
  }
6188
6651
  /**
6189
6652
  * Adapts an {@link MCPTransportInterface} (the environment-agnostic duplex message
6190
- * channel) into a {@link MCPClientTransportInterface} — the additive bridge that lets
6653
+ * channel) into a {@link MCPMessageTransportInterface} — the additive bridge that lets
6191
6654
  * `createMCPClient` run over the new port without any change to `MCPClient`'s
6192
6655
  * existing shape.
6193
6656
  *
6194
6657
  * @remarks
6195
- * Hand the RESULT to `createMCPClient({ transport })`, then pass the SAME
6658
+ * Hand the result to `createMCPClient({ transport })`, then pass the same
6196
6659
  * `transport` to {@link import('./helpers.js').bindClient} to complete the inbound
6197
6660
  * wiring: `send` serializes each outbound {@link JSONRPCMessage} and writes it through
6198
6661
  * `transport.send`; `close` closes the underlying
@@ -6200,17 +6663,17 @@ function createMCPLegacyClientTransport(transport, options) {
6200
6663
  * it is handed in — there is no separate connect step at this layer); `session` is
6201
6664
  * always `undefined` (session correlation is a higher-level concern the duplex port
6202
6665
  * does not carry); and `duplex` is always `true`, because carrying frames in both
6203
- * directions at any moment is exactly what the adapted port is — a claim DRIVEN over a real
6666
+ * directions at any moment is exactly what the adapted port is — a claim driven over a real
6204
6667
  * `MessageChannel` and a real scope pair (a client-initiated `notifications/cancelled`
6205
6668
  * observed arriving at the peer) rather than read back off this literal. The literal is
6206
- * true of the PORT, and stays true only while the port has a peer: close the far half and
6669
+ * true of the port, and stays true only while the port has a peer: close the far half and
6207
6670
  * this transport still declares `true` while carrying nothing, which is the one thing a
6208
6671
  * per-carrier declaration cannot express. Inbound delivery (`emitter`'s `message` / `close` events) is
6209
6672
  * `bindClient`'s job, not this factory's — the returned object exposes a `message`-
6210
6673
  * capable emitter for `bindClient` to push onto.
6211
6674
  *
6212
6675
  * @param transport - The duplex channel to adapt
6213
- * @returns A {@link MCPClientTransportInterface} `createMCPClient` can drive
6676
+ * @returns A {@link MCPMessageTransportInterface} `createMCPClient` can drive
6214
6677
  *
6215
6678
  * @example
6216
6679
  * ```ts
@@ -6241,6 +6704,7 @@ exports.DEFAULT_MCP_LIMITS = DEFAULT_MCP_LIMITS;
6241
6704
  exports.DEFAULT_MCP_REQUEST_TIMEOUT = DEFAULT_MCP_REQUEST_TIMEOUT;
6242
6705
  exports.DEFAULT_MCP_SUBSCRIPTION_CAPACITY = DEFAULT_MCP_SUBSCRIPTION_CAPACITY;
6243
6706
  exports.EMPTY_MCP_ARGUMENTS = EMPTY_MCP_ARGUMENTS;
6707
+ exports.HTTPClientTransport = HTTPClientTransport;
6244
6708
  exports.JSONRPC_INTERNAL_ERROR = JSONRPC_INTERNAL_ERROR;
6245
6709
  exports.JSONRPC_INVALID_PARAMS = JSONRPC_INVALID_PARAMS;
6246
6710
  exports.JSONRPC_INVALID_REQUEST = JSONRPC_INVALID_REQUEST;
@@ -6268,12 +6732,17 @@ exports.MCP_META_CLIENT = MCP_META_CLIENT;
6268
6732
  exports.MCP_META_SERVER = MCP_META_SERVER;
6269
6733
  exports.MCP_META_SUBSCRIPTION = MCP_META_SUBSCRIPTION;
6270
6734
  exports.MCP_META_VERSION = MCP_META_VERSION;
6735
+ exports.MCP_METHOD_HEADER = MCP_METHOD_HEADER;
6271
6736
  exports.MCP_MISSING_CAPABILITY = MCP_MISSING_CAPABILITY;
6272
6737
  exports.MCP_MODERN_VERSION = MCP_MODERN_VERSION;
6738
+ exports.MCP_NAME_HEADER = MCP_NAME_HEADER;
6273
6739
  exports.MCP_PARAM_PREFIX = MCP_PARAM_PREFIX;
6740
+ exports.MCP_PROTOCOL_VERSION_HEADER = MCP_PROTOCOL_VERSION_HEADER;
6274
6741
  exports.MCP_SENTINEL_PREFIX = MCP_SENTINEL_PREFIX;
6275
6742
  exports.MCP_SENTINEL_SUFFIX = MCP_SENTINEL_SUFFIX;
6743
+ exports.MCP_SESSION_HEADER = MCP_SESSION_HEADER;
6276
6744
  exports.MCP_UNSUPPORTED_VERSION = MCP_UNSUPPORTED_VERSION;
6745
+ exports.MCP_WEBSOCKET_SUBPROTOCOL = MCP_WEBSOCKET_SUBPROTOCOL;
6277
6746
  exports.SUPPORTED_LEGACY_PROTOCOL_VERSIONS = SUPPORTED_LEGACY_PROTOCOL_VERSIONS;
6278
6747
  exports.SUPPORTED_MCP_VERSIONS = SUPPORTED_MCP_VERSIONS;
6279
6748
  exports.SUPPORTED_MODERN_PROTOCOL_VERSIONS = SUPPORTED_MODERN_PROTOCOL_VERSIONS;
@@ -6290,6 +6759,7 @@ exports.buildJSONRPCResult = buildJSONRPCResult;
6290
6759
  exports.buildMethodOptions = buildMethodOptions;
6291
6760
  exports.buildModernResult = buildModernResult;
6292
6761
  exports.buildProgressNotification = buildProgressNotification;
6762
+ exports.buildResponseError = buildResponseError;
6293
6763
  exports.buildSubscriptionAcknowledgement = buildSubscriptionAcknowledgement;
6294
6764
  exports.buildSubscriptionFilter = buildSubscriptionFilter;
6295
6765
  exports.buildSubscriptionResult = buildSubscriptionResult;
@@ -6303,13 +6773,16 @@ exports.createMCPLegacy = createMCPLegacy;
6303
6773
  exports.createMCPLegacyClientTransport = createMCPLegacyClientTransport;
6304
6774
  exports.createMCPServer = createMCPServer;
6305
6775
  exports.decodeBoundedMessage = decodeBoundedMessage;
6776
+ exports.decodeEvent = decodeEvent;
6306
6777
  exports.decodeSentinel = decodeSentinel;
6778
+ exports.deliverMessage = deliverMessage;
6307
6779
  exports.digestJSON = digestJSON;
6308
6780
  exports.encodeSentinel = encodeSentinel;
6309
6781
  exports.extractContentText = extractContentText;
6310
6782
  exports.extractHeaderAnnotations = extractHeaderAnnotations;
6311
6783
  exports.extractToolSchema = extractToolSchema;
6312
6784
  exports.inferEra = inferEra;
6785
+ exports.inferRequestEra = inferRequestEra;
6313
6786
  exports.inferRequestVersion = inferRequestVersion;
6314
6787
  exports.inferVersion = inferVersion;
6315
6788
  exports.isAbsoluteURI = isAbsoluteURI;
@@ -6317,7 +6790,6 @@ exports.isBoundedJSON = isBoundedJSON;
6317
6790
  exports.isBoundedString = isBoundedString;
6318
6791
  exports.isElicitContent = isElicitContent;
6319
6792
  exports.isFieldToken = isFieldToken;
6320
- exports.isFormElicitationSupported = isFormElicitationSupported;
6321
6793
  exports.isInitializeRequest = isInitializeRequest;
6322
6794
  exports.isJSONObject = isJSONObject;
6323
6795
  exports.isJSONRPCError = isJSONRPCError;
@@ -6392,7 +6864,6 @@ exports.isModernRequest = isModernRequest;
6392
6864
  exports.isRFC3339Date = isRFC3339Date;
6393
6865
  exports.isRFC3339DateTime = isRFC3339DateTime;
6394
6866
  exports.isStandardBase64 = isStandardBase64;
6395
- exports.isTaskSupported = isTaskSupported;
6396
6867
  exports.legacyInvocationToModern = legacyInvocationToModern;
6397
6868
  exports.legacyResultToModern = legacyResultToModern;
6398
6869
  exports.matchesResultType = matchesResultType;
@@ -6403,11 +6874,14 @@ exports.parseJSONRPCMessage = parseJSONRPCMessage;
6403
6874
  exports.parseMCPInputState = parseMCPInputState;
6404
6875
  exports.parseRequestContext = parseRequestContext;
6405
6876
  exports.readCancelledId = readCancelledId;
6877
+ exports.readEventStream = readEventStream;
6406
6878
  exports.renderHeaderValue = renderHeaderValue;
6407
6879
  exports.sendStream = sendStream;
6408
6880
  exports.serializeJSON = serializeJSON;
6409
6881
  exports.snapshotJSON = snapshotJSON;
6410
6882
  exports.snapshotToolResult = snapshotToolResult;
6411
6883
  exports.stampSubscriptionNotification = stampSubscriptionNotification;
6884
+ exports.supportsFormElicitation = supportsFormElicitation;
6885
+ exports.supportsTask = supportsTask;
6412
6886
 
6413
6887
  //# sourceMappingURL=index.cjs.map