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