@orkestrel/mcp 0.0.19 → 0.0.21

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.
@@ -57,11 +57,11 @@ var MCP_HEADER_MISMATCH = -32020;
57
57
  *
58
58
  * @remarks
59
59
  * The GENERIC code for the whole condition, not one capability's code. This server answers
60
- * it in two places — a `tools/call` that needs `elicitation`, and a `tasks/*` request whose
61
- * client never declared `io.modelcontextprotocol/tasks` — and the two are told apart by
60
+ * it in more than one place — a `tools/call` that needs `elicitation`, and a `tasks/*` request
61
+ * whose client never declared `io.modelcontextprotocol/tasks` — and they are told apart by
62
62
  * `error.data.requiredCapabilities` alone (`{ elicitation: {} }` against
63
- * `{ extensions: { 'io.modelcontextprotocol/tasks': {} } }`). They are two instances of one
64
- * condition, so a second numeral would describe the same fact twice. The Tasks extension's
63
+ * `{ extensions: { 'io.modelcontextprotocol/tasks': {} } }`). They are instances of the same
64
+ * condition, so a separate numeral would describe the same fact twice. The Tasks extension's
65
65
  * own draft prose still shows `-32003` in examples; the dated core schema fixes this code,
66
66
  * and the dated schema is what a peer implements against.
67
67
  */
@@ -148,20 +148,18 @@ var DEFAULT_MCP_CLIENT_VERSION = "1.0.0";
148
148
  * is unset — a request the remote server does not answer within it rejects.
149
149
  */
150
150
  var DEFAULT_MCP_REQUEST_TIMEOUT = 3e4;
151
- /** The maximum discovery-probe deadline used when a client deadline is configured. */
152
- var DEFAULT_MCP_PROBE_TIMEOUT = 50;
153
151
  //#endregion
154
152
  //#region src/core/errors.ts
155
153
  /**
156
- * A remote Model Context Protocol JSON-RPC error, preserving its machine-readable
157
- * numeric code and optional structured context.
154
+ * A Model Context Protocol error preserving its machine-readable numeric code and
155
+ * optional structured context.
158
156
  *
159
157
  * @remarks
160
- * {@link MCPClient} throws this error only for a remote JSON-RPC `error` response.
161
- * Local lifecycle and transport conditions such as disconnects and request timeouts
162
- * remain plain `Error`s. `context` carries the response's optional `error.data`
163
- * unchanged and is `undefined` when the peer omitted it. This includes the modern
164
- * reserved paths: `-32020` carries no context, `-32021` may carry
158
+ * {@link MCPClient} throws this error for a remote JSON-RPC `error` response and for a
159
+ * locally detected protocol incompatibility. Local lifecycle and transport conditions such
160
+ * as disconnects and request timeouts remain plain `Error`s. For a remote response, `context`
161
+ * carries the optional `error.data` unchanged and is `undefined` when the peer omitted it.
162
+ * This includes the modern reserved paths: `-32020` carries no context, `-32021` may carry
165
163
  * `requiredCapabilities`, and `-32022` carries the peer's `supported` revisions and
166
164
  * `requested` revision for negotiation recovery.
167
165
  *
@@ -180,7 +178,7 @@ var MCPError = class extends Error {
180
178
  code;
181
179
  context;
182
180
  /**
183
- * Create a remote MCP protocol error.
181
+ * Creates an MCP protocol error.
184
182
  *
185
183
  * @param message - The human-readable JSON-RPC error message
186
184
  * @param code - The machine-readable numeric JSON-RPC error code
@@ -193,7 +191,7 @@ var MCPError = class extends Error {
193
191
  }
194
192
  };
195
193
  /**
196
- * Determine whether an unknown value is an {@link MCPError}.
194
+ * Determines whether an unknown value is an {@link MCPError}.
197
195
  *
198
196
  * @param value - The unknown value to inspect
199
197
  * @returns `true` only when the value is an `MCPError`
@@ -214,7 +212,7 @@ function isMCPError(value) {
214
212
  //#endregion
215
213
  //#region src/core/inferers.ts
216
214
  /**
217
- * Infer the wire era for an MCP protocol revision.
215
+ * Infers the wire era for an MCP protocol revision.
218
216
  *
219
217
  * @param version - The protocol revision to classify
220
218
  * @returns `'modern'` for `2026-07-28`, `'legacy'` for either supported legacy
@@ -229,7 +227,7 @@ function inferEra(version) {
229
227
  }
230
228
  }
231
229
  /**
232
- * Infer the newest supported protocol revision present in a peer's offer.
230
+ * Infers the newest supported protocol revision present in a peer's offer.
233
231
  *
234
232
  * @param offered - The protocol revisions offered by the peer
235
233
  * @returns The newest locally supported offered revision, or `undefined`
@@ -238,12 +236,12 @@ function inferVersion(offered) {
238
236
  for (const version of SUPPORTED_PROTOCOL_VERSIONS) if (offered.includes(version)) return version;
239
237
  }
240
238
  /**
241
- * Infer the protocol version an outbound message announces itself with — the ONE
239
+ * Infers the protocol version an outbound message announces itself with — the ONE
242
240
  * projection every HTTP client transport stamps `mcp-protocol-version` from.
243
241
  *
244
242
  * @remarks
245
243
  * This is deliberately the SAME read the server's own expectation performs
246
- * ({@link import('@src/server').inferHeaderIssue}): a modern request's reserved
244
+ * ({@link import('@orkestrel/mcp/server').inferHeaderIssue}): a modern request's reserved
247
245
  * `_meta` version, accepted whenever it is a string. It is NOT
248
246
  * {@link import('./parsers.js').parseRequestContext}, and the difference is the whole
249
247
  * point. That parser answers a different question — is the modern metadata WELL FORMED —
@@ -256,7 +254,7 @@ function inferVersion(offered) {
256
254
  * `initialize` handshake the transport captured, not from the message.
257
255
  *
258
256
  * Header NAMES stay with the transports that own the wire (see `constants.ts`); core owns
259
- * the value this projection derives, which is the part the two faces disagreed about.
257
+ * the value this projection derives, which is the part the browser and Node faces disagreed about.
260
258
  *
261
259
  * @param message - The outbound message about to be written
262
260
  * @returns The version to announce, or `undefined` when the message announces none
@@ -274,7 +272,7 @@ function inferRequestVersion(message) {
274
272
  //#endregion
275
273
  //#region src/core/cloners.ts
276
274
  /**
277
- * Snapshot one bounded exact JSON value together with its canonical wire serialization.
275
+ * Snapshots one bounded exact JSON value together with its canonical wire serialization.
278
276
  *
279
277
  * The returned value is an owned, deeply frozen graph reconstructed from the canonical text;
280
278
  * the frozen tuple shares no mutable structure with the input. Invalid exact-JSON shapes,
@@ -304,10 +302,10 @@ function snapshotJSON(value, limits) {
304
302
  return Object.freeze(snapshot);
305
303
  }
306
304
  /**
307
- * Snapshot one exact Tool result and the canonical wire text of a defined success value.
305
+ * Snapshots one exact Tool result and the canonical wire text of a defined success value.
308
306
  *
309
- * A success must have exactly four own enumerable data properties: `id`, `name`, `success: true`,
310
- * and `value`. A failure must instead have exactly `id`, `name`, `success: false`, and a string
307
+ * A success must have exactly the own enumerable data properties `id`, `name`,
308
+ * `success: true`, and `value`. A failure must instead have exactly `id`, `name`, `success: false`, and a string
311
309
  * `error`. The returned result and tuple are frozen. Only a defined success value crosses the
312
310
  * bounded JSON ownership seam; it becomes an owned deeply frozen value and receives canonical
313
311
  * text. Value-less successes and failures pair with `undefined` text. Non-records, symbol keys,
@@ -382,11 +380,11 @@ function snapshotToolResult(value, limits) {
382
380
  //#endregion
383
381
  //#region src/core/parsers.ts
384
382
  /**
385
- * Narrow an already-parsed value to a {@link JSONRPCMessage}, or `undefined` when
383
+ * Narrows an already-parsed value to a {@link JSONRPCMessage}, or `undefined` when
386
384
  * it is not one.
387
385
  *
388
386
  * @remarks
389
- * Total (§14) — a non-message returns `undefined`, never throws. The input must
387
+ * Total — a non-message returns `undefined`, never throws. The input must
390
388
  * ALREADY be `JSON.parse`d: the raw-string parse (which can throw on malformed
391
389
  * JSON) happens in `MCPServer.handle` inside a try/catch that maps a parse failure
392
390
  * to a `-32700` response.
@@ -400,7 +398,7 @@ function snapshotToolResult(value, limits) {
400
398
  * result. Identity is not preserved and is not promised.
401
399
  *
402
400
  * The parser's sound partner is the COMPOSITE `isJSONRPCMessage(value) &&
403
- * isBoundedJSON(value, limits)`, and against it both halves of the §15 soundness law
401
+ * isBoundedJSON(value, limits)`, and against it both halves of the soundness law
404
402
  * hold by construction:
405
403
  *
406
404
  * - Every non-`undefined` result satisfies {@link isJSONRPCMessage}, because the guard
@@ -436,7 +434,7 @@ function parseJSONRPCMessage(value, limits = {
436
434
  return snapshot !== void 0 && isJSONRPCMessage(snapshot[0]) ? snapshot[0] : void 0;
437
435
  }
438
436
  /**
439
- * Parse the reserved modern request metadata into an {@link MCPRequestContext}.
437
+ * Parses the reserved modern request metadata into an {@link MCPRequestContext}.
440
438
  *
441
439
  * @remarks
442
440
  * This is the validity step after {@link isModernRequest}: a defined result can
@@ -486,7 +484,7 @@ function parseRequestContext(value, limits = {
486
484
  }
487
485
  }
488
486
  /**
489
- * Parse the opened value carried by an opaque `requestState` continuation.
487
+ * Parses the opened value carried by an opaque `requestState` continuation.
490
488
  *
491
489
  * @remarks
492
490
  * This parser does not open the opaque continuation carrier; the configured
@@ -544,7 +542,69 @@ function parseMCPInputState(value) {
544
542
  //#endregion
545
543
  //#region src/core/helpers.ts
546
544
  /**
547
- * Deterministically serialize one exact JSON value within explicit bounds.
545
+ * Determines whether a client capability record declares form-mode elicitation.
546
+ *
547
+ * @remarks
548
+ * The protocol's empty `elicitation` object is the implicit form-only declaration.
549
+ * A non-empty declaration must carry a record-valued `form` member; URL-only support
550
+ * does not authorize a form request. Total over hostile input.
551
+ *
552
+ * @param value - The client capability record to inspect
553
+ * @returns `true` when form-mode elicitation is declared
554
+ *
555
+ * @example
556
+ * ```ts
557
+ * isFormElicitationSupported({ elicitation: {} }) // true — implicit form mode
558
+ * isFormElicitationSupported({ elicitation: { url: {} } }) // false
559
+ * ```
560
+ */
561
+ function isFormElicitationSupported(value) {
562
+ const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
563
+ if (!owned.success) return false;
564
+ try {
565
+ const elicitation = owned.value["elicitation"];
566
+ if (!(0, _orkestrel_contract.isRecord)(elicitation)) return false;
567
+ if ((0, _orkestrel_contract.isRecord)(elicitation["form"])) return true;
568
+ return Object.keys(elicitation).length === 0;
569
+ } catch {
570
+ return false;
571
+ }
572
+ }
573
+ /**
574
+ * Determines whether a client capability record declares the draft Tasks extension.
575
+ *
576
+ * @remarks
577
+ * The declaration lives at `extensions['io.modelcontextprotocol/tasks']` and its value is
578
+ * an empty object, so this is a PRESENCE check: the extension defines no options, and a
579
+ * server that read one would be reading a field no client can meaningfully set. The value
580
+ * must still be a record, because that is the shape the capability record declares — a
581
+ * `true` or a string there is a client speaking a different protocol, not a shorthand.
582
+ *
583
+ * A client declares this PER REQUEST. Nothing here consults a session, because the modern
584
+ * revision is stateless and a capability declared once at connect time says nothing about
585
+ * the request in hand. Total over hostile input.
586
+ *
587
+ * @param value - The client capability record to inspect
588
+ * @returns `true` when the tasks extension is declared
589
+ *
590
+ * @example
591
+ * ```ts
592
+ * isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
593
+ * isTaskSupported({ extensions: {} }) // false — the key is the declaration
594
+ * ```
595
+ */
596
+ function isTaskSupported(value) {
597
+ const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
598
+ if (!owned.success) return false;
599
+ try {
600
+ const extensions = owned.value["extensions"];
601
+ return (0, _orkestrel_contract.isRecord)(extensions) && (0, _orkestrel_contract.isRecord)(extensions["io.modelcontextprotocol/tasks"]);
602
+ } catch {
603
+ return false;
604
+ }
605
+ }
606
+ /**
607
+ * Serializes one exact JSON value deterministically within explicit bounds.
548
608
  *
549
609
  * @param value - The unknown value to validate and serialize
550
610
  * @param limits - Serialized byte, key, and depth limits
@@ -763,7 +823,7 @@ function serializeJSON(value, limits) {
763
823
  return serialized.success ? serialized.value : void 0;
764
824
  }
765
825
  /**
766
- * Compute a lowercase host-neutral SHA-256 digest of one bounded canonical JSON value.
826
+ * Computes a lowercase host-neutral SHA-256 digest of one bounded canonical JSON value.
767
827
  *
768
828
  * @param value - The unknown value to validate and digest
769
829
  * @param limits - Serialized byte, key, and depth limits
@@ -776,7 +836,7 @@ async function digestJSON(value, limits) {
776
836
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
777
837
  }
778
838
  /**
779
- * Build one official progress notification for the original request stream.
839
+ * Builds one official progress notification for the original request stream.
780
840
  *
781
841
  * @param token - The request's original opaque progress token
782
842
  * @param progress - The finite progress payload
@@ -795,7 +855,7 @@ function buildProgressNotification(token, progress) {
795
855
  };
796
856
  }
797
857
  /**
798
- * Build one official cancellation notification for a request already sent.
858
+ * Builds one official cancellation notification for a request already sent.
799
859
  *
800
860
  * @remarks
801
861
  * `requestId` and `reason` are WIRE SPELLINGS carried verbatim from the dated schema's
@@ -833,15 +893,15 @@ function buildCancelledNotification(id, reason) {
833
893
  };
834
894
  }
835
895
  /**
836
- * Determine whether one method may answer with a given modern `resultType`.
896
+ * Determines whether one method may answer with a given modern `resultType`.
837
897
  *
838
898
  * @remarks
839
- * The dated protocol lets a `tools/call` answer three ways — it COMPLETED, it became a
899
+ * The dated protocol lets a `tools/call` answer in more than one way — it COMPLETED, it became a
840
900
  * durable task, or it needs another round trip — while every other method this client
841
901
  * issues has exactly one legal answer. So the arm a peer chose is only meaningful beside
842
902
  * the method it answers, and this is the one place that pairing is decided.
843
903
  *
844
- * The rule is deliberately a WHITELIST: an unrecognized `resultType` is refused for every
904
+ * The rule is deliberately closed: an unrecognized `resultType` is refused for every
845
905
  * method, including `tools/call`. A client that carried an arm it cannot name would hand
846
906
  * its caller a value whose meaning it invented.
847
907
  *
@@ -862,7 +922,7 @@ function matchesResultType(method, resultType) {
862
922
  return resultType === "task" || resultType === "input_required";
863
923
  }
864
924
  /**
865
- * Concatenate an MCP tool-call result's text content blocks into one string.
925
+ * Concatenates an MCP tool-call result's text content blocks into one string.
866
926
  *
867
927
  * @remarks
868
928
  * The inverse of a server splitting a value into text block(s), and TOTAL: a non-record
@@ -886,12 +946,12 @@ function extractContentText(result) {
886
946
  return parts.join("\n");
887
947
  }
888
948
  /**
889
- * Narrow one `tools/call` answer to the arm the peer chose.
949
+ * Narrows one `tools/call` answer to the arm the peer chose.
890
950
  *
891
951
  * @remarks
892
- * The three arms {@link matchesResultType} admits are the whole space this sees, because a
952
+ * The arms {@link matchesResultType} admits are the whole space this sees, because a
893
953
  * `resultType` the client cannot name is refused at correlation. What is left is validating
894
- * the two arms the protocol gives a shape to, and deriving the tool's value from the one it
954
+ * the arms the protocol gives a shape to, and deriving the tool's value from the one it
895
955
  * does not:
896
956
  *
897
957
  * - A peer's `structuredContent` is PREFERRED over the content blocks, because it is the
@@ -939,7 +999,7 @@ function buildCallOutcome(name, result) {
939
999
  });
940
1000
  }
941
1001
  /**
942
- * Build the canonical Tool call for one validated MCP `tools/call` request.
1002
+ * Builds the canonical Tool call for one validated MCP `tools/call` request.
943
1003
  *
944
1004
  * @param request - The original MCP request
945
1005
  * @param caller - Optional consumer-asserted caller context
@@ -958,7 +1018,7 @@ function buildToolCall(request, caller, args) {
958
1018
  };
959
1019
  }
960
1020
  /**
961
- * Build a JSON-RPC success {@link JSONRPCResultResponse} — the `id` echoed, the
1021
+ * Builds a JSON-RPC success {@link JSONRPCResultResponse} — the `id` echoed, the
962
1022
  * method's value as `result`.
963
1023
  *
964
1024
  * @remarks
@@ -977,7 +1037,7 @@ function buildJSONRPCResult(id, result) {
977
1037
  };
978
1038
  }
979
1039
  /**
980
- * Build a JSON-RPC error {@link JSONRPCErrorResponse} — the `id` echoed, the failure
1040
+ * Builds a JSON-RPC error {@link JSONRPCErrorResponse} — the `id` echoed, the failure
981
1041
  * as an `error` object.
982
1042
  *
983
1043
  * @remarks
@@ -1006,7 +1066,7 @@ function buildJSONRPCError(id, code, message, data) {
1006
1066
  };
1007
1067
  }
1008
1068
  /**
1009
- * Resolve the caller-facing dispatch options into the options a dispatched method
1069
+ * Resolves the caller-facing dispatch options into the options a dispatched method
1010
1070
  * receives.
1011
1071
  *
1012
1072
  * @remarks
@@ -1040,7 +1100,7 @@ function buildMethodOptions(options, lifetime) {
1040
1100
  };
1041
1101
  }
1042
1102
  /**
1043
- * Map a {@link ToolManagerInterface}'s definitions to MCP `tools/list` descriptors
1103
+ * Maps a {@link ToolManagerInterface}'s definitions to MCP `tools/list` descriptors
1044
1104
  * — renaming `parameters` to the wire's `inputSchema`.
1045
1105
  *
1046
1106
  * @remarks
@@ -1083,7 +1143,7 @@ function buildModernResult(result, identity, ttl, scope) {
1083
1143
  };
1084
1144
  }
1085
1145
  /**
1086
- * Intersect a requested subscription filter with the notification families a server supports.
1146
+ * Intersects a requested subscription filter with the notification families a server supports.
1087
1147
  *
1088
1148
  * @param requested - The notification families requested by the client
1089
1149
  * @param supported - The notification families the server can actually produce
@@ -1103,7 +1163,7 @@ function buildSubscriptionFilter(requested, supported) {
1103
1163
  };
1104
1164
  }
1105
1165
  /**
1106
- * Determine whether a produced notification belongs to an honoured subscription filter.
1166
+ * Determines whether a produced notification belongs to an honoured subscription filter.
1107
1167
  *
1108
1168
  * @param notification - The server notification offered by the configured producer
1109
1169
  * @param filter - The filter acknowledged to the client
@@ -1118,7 +1178,7 @@ function matchesSubscriptionNotification(notification, filter) {
1118
1178
  return typeof uri === "string" && filter.resourceSubscriptions?.includes(uri) === true;
1119
1179
  }
1120
1180
  /**
1121
- * Stamp a subscription notification with the request id reserved for its held-open stream.
1181
+ * Stamps a subscription notification with the request id reserved for its held-open stream.
1122
1182
  *
1123
1183
  * @param notification - The notification to copy and stamp
1124
1184
  * @param id - The `subscriptions/listen` request id
@@ -1139,7 +1199,7 @@ function stampSubscriptionNotification(notification, id) {
1139
1199
  };
1140
1200
  }
1141
1201
  /**
1142
- * Build the first notification carrying a subscription id for a listen request.
1202
+ * Builds the first notification carrying a subscription id for a listen request.
1143
1203
  *
1144
1204
  * @param notifications - The exact notification filter the server will honour
1145
1205
  * @param id - The `subscriptions/listen` request id
@@ -1153,7 +1213,7 @@ function buildSubscriptionAcknowledgement(notifications, id) {
1153
1213
  }, id);
1154
1214
  }
1155
1215
  /**
1156
- * Build the terminating response for a subscription source that closes gracefully.
1216
+ * Builds the terminating response for a subscription source that closes gracefully.
1157
1217
  *
1158
1218
  * @param id - The `subscriptions/listen` request id
1159
1219
  * @param identity - The server identity included by the modern result stamping site
@@ -1163,7 +1223,7 @@ function buildSubscriptionResult(id, identity) {
1163
1223
  return buildJSONRPCResult(id, buildModernResult({ _meta: { [MCP_META_SUBSCRIPTION]: id } }, identity));
1164
1224
  }
1165
1225
  /**
1166
- * Build the mandatory modern `server/discover` result.
1226
+ * Builds the mandatory modern `server/discover` result.
1167
1227
  *
1168
1228
  * @remarks
1169
1229
  * `capabilities.resources` and `capabilities.prompts` appear only for servers with their
@@ -1195,7 +1255,7 @@ function buildDiscoverResult(options) {
1195
1255
  }, options.identity, options.cache?.ttl ?? 6e4, options.cache?.scope);
1196
1256
  }
1197
1257
  /**
1198
- * Build the MCP `initialize` result — the negotiated protocol version, the
1258
+ * Builds the MCP `initialize` result — the negotiated protocol version, the
1199
1259
  * advertised capabilities, and the server identity.
1200
1260
  *
1201
1261
  * @remarks
@@ -1222,7 +1282,7 @@ function buildInitializeResult(name, version, requested) {
1222
1282
  };
1223
1283
  }
1224
1284
  /**
1225
- * Decode one raw inbound message within an explicit bound — the decode a binder performs
1285
+ * Decodes one raw inbound message within an explicit bound — the decode a binder performs
1226
1286
  * before it hands the string on.
1227
1287
  *
1228
1288
  * @remarks
@@ -1230,14 +1290,14 @@ function buildInitializeResult(name, version, requested) {
1230
1290
  * `JSON.parse`d at all: a decoder that parses before it measures has already spent the work
1231
1291
  * the bound exists to refuse. A message over the bound, malformed JSON, and a well-formed
1232
1292
  * value that is not a JSON-RPC message are one answer — `undefined` — because a binder does
1233
- * exactly the same thing with all three: nothing, and let
1293
+ * exactly the same thing with each of them: nothing, and let
1234
1294
  * {@link import('./types.js').MCPServerInterface.handle} produce the wire refusal from the
1235
1295
  * same bound.
1236
1296
  *
1237
- * Total (§14) — never throws, whatever the input.
1297
+ * Total — never throws, whatever the input.
1238
1298
  *
1239
1299
  * @param message - The raw inbound JSON-RPC message string
1240
- * @param limits - The byte and depth bounds to decode within (the server's own, via `limit`)
1300
+ * @param limits - The byte and depth bounds to decode within (the server's own, from `limit`)
1241
1301
  * @returns The decoded message, or `undefined` when it is over the bound or is not one
1242
1302
  *
1243
1303
  * @example
@@ -1251,7 +1311,7 @@ function decodeBoundedMessage(message, limits) {
1251
1311
  return parsed.success ? parseJSONRPCMessage(parsed.value, limits) : void 0;
1252
1312
  }
1253
1313
  /**
1254
- * Read the request id an inbound `notifications/cancelled` names — the inverse of
1314
+ * Reads the request id an inbound `notifications/cancelled` names — the inverse of
1255
1315
  * {@link buildCancelledNotification}.
1256
1316
  *
1257
1317
  * @remarks
@@ -1259,7 +1319,7 @@ function decodeBoundedMessage(message, limits) {
1259
1319
  * real {@link JSONRPCId}: `null` is not one, and neither is an absent member, so a
1260
1320
  * malformed frame reads as "cancels nothing" rather than as an error. Anything that is not a
1261
1321
  * `notifications/cancelled` notification — a response, a request that happens to use the
1262
- * method name, another notification — reads the same way. Total (§14).
1322
+ * method name, another notification — reads the same way. Total.
1263
1323
  *
1264
1324
  * @param message - The decoded inbound message to read
1265
1325
  * @returns The id of the request being cancelled, or `undefined` when the message cancels nothing
@@ -1275,7 +1335,7 @@ function readCancelledId(message) {
1275
1335
  return isJSONRPCId(requested) ? requested : void 0;
1276
1336
  }
1277
1337
  /**
1278
- * Pump a controlled serialized exchange onto a transport — every notification in order, then
1338
+ * Pumps a controlled serialized exchange onto a transport — every notification in order, then
1279
1339
  * the terminating response — and END the exchange however the pump leaves.
1280
1340
  *
1281
1341
  * @remarks
@@ -1320,9 +1380,9 @@ async function sendStream(stream, transport) {
1320
1380
  }
1321
1381
  }
1322
1382
  /**
1323
- * Pipe an {@link MCPTransportInterface} into an {@link MCPDispatcherInterface} — every
1383
+ * Pipes an {@link MCPTransportInterface} into an {@link MCPDispatcherInterface} — every
1324
1384
  * inbound message runs through `server.handle`, and a defined reply is written back
1325
- * via `transport.send`.
1385
+ * through `transport.send`.
1326
1386
  *
1327
1387
  * @remarks
1328
1388
  * `server.handle` already turns a malformed message into a serialized `-32700` /
@@ -1343,7 +1403,7 @@ async function sendStream(stream, transport) {
1343
1403
  * **This binder OWNS every exchange it starts, and ends each one on every exit.** It holds one
1344
1404
  * `AbortController` per live request, keyed by the request's id and deleted whenever that
1345
1405
  * request leaves — normally, by a throw, or by cancellation — and it supplies that signal to
1346
- * `handle` as {@link import('./types.js').MCPDispatchOptions}. Three consequences follow.
1406
+ * `handle` as {@link import('./types.js').MCPDispatchOptions}. These consequences follow.
1347
1407
  * An inbound `notifications/cancelled` ABORTS the request it names, which is how the message-
1348
1408
  * based cancellation path reaches a tool on the carriers that have one (stdio, WebSocket,
1349
1409
  * `MessagePort`); a cancelled request writes NO response, because a peer that asked for a call
@@ -1416,7 +1476,7 @@ function bindServer(server, transport) {
1416
1476
  };
1417
1477
  }
1418
1478
  /**
1419
- * Pipe an {@link MCPTransportInterface} into an {@link MCPClientInterface} — every
1479
+ * Pipes an {@link MCPTransportInterface} into an {@link MCPClientInterface} — every
1420
1480
  * inbound message is decoded and delivered onto the client's OWN transport
1421
1481
  * (`client.transport.emitter`'s `message` / `close` events), resolving/rejecting the
1422
1482
  * client's correlated pending requests exactly as a direct reply would.
@@ -1430,7 +1490,7 @@ function bindServer(server, transport) {
1430
1490
  * this binder then completes the inbound half by decoding each message and pushing it
1431
1491
  * onto `client.transport.emitter` (an {@link import('@orkestrel/emitter').EmitterInterface}
1432
1492
  * exposes `emit`, so no client modification is needed). A malformed / non-JSON-RPC
1433
- * inbound message is DROPPED (§14, total — never throws); a delivery fault is routed to
1493
+ * inbound message is DROPPED (total — never throws); a delivery fault is routed to
1434
1494
  * `client.transport.emitter`'s `error` event (never rethrown). The returned unbind
1435
1495
  * DETACHES this binder (further inbound messages and the transport's `closed` signal are
1436
1496
  * ignored) WITHOUT closing the transport.
@@ -1448,7 +1508,7 @@ function bindServer(server, transport) {
1448
1508
  * itself when a caller's `signal` aborts, on a carrier declaring `duplex`. Adding a registry
1449
1509
  * here would be a second correlation table for ids the client is already correlating, and two
1450
1510
  * tables for one fact drift. The one obligation this binder does carry is delivery: a
1451
- * malformed / non-JSON-RPC inbound message is DROPPED (§14, total — never throws).
1511
+ * malformed / non-JSON-RPC inbound message is DROPPED (total — never throws).
1452
1512
  *
1453
1513
  * @param client - The transport-agnostic client whose transport to deliver messages onto
1454
1514
  * @param transport - The duplex channel to pipe the client over
@@ -1498,11 +1558,11 @@ function bindClient(client, transport) {
1498
1558
  }
1499
1559
  //#endregion
1500
1560
  //#region src/core/validators.ts
1501
- /** Determine whether a value is an exact finite JSON object. */
1561
+ /** Determines whether a value is an exact finite JSON object. */
1502
1562
  function isJSONObject(value) {
1503
1563
  return (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value)).success;
1504
1564
  }
1505
- /** Determine whether a string follows the dated MCP `_meta` key grammar. */
1565
+ /** Determines whether a string follows the dated MCP `_meta` key grammar. */
1506
1566
  function isMCPMetaKey(value) {
1507
1567
  if (!(0, _orkestrel_contract.isString)(value)) return false;
1508
1568
  const slash = value.indexOf("/");
@@ -1515,24 +1575,24 @@ function isMCPMetaKey(value) {
1515
1575
  }
1516
1576
  return name.length === 0 || /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(name);
1517
1577
  }
1518
- /** Determine whether a value is exact finite MCP metadata with valid keys. */
1578
+ /** Determines whether a value is exact finite MCP metadata with valid keys. */
1519
1579
  function isMCPMetaObject(value) {
1520
1580
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
1521
1581
  return owned.success && Object.keys(owned.value).every((key) => isMCPMetaKey(key));
1522
1582
  }
1523
- /** Determine whether a value is exact result metadata with a valid reserved server identity. */
1583
+ /** Determines whether a value is exact result metadata with a valid reserved server identity. */
1524
1584
  function isMCPResultMetaObject(value) {
1525
1585
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
1526
1586
  if (!owned.success || !Object.keys(owned.value).every((key) => isMCPMetaKey(key))) return false;
1527
1587
  const identity = owned.value[MCP_META_SERVER];
1528
1588
  return (0, _orkestrel_contract.isUndefined)(identity) || isMCPIdentity(identity);
1529
1589
  }
1530
- /** Determine whether a value is one dated MCP logging level. */
1590
+ /** Determines whether a value is one dated MCP logging level. */
1531
1591
  function isMCPLoggingLevel(value) {
1532
1592
  return value === "debug" || value === "info" || value === "notice" || value === "warning" || value === "error" || value === "critical" || value === "alert" || value === "emergency";
1533
1593
  }
1534
1594
  /**
1535
- * Determine whether a value is standard padded base64 as required by JSON Schema `byte` format.
1595
+ * Determines whether a value is standard padded base64 as required by JSON Schema `byte` format.
1536
1596
  *
1537
1597
  * @param value - The unknown value to inspect
1538
1598
  * @returns Whether the value is an empty or completely padded standard base64 encoding
@@ -1541,7 +1601,7 @@ function isStandardBase64(value) {
1541
1601
  return (0, _orkestrel_contract.isString)(value) && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
1542
1602
  }
1543
1603
  /**
1544
- * Determine whether a value is one absolute URI under RFC 3986 syntax.
1604
+ * Determines whether a value is one absolute URI under RFC 3986 syntax.
1545
1605
  *
1546
1606
  * @remarks
1547
1607
  * This host-neutral syntax guard does not resolve, normalize, decode, fetch, or apply a
@@ -1630,7 +1690,7 @@ function isAbsoluteURI(value) {
1630
1690
  return /^[A-Za-z0-9\-._~!$&'()*+,;=%]*$/.test(host);
1631
1691
  }
1632
1692
  /**
1633
- * Determine whether a value is one RFC 3339 `full-date` naming a real calendar day.
1693
+ * Determines whether a value is one RFC 3339 `full-date` naming a real calendar day.
1634
1694
  *
1635
1695
  * @remarks
1636
1696
  * RFC 3339 §5.6 defines `date-mday` as `01-28`, `29`, `30`, or `31` BASED ON the month and
@@ -1665,7 +1725,7 @@ function isRFC3339Date(value) {
1665
1725
  return day <= (month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31);
1666
1726
  }
1667
1727
  /**
1668
- * Determine whether a value is one RFC 3339 `date-time` naming a real calendar day.
1728
+ * Determines whether a value is one RFC 3339 `date-time` naming a real calendar day.
1669
1729
  *
1670
1730
  * @remarks
1671
1731
  * The `full-date` half is {@link isRFC3339Date}, so an impossible day is refused here for
@@ -1692,7 +1752,7 @@ function isRFC3339DateTime(value) {
1692
1752
  return matched !== null && isRFC3339Date(matched[1]);
1693
1753
  }
1694
1754
  /**
1695
- * Determine whether a value is one exact finite MCP progress payload.
1755
+ * Determines whether a value is one exact finite MCP progress payload.
1696
1756
  *
1697
1757
  * @param value - The unknown value to inspect
1698
1758
  * @returns Whether required progress and optional total/message fields match the dated schema
@@ -1711,7 +1771,7 @@ function isMCPProgress(value) {
1711
1771
  }
1712
1772
  }
1713
1773
  /**
1714
- * Determine whether a value carries valid dated-schema MCP content annotations.
1774
+ * Determines whether a value carries valid dated-schema MCP content annotations.
1715
1775
  *
1716
1776
  * @param value - The unknown value to inspect
1717
1777
  * @returns Whether the value is valid MCP annotations
@@ -1732,7 +1792,7 @@ function isMCPAnnotations(value) {
1732
1792
  }
1733
1793
  }
1734
1794
  /**
1735
- * Determine whether a value is one exact dated-schema MCP icon.
1795
+ * Determines whether a value is one exact dated-schema MCP icon.
1736
1796
  *
1737
1797
  * @param value - The unknown value to inspect
1738
1798
  * @returns Whether the value is a valid MCP icon
@@ -1751,7 +1811,7 @@ function isMCPIcon(value) {
1751
1811
  return false;
1752
1812
  }
1753
1813
  }
1754
- /** Determine whether a value is one complete dated MCP implementation identity. */
1814
+ /** Determines whether a value is one complete dated MCP implementation identity. */
1755
1815
  function isMCPIdentity(value) {
1756
1816
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
1757
1817
  if (!owned.success) return false;
@@ -1767,7 +1827,7 @@ function isMCPIdentity(value) {
1767
1827
  return false;
1768
1828
  }
1769
1829
  }
1770
- /** Determine whether a value is one exact open dated client-capability declaration. */
1830
+ /** Determines whether a value is one exact open dated client-capability declaration. */
1771
1831
  function isMCPClientCapabilities(value) {
1772
1832
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
1773
1833
  if (!owned.success) return false;
@@ -1797,7 +1857,7 @@ function isMCPClientCapabilities(value) {
1797
1857
  return false;
1798
1858
  }
1799
1859
  }
1800
- /** Determine whether a value is one exact open dated server-capability declaration. */
1860
+ /** Determines whether a value is one exact open dated server-capability declaration. */
1801
1861
  function isMCPServerCapabilities(value) {
1802
1862
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
1803
1863
  if (!owned.success) return false;
@@ -1823,7 +1883,7 @@ function isMCPServerCapabilities(value) {
1823
1883
  }
1824
1884
  }
1825
1885
  /**
1826
- * Determine whether a value is embedded textual MCP resource contents.
1886
+ * Determines whether a value is embedded textual MCP resource contents.
1827
1887
  *
1828
1888
  * @param value - The unknown value to inspect
1829
1889
  * @returns Whether the value is embedded textual resource contents
@@ -1842,7 +1902,7 @@ function isMCPTextResource(value) {
1842
1902
  }
1843
1903
  }
1844
1904
  /**
1845
- * Determine whether a value is embedded blob MCP resource contents.
1905
+ * Determines whether a value is embedded blob MCP resource contents.
1846
1906
  *
1847
1907
  * @param value - The unknown value to inspect
1848
1908
  * @returns Whether the value is embedded blob resource contents
@@ -1861,7 +1921,7 @@ function isMCPBlobResource(value) {
1861
1921
  }
1862
1922
  }
1863
1923
  /**
1864
- * Determine whether a value is one `resources/list` descriptor.
1924
+ * Determines whether a value is one `resources/list` descriptor.
1865
1925
  *
1866
1926
  * @param value - The unknown value to inspect
1867
1927
  * @returns Whether the value is a valid resource descriptor
@@ -1879,7 +1939,7 @@ function isMCPResource(value) {
1879
1939
  }
1880
1940
  }
1881
1941
  /**
1882
- * Determine whether a value is one resource-template descriptor.
1942
+ * Determines whether a value is one resource-template descriptor.
1883
1943
  *
1884
1944
  * @remarks
1885
1945
  * This guard validates the descriptor shape. Template expansion and the RFC 6570 feature
@@ -1900,7 +1960,7 @@ function isMCPResourceTemplate(value) {
1900
1960
  }
1901
1961
  }
1902
1962
  /**
1903
- * Determine whether a value is structurally discriminated resource contents.
1963
+ * Determines whether a value is structurally discriminated resource contents.
1904
1964
  *
1905
1965
  * @param value - The unknown value to inspect
1906
1966
  * @returns Whether exactly one of `text` and `blob` is present and valid
@@ -1914,7 +1974,7 @@ function isMCPResourceContents(value) {
1914
1974
  return text ? isMCPTextResource(resource) : isMCPBlobResource(resource);
1915
1975
  }
1916
1976
  /**
1917
- * Determine whether a value carries the shared optional pagination cursor.
1977
+ * Determines whether a value carries the shared optional pagination cursor.
1918
1978
  *
1919
1979
  * @param value - The unknown value to inspect
1920
1980
  * @returns Whether a present `cursor` is a string
@@ -1924,7 +1984,7 @@ function isMCPPaginationParams(value) {
1924
1984
  return owned.success && ((0, _orkestrel_contract.isUndefined)(owned.value["cursor"]) || (0, _orkestrel_contract.isString)(owned.value["cursor"]));
1925
1985
  }
1926
1986
  /**
1927
- * Determine whether a value is one consumer-owned resource page.
1987
+ * Determines whether a value is one consumer-owned resource page.
1928
1988
  *
1929
1989
  * @param value - The unknown value to inspect
1930
1990
  * @returns Whether the resources and optional following cursor are valid
@@ -1937,7 +1997,7 @@ function isMCPResourcePage(value) {
1937
1997
  return Array.isArray(resources) && resources.every((resource) => isMCPResource(resource)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
1938
1998
  }
1939
1999
  /**
1940
- * Determine whether a value is one consumer-owned resource-template page.
2000
+ * Determines whether a value is one consumer-owned resource-template page.
1941
2001
  *
1942
2002
  * @param value - The unknown value to inspect
1943
2003
  * @returns Whether the templates and optional following cursor are valid
@@ -1950,7 +2010,7 @@ function isMCPResourceTemplatePage(value) {
1950
2010
  return Array.isArray(templates) && templates.every((template) => isMCPResourceTemplate(template)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
1951
2011
  }
1952
2012
  /**
1953
- * Determine whether a value is a string-valued MCP argument record.
2013
+ * Determines whether a value is a string-valued MCP argument record.
1954
2014
  *
1955
2015
  * @param value - The unknown value to inspect
1956
2016
  * @returns Whether every own argument value is a string
@@ -1960,7 +2020,7 @@ function isMCPStringArguments(value) {
1960
2020
  return owned.success && Object.values(owned.value).every((argument) => (0, _orkestrel_contract.isString)(argument));
1961
2021
  }
1962
2022
  /**
1963
- * Determine whether a value is one prompt argument descriptor.
2023
+ * Determines whether a value is one prompt argument descriptor.
1964
2024
  *
1965
2025
  * @param value - The unknown value to inspect
1966
2026
  * @returns Whether the prompt argument descriptor is valid
@@ -1972,7 +2032,7 @@ function isMCPPromptArgument(value) {
1972
2032
  return (0, _orkestrel_contract.isString)(argument["name"]) && ((0, _orkestrel_contract.isUndefined)(argument["title"]) || (0, _orkestrel_contract.isString)(argument["title"])) && ((0, _orkestrel_contract.isUndefined)(argument["description"]) || (0, _orkestrel_contract.isString)(argument["description"])) && ((0, _orkestrel_contract.isUndefined)(argument["required"]) || (0, _orkestrel_contract.isBoolean)(argument["required"]));
1973
2033
  }
1974
2034
  /**
1975
- * Determine whether a value is one `prompts/list` descriptor.
2035
+ * Determines whether a value is one `prompts/list` descriptor.
1976
2036
  *
1977
2037
  * @param value - The unknown value to inspect
1978
2038
  * @returns Whether the prompt descriptor is valid
@@ -1986,7 +2046,7 @@ function isMCPPrompt(value) {
1986
2046
  return (0, _orkestrel_contract.isString)(prompt["name"]) && ((0, _orkestrel_contract.isUndefined)(prompt["title"]) || (0, _orkestrel_contract.isString)(prompt["title"])) && ((0, _orkestrel_contract.isUndefined)(prompt["description"]) || (0, _orkestrel_contract.isString)(prompt["description"])) && ((0, _orkestrel_contract.isUndefined)(argumentsValue) || Array.isArray(argumentsValue) && argumentsValue.every((argument) => isMCPPromptArgument(argument))) && ((0, _orkestrel_contract.isUndefined)(icons) || Array.isArray(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(prompt["_meta"]) || isMCPMetaObject(prompt["_meta"]));
1987
2047
  }
1988
2048
  /**
1989
- * Determine whether a value is one prompt message with existing rich content.
2049
+ * Determines whether a value is one prompt message with existing rich content.
1990
2050
  *
1991
2051
  * @param value - The unknown value to inspect
1992
2052
  * @returns Whether the role and content are valid
@@ -1996,7 +2056,7 @@ function isMCPPromptMessage(value) {
1996
2056
  return owned.success && (owned.value["role"] === "user" || owned.value["role"] === "assistant") && isMCPContent(owned.value["content"]);
1997
2057
  }
1998
2058
  /**
1999
- * Determine whether a value is one consumer-owned prompt page.
2059
+ * Determines whether a value is one consumer-owned prompt page.
2000
2060
  *
2001
2061
  * @param value - The unknown value to inspect
2002
2062
  * @returns Whether the prompts and optional following cursor are valid
@@ -2009,7 +2069,7 @@ function isMCPPromptPage(value) {
2009
2069
  return Array.isArray(prompts) && prompts.every((prompt) => isMCPPrompt(prompt)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2010
2070
  }
2011
2071
  /**
2012
- * Determine whether a value is one complete `prompts/get` result.
2072
+ * Determines whether a value is one complete `prompts/get` result.
2013
2073
  *
2014
2074
  * @param value - The unknown value to inspect
2015
2075
  * @returns Whether the prompt result and all messages are valid
@@ -2022,7 +2082,7 @@ function isMCPPromptGetResult(value) {
2022
2082
  return result["resultType"] === "complete" && ((0, _orkestrel_contract.isUndefined)(result["description"]) || (0, _orkestrel_contract.isString)(result["description"])) && Array.isArray(messages) && messages.every((message) => isMCPPromptMessage(message)) && ((0, _orkestrel_contract.isUndefined)(result["_meta"]) || isMCPResultMetaObject(result["_meta"]));
2023
2083
  }
2024
2084
  /**
2025
- * Determine whether a value is a prompt or resource-template completion reference.
2085
+ * Determines whether a value is a prompt or resource-template completion reference.
2026
2086
  *
2027
2087
  * @param value - The unknown value to inspect
2028
2088
  * @returns Whether the discriminated reference is valid
@@ -2034,7 +2094,7 @@ function isMCPCompletionReference(value) {
2034
2094
  return reference["type"] === "ref/prompt" ? (0, _orkestrel_contract.isString)(reference["name"]) : reference["type"] === "ref/resource" && (0, _orkestrel_contract.isString)(reference["uri"]);
2035
2095
  }
2036
2096
  /**
2037
- * Determine whether a value is one `completion/complete` parameter object.
2097
+ * Determines whether a value is one `completion/complete` parameter object.
2038
2098
  *
2039
2099
  * @param value - The unknown value to inspect
2040
2100
  * @returns Whether its reference, fragment, and optional string context are valid
@@ -2053,7 +2113,7 @@ function isMCPCompletionParams(value) {
2053
2113
  return isMCPCompletionReference(params["ref"]) && (0, _orkestrel_contract.isString)(argument.value["name"]) && (0, _orkestrel_contract.isString)(argument.value["value"]);
2054
2114
  }
2055
2115
  /**
2056
- * Determine whether a value is one host-produced completion candidate set.
2116
+ * Determines whether a value is one host-produced completion candidate set.
2057
2117
  *
2058
2118
  * @param value - The unknown value to inspect
2059
2119
  * @returns Whether its candidates and optional result facts are valid
@@ -2067,7 +2127,7 @@ function isMCPCompletion(value) {
2067
2127
  return Array.isArray(values) && values.every((candidate) => (0, _orkestrel_contract.isString)(candidate)) && ((0, _orkestrel_contract.isUndefined)(total) || (0, _orkestrel_contract.isInteger)(total) && total >= 0) && ((0, _orkestrel_contract.isUndefined)(completion["hasMore"]) || (0, _orkestrel_contract.isBoolean)(completion["hasMore"]));
2068
2128
  }
2069
2129
  /**
2070
- * Determine whether a value is one complete, capped `completion/complete` result.
2130
+ * Determines whether a value is one complete, capped `completion/complete` result.
2071
2131
  *
2072
2132
  * @param value - The unknown value to inspect
2073
2133
  * @returns Whether the result is complete and carries at most 100 candidates
@@ -2079,7 +2139,7 @@ function isMCPCompletionResult(value) {
2079
2139
  return isMCPCompletion(completion) && completion.values.length <= 100 && ((0, _orkestrel_contract.isUndefined)(owned.value["_meta"]) || isMCPResultMetaObject(owned.value["_meta"]));
2080
2140
  }
2081
2141
  /**
2082
- * Determine whether a value is one exact dated-schema MCP tool content block.
2142
+ * Determines whether a value is one exact dated-schema MCP tool content block.
2083
2143
  *
2084
2144
  * @param value - The unknown value to inspect
2085
2145
  * @returns Whether the value is valid MCP content
@@ -2111,7 +2171,7 @@ function isMCPContent(value) {
2111
2171
  }
2112
2172
  }
2113
2173
  /**
2114
- * Determine whether a value is one modern MCP result.
2174
+ * Determines whether a value is one modern MCP result.
2115
2175
  *
2116
2176
  * @remarks
2117
2177
  * The open contract's guard: a record carrying a string `resultType` and, when
@@ -2141,7 +2201,7 @@ function isMCPResult(value) {
2141
2201
  return (0, _orkestrel_contract.isUndefined)(metadata) || isMCPResultMetaObject(metadata);
2142
2202
  }
2143
2203
  /**
2144
- * Determine whether a value is one legacy-era MCP result.
2204
+ * Determines whether a value is one legacy-era MCP result.
2145
2205
  *
2146
2206
  * @remarks
2147
2207
  * The legacy revision has no result discriminator, so the absence of `resultType` is
@@ -2162,7 +2222,7 @@ function isMCPLegacyResult(value) {
2162
2222
  return owned.success && !Object.hasOwn(owned.value, "resultType");
2163
2223
  }
2164
2224
  /**
2165
- * Determine whether a value is a complete modern MCP tool result.
2225
+ * Determines whether a value is a complete modern MCP tool result.
2166
2226
  *
2167
2227
  * @param value - The unknown value to inspect
2168
2228
  * @returns Whether the value is a complete MCP call result
@@ -2182,7 +2242,7 @@ function isMCPCallResult(value) {
2182
2242
  }
2183
2243
  }
2184
2244
  /**
2185
- * Determine whether a value is a modern MCP task-creation result.
2245
+ * Determines whether a value is a modern MCP task-creation result.
2186
2246
  *
2187
2247
  * @remarks
2188
2248
  * The runtime enforcement of {@link MCPTaskManagerInterface.start}'s declared return
@@ -2216,7 +2276,7 @@ function isMCPTaskResult(value) {
2216
2276
  }
2217
2277
  }
2218
2278
  /**
2219
- * Determine whether a value is one of the extension's five task lifecycle states.
2279
+ * Determines whether a value is one of the extension's task lifecycle states.
2220
2280
  *
2221
2281
  * @param value - The unknown value to inspect
2222
2282
  * @returns Whether the value is an {@link MCPTaskStatus}
@@ -2231,7 +2291,7 @@ function isMCPTaskStatus(value) {
2231
2291
  return value === "working" || value === "input_required" || value === "completed" || value === "failed" || value === "cancelled";
2232
2292
  }
2233
2293
  /**
2234
- * Determine whether a value is one durable task's full snapshot.
2294
+ * Determines whether a value is one durable task's full snapshot.
2235
2295
  *
2236
2296
  * @remarks
2237
2297
  * The runtime enforcement of {@link MCPTaskManagerInterface.task}'s declared return shape,
@@ -2274,7 +2334,7 @@ function isMCPTaskDetail(value) {
2274
2334
  }
2275
2335
  }
2276
2336
  /**
2277
- * Determine whether a value is a string within a UTF-8 byte bound.
2337
+ * Determines whether a value is a string within a UTF-8 byte bound.
2278
2338
  *
2279
2339
  * @param value - The unknown value to inspect
2280
2340
  * @param bytes - The maximum accepted encoded bytes
@@ -2305,7 +2365,7 @@ function isBoundedString(value, bytes) {
2305
2365
  return true;
2306
2366
  }
2307
2367
  /**
2308
- * Determine whether a value is bounded, cycle-free exact JSON.
2368
+ * Determines whether a value is bounded, cycle-free exact JSON.
2309
2369
  *
2310
2370
  * @remarks
2311
2371
  * Traversal is iterative, ancestor-aware, and contained by {@link attempt}; deep input,
@@ -2325,7 +2385,7 @@ function isBoundedJSON(value, limits) {
2325
2385
  return serializeJSON(value, limits) !== void 0;
2326
2386
  }
2327
2387
  /**
2328
- * Determine whether a value is a valid JSON-RPC correlation id — a string or a
2388
+ * Determines whether a value is a valid JSON-RPC correlation id — a string or a
2329
2389
  * finite integer.
2330
2390
  *
2331
2391
  * @remarks
@@ -2333,7 +2393,7 @@ function isBoundedJSON(value, limits) {
2333
2393
  * absence is what makes a call a notification) and `null` is not one either (MCP
2334
2394
  * omits an unreadable id rather than nulling it). A runtime numeric id must be a
2335
2395
  * finite integer; an empty string is a legal id, because the dated schema imposes
2336
- * no minimum length. Total (§14): any other input returns `false`.
2396
+ * no minimum length. Total: any other input returns `false`.
2337
2397
  *
2338
2398
  * @param value - The already-parsed value to test
2339
2399
  * @returns `true` when `value` is a string or a finite integer
@@ -2351,7 +2411,7 @@ function isJSONRPCId(value) {
2351
2411
  return (0, _orkestrel_contract.isString)(value) || (0, _orkestrel_contract.isInteger)(value);
2352
2412
  }
2353
2413
  /**
2354
- * Determine whether a value is a supported {@link MCPVersion}.
2414
+ * Determines whether a value is a supported {@link MCPVersion}.
2355
2415
  *
2356
2416
  * @param value - The unknown value to inspect
2357
2417
  * @returns `true` when the value is one of {@link SUPPORTED_PROTOCOL_VERSIONS}
@@ -2360,7 +2420,7 @@ function isMCPVersion(value) {
2360
2420
  return (0, _orkestrel_contract.isString)(value) && SUPPORTED_PROTOCOL_VERSIONS.some((version) => version === value);
2361
2421
  }
2362
2422
  /**
2363
- * Determine whether a value is an MCP {@link MCPSubscriptionFilter}.
2423
+ * Determines whether a value is an MCP {@link MCPSubscriptionFilter}.
2364
2424
  *
2365
2425
  * @remarks
2366
2426
  * Every filter field is optional. Boolean notification families accept only booleans, and
@@ -2385,69 +2445,7 @@ function isMCPSubscriptionFilter(value) {
2385
2445
  return (0, _orkestrel_contract.isUndefined)(subscriptions) || (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)(subscriptions);
2386
2446
  }
2387
2447
  /**
2388
- * Determine whether a client capability record declares form-mode elicitation.
2389
- *
2390
- * @remarks
2391
- * The protocol's empty `elicitation` object is the implicit form-only declaration.
2392
- * A non-empty declaration must carry a record-valued `form` member; URL-only support
2393
- * does not authorize a form request. Total over hostile input.
2394
- *
2395
- * @param value - The client capability record to inspect
2396
- * @returns `true` when form-mode elicitation is declared
2397
- *
2398
- * @example
2399
- * ```ts
2400
- * isFormElicitationSupported({ elicitation: {} }) // true — implicit form mode
2401
- * isFormElicitationSupported({ elicitation: { url: {} } }) // false
2402
- * ```
2403
- */
2404
- function isFormElicitationSupported(value) {
2405
- const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
2406
- if (!owned.success) return false;
2407
- try {
2408
- const elicitation = owned.value["elicitation"];
2409
- if (!(0, _orkestrel_contract.isRecord)(elicitation)) return false;
2410
- if ((0, _orkestrel_contract.isRecord)(elicitation["form"])) return true;
2411
- return Object.keys(elicitation).length === 0;
2412
- } catch {
2413
- return false;
2414
- }
2415
- }
2416
- /**
2417
- * Determine whether a client capability record declares the draft Tasks extension.
2418
- *
2419
- * @remarks
2420
- * The declaration lives at `extensions['io.modelcontextprotocol/tasks']` and its value is
2421
- * an empty object, so this is a PRESENCE check: the extension defines no options, and a
2422
- * server that read one would be reading a field no client can meaningfully set. The value
2423
- * must still be a record, because that is the shape the capability record declares — a
2424
- * `true` or a string there is a client speaking a different protocol, not a shorthand.
2425
- *
2426
- * A client declares this PER REQUEST. Nothing here consults a session, because the modern
2427
- * revision is stateless and a capability declared once at connect time says nothing about
2428
- * the request in hand. Total over hostile input.
2429
- *
2430
- * @param value - The client capability record to inspect
2431
- * @returns `true` when the tasks extension is declared
2432
- *
2433
- * @example
2434
- * ```ts
2435
- * isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
2436
- * isTaskSupported({ extensions: {} }) // false — the key is the declaration
2437
- * ```
2438
- */
2439
- function isTaskSupported(value) {
2440
- const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
2441
- if (!owned.success) return false;
2442
- try {
2443
- const extensions = owned.value["extensions"];
2444
- return (0, _orkestrel_contract.isRecord)(extensions) && (0, _orkestrel_contract.isRecord)(extensions["io.modelcontextprotocol/tasks"]);
2445
- } catch {
2446
- return false;
2447
- }
2448
- }
2449
- /**
2450
- * Determine whether a value is one restricted primitive form-elicitation schema.
2448
+ * Determines whether a value is one restricted primitive form-elicitation schema.
2451
2449
  *
2452
2450
  * @param value - The unknown value to inspect
2453
2451
  * @returns `true` for a supported boolean, numeric, string, or string-array schema
@@ -2503,7 +2501,7 @@ function isMCPElicitFieldSchema(value) {
2503
2501
  }
2504
2502
  }
2505
2503
  /**
2506
- * Determine whether a value is the restricted top-level object schema a form elicitation issues.
2504
+ * Determines whether a value is the restricted top-level object schema a form elicitation issues.
2507
2505
  *
2508
2506
  * @remarks
2509
2507
  * The schema half of {@link isMCPElicitForm}, exported on its own because the issued schema
@@ -2537,7 +2535,7 @@ function isMCPElicitSchema(value) {
2537
2535
  }
2538
2536
  }
2539
2537
  /**
2540
- * Determine whether a value is a form-mode elicitation parameter object.
2538
+ * Determines whether a value is a form-mode elicitation parameter object.
2541
2539
  *
2542
2540
  * @param value - The unknown value to inspect
2543
2541
  * @returns `true` when `value` has the restricted form elicitation shape
@@ -2563,7 +2561,7 @@ function isMCPElicitForm(value) {
2563
2561
  }
2564
2562
  }
2565
2563
  /**
2566
- * Determine whether a value is a URL-mode elicitation parameter object.
2564
+ * Determines whether a value is a URL-mode elicitation parameter object.
2567
2565
  *
2568
2566
  * @param value - The unknown value to inspect
2569
2567
  * @returns `true` when `value` has the URL elicitation shape
@@ -2584,7 +2582,7 @@ function isMCPElicitURL(value) {
2584
2582
  }
2585
2583
  }
2586
2584
  /**
2587
- * Determine whether a value is an embedded `elicitation/create` request.
2585
+ * Determines whether a value is an embedded `elicitation/create` request.
2588
2586
  *
2589
2587
  * @param value - The unknown value to inspect
2590
2588
  * @returns `true` when `value` is a form- or URL-mode elicitation request
@@ -2609,7 +2607,7 @@ function isMCPElicitRequest(value) {
2609
2607
  }
2610
2608
  }
2611
2609
  /**
2612
- * Determine whether a value is one legal embedded multi-round-trip request.
2610
+ * Determines whether a value is one legal embedded multi-round-trip request.
2613
2611
  *
2614
2612
  * @param value - The unknown value to inspect
2615
2613
  * @returns `true` for elicitation, deprecated sampling, or deprecated roots requests
@@ -2633,7 +2631,7 @@ function isMCPInputRequest(value) {
2633
2631
  }
2634
2632
  }
2635
2633
  /**
2636
- * Determine whether a value is a server-keyed map of embedded input requests.
2634
+ * Determines whether a value is a server-keyed map of embedded input requests.
2637
2635
  *
2638
2636
  * @param value - The unknown value to inspect
2639
2637
  * @returns `true` when every own value is a legal {@link MCPInputRequest}
@@ -2653,7 +2651,7 @@ function isMCPInputRequestMap(value) {
2653
2651
  }
2654
2652
  }
2655
2653
  /**
2656
- * Determine whether a value is one elicitation response.
2654
+ * Determines whether a value is one elicitation response.
2657
2655
  *
2658
2656
  * @param value - The unknown value to inspect
2659
2657
  * @returns `true` when action/content have the protocol shape
@@ -2680,7 +2678,7 @@ function isMCPElicitResult(value) {
2680
2678
  }
2681
2679
  }
2682
2680
  /**
2683
- * Determine whether accepted elicitation content satisfies the exact schema that was issued.
2681
+ * Determines whether accepted elicitation content satisfies the exact schema that was issued.
2684
2682
  *
2685
2683
  * @remarks
2686
2684
  * {@link isMCPElicitResult} says a response has the SHAPE of a response; this says the
@@ -2702,7 +2700,7 @@ function isMCPElicitResult(value) {
2702
2700
  * that answers more than it was asked is not refused for it. A `schema` that is not itself a
2703
2701
  * valid {@link MCPElicitSchema} admits NOTHING — an unenforceable schema is never a permissive
2704
2702
  * one — which is why `schema` is accepted as `unknown` and checked rather than trusted. Total
2705
- * (§14) over hostile content and hostile schemas alike.
2703
+ * over hostile content and hostile schemas alike.
2706
2704
  *
2707
2705
  * @param value - The accepted response content to check
2708
2706
  * @param schema - The exact {@link MCPElicitSchema} that was issued with the elicitation
@@ -2787,7 +2785,7 @@ function isElicitContent(value, schema) {
2787
2785
  }
2788
2786
  }
2789
2787
  /**
2790
- * Determine whether a value is an MCP input-required result.
2788
+ * Determines whether a value is an MCP input-required result.
2791
2789
  *
2792
2790
  * @remarks
2793
2791
  * Enforces the at-least-one-of rule at runtime: `inputRequests`, `requestState`, or
@@ -2820,15 +2818,15 @@ function isMCPInputResult(value) {
2820
2818
  }
2821
2819
  }
2822
2820
  /**
2823
- * Determine whether a parsed value is a {@link JSONRPCRequest}.
2821
+ * Determines whether a parsed value is a {@link JSONRPCRequest}.
2824
2822
  *
2825
2823
  * @remarks
2826
2824
  * A request is a record with `jsonrpc === '2.0'`, a string `method`, and an `id`
2827
2825
  * that {@link isJSONRPCId} accepts. An id-less call is NOT a request — it is a
2828
2826
  * {@link JSONRPCNotification}, which {@link isJSONRPCNotification} answers for. The
2829
- * two guards are mutually exclusive on every input: this one requires a valid `id`
2827
+ * guards are mutually exclusive on every input: this one requires a valid `id`
2830
2828
  * value, that one requires no own `id` member at all. `params`, when present, must
2831
- * be a record. Total (§14): any other input returns `false`.
2829
+ * be a record. Total: any other input returns `false`.
2832
2830
  *
2833
2831
  * @param value - The already-parsed value to test
2834
2832
  * @returns `true` when `value` is a valid JSON-RPC request
@@ -2850,12 +2848,12 @@ function isJSONRPCRequest(value) {
2850
2848
  return (0, _orkestrel_contract.isUndefined)(params) || (0, _orkestrel_contract.isRecord)(params);
2851
2849
  }
2852
2850
  /**
2853
- * Determine whether a parsed value is a {@link JSONRPCNotification}.
2851
+ * Determines whether a parsed value is a {@link JSONRPCNotification}.
2854
2852
  *
2855
2853
  * @remarks
2856
2854
  * A notification is a request-shaped call carrying NO `id` member — the protocol
2857
2855
  * forbids one, because nothing answers a notification. `params`, when present, must
2858
- * be a record. Total (§14): any other input returns `false`.
2856
+ * be a record. Total: any other input returns `false`.
2859
2857
  *
2860
2858
  * @param value - The already-parsed value to test
2861
2859
  * @returns `true` when `value` is a valid JSON-RPC notification
@@ -2876,12 +2874,12 @@ function isJSONRPCNotification(value) {
2876
2874
  return (0, _orkestrel_contract.isUndefined)(params) || (0, _orkestrel_contract.isRecord)(params);
2877
2875
  }
2878
2876
  /**
2879
- * Determine whether a parsed value is a {@link JSONRPCInvocation} — a request or a
2877
+ * Determines whether a parsed value is a {@link JSONRPCInvocation} — a request or a
2880
2878
  * notification.
2881
2879
  *
2882
2880
  * @remarks
2883
2881
  * The union of {@link isJSONRPCRequest} and {@link isJSONRPCNotification}, which are
2884
- * mutually exclusive, so a positive answer names exactly one arm. Total (§14).
2882
+ * mutually exclusive, so a positive answer names exactly one arm. Total.
2885
2883
  *
2886
2884
  * @param value - The already-parsed value to test
2887
2885
  * @returns `true` when `value` is a valid JSON-RPC request or notification
@@ -2890,7 +2888,7 @@ function isJSONRPCInvocation(value) {
2890
2888
  return isJSONRPCRequest(value) || isJSONRPCNotification(value);
2891
2889
  }
2892
2890
  /**
2893
- * Determine whether a parsed value is a {@link JSONRPCResultResponse} — the success
2891
+ * Determines whether a parsed value is a {@link JSONRPCResultResponse} — the success
2894
2892
  * arm of a response.
2895
2893
  *
2896
2894
  * @remarks
@@ -2899,7 +2897,7 @@ function isJSONRPCInvocation(value) {
2899
2897
  * which is what makes this guard and {@link isJSONRPCErrorResponse} mutually
2900
2898
  * exclusive on every input. `result` itself must be an object: either a modern
2901
2899
  * {@link isMCPResult} or a legacy {@link isMCPLegacyResult}, never a bare primitive.
2902
- * Total (§14).
2900
+ * Total.
2903
2901
  *
2904
2902
  * @param value - The already-parsed value to test
2905
2903
  * @returns `true` when `value` is a valid JSON-RPC result response
@@ -2920,22 +2918,22 @@ function isJSONRPCResultResponse(value) {
2920
2918
  return isMCPResult(result) || isMCPLegacyResult(result);
2921
2919
  }
2922
2920
  /**
2923
- * Determine whether a value is one JSON-RPC `error` member.
2921
+ * Determines whether a value is one JSON-RPC `error` member.
2924
2922
  *
2925
2923
  * @remarks
2926
2924
  * The failure OBJECT, not the envelope carrying it — the shape a failed response owns
2927
2925
  * under `error`, and the shape a `failed` {@link MCPTaskDetail} owns under the same name,
2928
- * which is why it is one guard rather than the same three checks written twice.
2926
+ * which is why it is one guard rather than the same checks written twice.
2929
2927
  *
2930
2928
  * It is deliberately STRUCTURAL rather than exact-JSON: `data` is declared `unknown`, so
2931
2929
  * requiring the whole object to survive a JSON clone would refuse a legal error that
2932
2930
  * carried a non-JSON payload. Both callers here hand it an already-owned value.
2933
2931
  *
2934
- * That choice is why the two key reads are guarded. Every sibling guard clones first, and a
2932
+ * That choice is why the key reads are guarded. Every sibling guard clones first, and a
2935
2933
  * clone reads each key once behind a boundary that already owns totality; this one is the
2936
2934
  * family's only DIRECT reader, so it meets `code` and `message` exactly as the value defines
2937
2935
  * them — including as accessors that throw. Reading a named key off an unowned value is
2938
- * itself the hostile step, and it is bounded here rather than allowed to escape. Total (§14).
2936
+ * itself the hostile step, and it is bounded here rather than allowed to escape. Total.
2939
2937
  *
2940
2938
  * @param value - The already-parsed value to test
2941
2939
  * @returns `true` when `value` carries an integer `code` and a string `message`
@@ -2952,14 +2950,14 @@ function isJSONRPCError(value) {
2952
2950
  return read.success && read.value;
2953
2951
  }
2954
2952
  /**
2955
- * Determine whether a parsed value is a {@link JSONRPCErrorResponse} — the failure
2953
+ * Determines whether a parsed value is a {@link JSONRPCErrorResponse} — the failure
2956
2954
  * arm of a response.
2957
2955
  *
2958
2956
  * @remarks
2959
2957
  * `id` is OPTIONAL here and only here: a peer that could not read the failed
2960
2958
  * request's id OMITS the member rather than sending `null`, so an absent `id` is
2961
2959
  * valid and a `null` one is not. The envelope must own an `error` and must NOT own a
2962
- * `result`. `error` carries an integer `code` and a string `message`. Total (§14).
2960
+ * `result`. `error` carries an integer `code` and a string `message`. Total.
2963
2961
  *
2964
2962
  * @param value - The already-parsed value to test
2965
2963
  * @returns `true` when `value` is a valid JSON-RPC error response
@@ -2980,10 +2978,10 @@ function isJSONRPCErrorResponse(value) {
2980
2978
  return isJSONRPCError(response["error"]);
2981
2979
  }
2982
2980
  /**
2983
- * Determine whether a parsed value is a {@link JSONRPCResponse}.
2981
+ * Determines whether a parsed value is a {@link JSONRPCResponse}.
2984
2982
  *
2985
2983
  * @remarks
2986
- * The union of the two mutually exclusive arms. Total (§14).
2984
+ * The union of the mutually exclusive arms. Total.
2987
2985
  *
2988
2986
  * @param value - The already-parsed value to test
2989
2987
  * @returns `true` when `value` is a valid JSON-RPC response
@@ -2992,11 +2990,11 @@ function isJSONRPCResponse(value) {
2992
2990
  return isJSONRPCResultResponse(value) || isJSONRPCErrorResponse(value);
2993
2991
  }
2994
2992
  /**
2995
- * Determine whether a parsed value is a {@link JSONRPCMessage} — an invocation or a
2993
+ * Determines whether a parsed value is a {@link JSONRPCMessage} — an invocation or a
2996
2994
  * response.
2997
2995
  *
2998
2996
  * @remarks
2999
- * The union of {@link isJSONRPCInvocation} and {@link isJSONRPCResponse}. Total (§14).
2997
+ * The union of {@link isJSONRPCInvocation} and {@link isJSONRPCResponse}. Total.
3000
2998
  *
3001
2999
  * @param value - The already-parsed value to test
3002
3000
  * @returns `true` when `value` is a valid JSON-RPC message
@@ -3005,7 +3003,7 @@ function isJSONRPCMessage(value) {
3005
3003
  return isJSONRPCInvocation(value) || isJSONRPCResponse(value);
3006
3004
  }
3007
3005
  /**
3008
- * Determine whether a parsed value is an MCP `initialize` invocation.
3006
+ * Determines whether a parsed value is an MCP `initialize` invocation.
3009
3007
  *
3010
3008
  * @param value - The already-parsed value to test
3011
3009
  * @returns `true` when `value` is a valid `initialize` request or notification
@@ -3021,7 +3019,7 @@ function isInitializeRequest(value) {
3021
3019
  return owned.success && isJSONRPCInvocation(owned.value) && owned.value["method"] === "initialize";
3022
3020
  }
3023
3021
  /**
3024
- * Determine whether a JSON-RPC invocation uses the modern per-request MCP wire shape.
3022
+ * Determines whether a JSON-RPC invocation uses the modern per-request MCP wire shape.
3025
3023
  *
3026
3024
  * @remarks
3027
3025
  * Presence routes and validity answers: this guard checks only that
@@ -3112,7 +3110,7 @@ var MCPProgressReporter = class {
3112
3110
  #last;
3113
3111
  #stopped = false;
3114
3112
  /**
3115
- * Create one non-durable progress slot for an active request.
3113
+ * Creates one non-durable progress slot for an active request.
3116
3114
  *
3117
3115
  * @param token - The opaque progress token copied into each notification
3118
3116
  * @param limits - The byte, key, and depth bounds applied to every progress payload
@@ -3127,7 +3125,7 @@ var MCPProgressReporter = class {
3127
3125
  else signal.addEventListener("abort", this.#listener, { once: true });
3128
3126
  }
3129
3127
  /**
3130
- * Publish one bounded, strictly increasing progress value and await its consumption.
3128
+ * Publishes one bounded, strictly increasing progress value and awaits its consumption.
3131
3129
  *
3132
3130
  * @param progress - The progress payload to own and hand to the serial consumer
3133
3131
  * @returns A promise resolving after {@link take} consumes the item
@@ -3148,7 +3146,7 @@ var MCPProgressReporter = class {
3148
3146
  await consumed.promise;
3149
3147
  }
3150
3148
  /**
3151
- * Take the next progress notification, waiting for the single producer slot when empty.
3149
+ * Takes the next progress notification, waiting for the single producer slot when empty.
3152
3150
  *
3153
3151
  * @returns The official progress notification carrying the original token
3154
3152
  * @throws When the reporter stops or another consumer already has a pending take
@@ -3172,7 +3170,7 @@ var MCPProgressReporter = class {
3172
3170
  return buildProgressNotification(this.#token, progress);
3173
3171
  }
3174
3172
  /**
3175
- * Permanently stop the reporter, reject pending work, and detach its abort listener.
3173
+ * Stops the reporter permanently, rejects pending work, and detaches its abort listener.
3176
3174
  *
3177
3175
  * Repeated calls are idempotent. No queued or replayable progress survives the first call.
3178
3176
  *
@@ -3214,10 +3212,10 @@ var MCPProgressReporter = class {
3214
3212
  * cleanup to the producer — so a cooperating producer is woken rather than waited on —
3215
3213
  * contains every promise the producer settles late, and makes every closure path idempotent.
3216
3214
  *
3217
- * The three closures are deliberately different answers: the source's own return is the
3215
+ * The closures are deliberately different answers: the source's own return is the
3218
3216
  * terminal RESPONSE, `return(value)` is the consumer saying it has the answer already, and
3219
- * {@link stop} is an owner saying there will be no answer at all. Only the first is a
3220
- * message a peer ever sees.
3217
+ * {@link stop} is an owner saying there will be no answer at all. Only the source's own
3218
+ * return is a message a peer ever sees.
3221
3219
  *
3222
3220
  * A producer's own resource cleanup remains the producer's: JavaScript cannot settle work a
3223
3221
  * generator is suspended inside, so the signal is how an uncooperative producer is asked to
@@ -3250,7 +3248,7 @@ var MCPStreamController = class {
3250
3248
  #error;
3251
3249
  #closed = false;
3252
3250
  /**
3253
- * Control one produced stream for the lifetime of one request.
3251
+ * Controls one produced stream for the lifetime of one request.
3254
3252
  *
3255
3253
  * @param source - The produced held-open result whose cancellation this controller owns
3256
3254
  * @param signal - The request signal every closure settles against; its abort ends the exchange
@@ -3265,7 +3263,7 @@ var MCPStreamController = class {
3265
3263
  else signal.addEventListener("abort", this.#listener, { once: true });
3266
3264
  }
3267
3265
  /**
3268
- * Read the next message, or the terminating response that ends the exchange.
3266
+ * Reads the next message, or the terminating response that ends the exchange.
3269
3267
  *
3270
3268
  * @remarks
3271
3269
  * At most one read is outstanding against the source at a time, and a rival read is
@@ -3300,7 +3298,7 @@ var MCPStreamController = class {
3300
3298
  };
3301
3299
  }
3302
3300
  /**
3303
- * End the exchange because the consumer already has its answer.
3301
+ * Ends the exchange because the consumer already has its answer.
3304
3302
  *
3305
3303
  * @param value - The terminal the consumer is ending on
3306
3304
  * @returns That terminal as the iteration's `return`
@@ -3314,7 +3312,7 @@ var MCPStreamController = class {
3314
3312
  };
3315
3313
  }
3316
3314
  /**
3317
- * End the exchange with a failure the consumer is raising.
3315
+ * Ends the exchange with a failure the consumer is raising.
3318
3316
  *
3319
3317
  * @param error - The failure to end the exchange with
3320
3318
  * @returns Never — the returned promise always rejects
@@ -3325,7 +3323,7 @@ var MCPStreamController = class {
3325
3323
  throw error;
3326
3324
  }
3327
3325
  /**
3328
- * End the exchange permanently, with no terminal response.
3326
+ * Ends the exchange permanently, with no terminal response.
3329
3327
  *
3330
3328
  * @remarks
3331
3329
  * Idempotent, and the operation an owner that is not the consumer uses: a transport whose
@@ -3340,7 +3338,7 @@ var MCPStreamController = class {
3340
3338
  if (!this.#closed) this.#fail(this.#closure.signal.reason);
3341
3339
  }
3342
3340
  /**
3343
- * End the exchange when the scope that owns it exits.
3341
+ * Ends the exchange when the scope that owns it exits.
3344
3342
  *
3345
3343
  * @remarks
3346
3344
  * {@link stop} under the name a `finally` calls it by, so a pump discharges its ownership
@@ -3353,7 +3351,7 @@ var MCPStreamController = class {
3353
3351
  this.stop();
3354
3352
  }
3355
3353
  /**
3356
- * Iterate this exchange — the controller is its own iterator.
3354
+ * Iterates this exchange — the controller is its own iterator.
3357
3355
  *
3358
3356
  * @returns This controller
3359
3357
  */
@@ -3442,7 +3440,7 @@ var MCPStreamController = class {
3442
3440
  var MCPTextStreamController = class {
3443
3441
  #stream;
3444
3442
  /**
3445
- * Mirror one controlled typed exchange as its serialized sequence.
3443
+ * Mirrors one controlled typed exchange as its serialized sequence.
3446
3444
  *
3447
3445
  * @param stream - The controlled typed stream this face serializes and delegates into
3448
3446
  */
@@ -3450,7 +3448,7 @@ var MCPTextStreamController = class {
3450
3448
  this.#stream = stream;
3451
3449
  }
3452
3450
  /**
3453
- * Read the next serialized message, or the serialized terminating response.
3451
+ * Reads the next serialized message, or the serialized terminating response.
3454
3452
  *
3455
3453
  * @returns The next message as a string, or the terminal as the iteration's `return`
3456
3454
  * @throws Whatever ended the typed exchange, unserialized — an abort is not a message
@@ -3467,7 +3465,7 @@ var MCPTextStreamController = class {
3467
3465
  };
3468
3466
  }
3469
3467
  /**
3470
- * End the exchange because the consumer already has its answer.
3468
+ * Ends the exchange because the consumer already has its answer.
3471
3469
  *
3472
3470
  * @remarks
3473
3471
  * The typed exchange ends with no terminal, because a string is not a
@@ -3486,7 +3484,7 @@ var MCPTextStreamController = class {
3486
3484
  };
3487
3485
  }
3488
3486
  /**
3489
- * End the exchange with a failure the consumer is raising.
3487
+ * Ends the exchange with a failure the consumer is raising.
3490
3488
  *
3491
3489
  * @param error - The failure to end the exchange with
3492
3490
  * @returns Never — the returned promise always rejects
@@ -3497,7 +3495,7 @@ var MCPTextStreamController = class {
3497
3495
  throw error;
3498
3496
  }
3499
3497
  /**
3500
- * End the typed exchange permanently, with no terminal response.
3498
+ * Ends the typed exchange permanently, with no terminal response.
3501
3499
  *
3502
3500
  * @returns Nothing
3503
3501
  */
@@ -3505,7 +3503,7 @@ var MCPTextStreamController = class {
3505
3503
  this.#stream.stop();
3506
3504
  }
3507
3505
  /**
3508
- * End the typed exchange when the scope that owns this face exits.
3506
+ * Ends the typed exchange when the scope that owns this face exits.
3509
3507
  *
3510
3508
  * @remarks
3511
3509
  * Delegates downward exactly as {@link stop} does — disposing the serialized arm is
@@ -3517,7 +3515,7 @@ var MCPTextStreamController = class {
3517
3515
  this.#stream.stop();
3518
3516
  }
3519
3517
  /**
3520
- * Iterate this exchange — the controller is its own iterator.
3518
+ * Iterates this exchange — the controller is its own iterator.
3521
3519
  *
3522
3520
  * @returns This controller
3523
3521
  */
@@ -3528,7 +3526,7 @@ var MCPTextStreamController = class {
3528
3526
  //#endregion
3529
3527
  //#region src/core/MCPLegacy.ts
3530
3528
  /**
3531
- * Translate the fixed legacy method set onto one modern dispatcher.
3529
+ * Translates the fixed legacy method set onto one modern dispatcher.
3532
3530
  *
3533
3531
  * @remarks
3534
3532
  * This decorator owns no execution engine or result normalizer. Modern invocations
@@ -3539,7 +3537,7 @@ var MCPTextStreamController = class {
3539
3537
  var MCPLegacy = class {
3540
3538
  #options;
3541
3539
  /**
3542
- * Create a legacy decorator.
3540
+ * Creates a legacy decorator.
3543
3541
  *
3544
3542
  * @param options - The sole dispatcher and legacy handshake identity
3545
3543
  */
@@ -3648,7 +3646,7 @@ var MCPLegacy = class {
3648
3646
  * requests over a live {@link ToolManagerInterface}, with NO transport coupling.
3649
3647
  *
3650
3648
  * @remarks
3651
- * - **Two entry points.** `dispatch(invocation)` runs an already-parsed invocation and
3649
+ * - **`dispatch` and `handle`.** `dispatch(invocation)` runs an already-parsed invocation and
3652
3650
  * resolves a {@link JSONRPCResponse} for a request — or `undefined` for a
3653
3651
  * {@link JSONRPCNotification}, which carries no `id` and is answered by nothing.
3654
3652
  * `handle(message)` is the string boundary: it
@@ -3664,8 +3662,8 @@ var MCPLegacy = class {
3664
3662
  * every dispatch: the same path a later method or a consumer's own takes, with an
3665
3663
  * unregistered method still answering `-32601`.
3666
3664
  * - **Provider-agnostic.** Imports only core siblings — JSON-RPC + the tool registry,
3667
- * no HTTP, no model. Wire fields are narrowed via the contracts guards (no `as`).
3668
- * - **Observable (§13).** The owned `emitter` fires `request` at the top of every
3665
+ * no HTTP, no model. Wire fields are narrowed with the contract guards (no `as`).
3666
+ * - **Observable.** The owned `emitter` fires `request` at the top of every
3669
3667
  * dispatch; the emitter isolates a listener throw and routes it to its `error` handler
3670
3668
  * (the `error` option), so a listener throw can never escape the dispatch.
3671
3669
  *
@@ -3786,6 +3784,11 @@ var MCPServer = class {
3786
3784
  }
3787
3785
  async #modern(request, options) {
3788
3786
  const id = request.id;
3787
+ const method = request.method;
3788
+ if (!isModernRequest(request)) {
3789
+ if (this.#methods.method(method) === void 0) return buildJSONRPCError(id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${method}`);
3790
+ return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request declares no protocol version");
3791
+ }
3789
3792
  const context = parseRequestContext(request, {
3790
3793
  bytes: this.#limits.message,
3791
3794
  depth: this.#limits.depth
@@ -4314,7 +4317,7 @@ var MCPServer = class {
4314
4317
  //#endregion
4315
4318
  //#region src/core/MCPTaskClient.ts
4316
4319
  /**
4317
- * The CLIENT half of the draft Tasks extension — the three `tasks/*` methods over one
4320
+ * The CLIENT half of the draft Tasks extension — the `tasks/*` methods over one
4318
4321
  * correlated-request door, exposed as an {@link import('./types.js').MCPClientInterface}'s
4319
4322
  * `tasks`.
4320
4323
  *
@@ -4334,7 +4337,7 @@ var MCPServer = class {
4334
4337
  * client's id space, pending table, deadline policy, and `disconnect` drain rather than
4335
4338
  * opening a second path to the same peer.
4336
4339
  * - **The peer authorizes.** Nothing is checked locally. A server that never configured the
4337
- * extension answers all three `-32601`, and a task that never existed, one whose TTL purged
4340
+ * extension answers each of them `-32601`, and a task that never existed, one whose TTL purged
4338
4341
  * it, and one belonging to another principal are all the same `-32602` — a refusal this
4339
4342
  * client passes on rather than resolving into a distinction the peer refused to publish.
4340
4343
  *
@@ -4411,16 +4414,16 @@ var MCPTaskClient = class {
4411
4414
  * exit — the deadline, an abort, a rejecting `send`, the answer itself, and the
4412
4415
  * teardown's drain — without any of those paths knowing they exist.
4413
4416
  * - **Per-request deadline.** An ordinary `#request` carries `this.#timeout`. The initial
4414
- * discovery probe uses that same 30-second default, or the shorter probe deadline where
4415
- * `timeout` was configured, so a silent peer cannot hold negotiation indefinitely.
4417
+ * discovery probe uses that same configured deadline, so a silent peer cannot hold
4418
+ * negotiation indefinitely.
4416
4419
  * `AbortSignal.timeout` (never a raw `setTimeout`) rejects only that pending request, and the
4417
4420
  * same deadline bounds the WAIT on the transport's `close`, the one wait no drain and no signal
4418
4421
  * can reach. It bounds the wait rather than the close, which keeps running, so a retry joins it
4419
4422
  * instead of shutting one connection down twice.
4420
4423
  * - **Transport-agnostic.** Imports only core siblings (JSON-RPC + the tool vocabulary);
4421
- * the concrete transport is injected. Wire fields are narrowed via the contracts
4424
+ * the concrete transport is injected. Wire fields are narrowed with the contract
4422
4425
  * guards (no `as`).
4423
- * - **Observable (§13).** The owned `emitter` fires `connect` / `disconnect` /
4426
+ * - **Observable.** The owned `emitter` fires `connect` / `disconnect` /
4424
4427
  * `notification` / `error`; the emitter isolates a listener throw and routes it to its
4425
4428
  * `error` handler (the `error` option), so a listener throw can never escape.
4426
4429
  *
@@ -4441,7 +4444,6 @@ var MCPClient = class {
4441
4444
  #capabilities;
4442
4445
  #pin;
4443
4446
  #timeout;
4444
- #probe;
4445
4447
  #tasks;
4446
4448
  #pending = /* @__PURE__ */ new Map();
4447
4449
  #nextId = 0;
@@ -4456,6 +4458,11 @@ var MCPClient = class {
4456
4458
  #era = void 0;
4457
4459
  #offer;
4458
4460
  constructor(options) {
4461
+ const requested = options.version;
4462
+ if (requested !== void 0 && !isMCPVersion(requested)) throw new MCPError("Unsupported protocol version", MCP_UNSUPPORTED_VERSION, {
4463
+ supported: SUPPORTED_PROTOCOL_VERSIONS,
4464
+ requested
4465
+ });
4459
4466
  this.#emitter = new _orkestrel_emitter.Emitter({
4460
4467
  ...options.on !== void 0 ? { on: options.on } : {},
4461
4468
  ...options.error !== void 0 ? { error: options.error } : {}
@@ -4466,10 +4473,9 @@ var MCPClient = class {
4466
4473
  version: "1.0.0"
4467
4474
  };
4468
4475
  this.#capabilities = options.capabilities ?? {};
4469
- this.#pin = options.version;
4470
- this.#offer = options.version ?? "2026-07-28";
4476
+ this.#pin = requested;
4477
+ this.#offer = requested ?? "2026-07-28";
4471
4478
  this.#timeout = options.timeout ?? 3e4;
4472
- this.#probe = options.timeout === void 0 ? DEFAULT_MCP_REQUEST_TIMEOUT : Math.min(options.timeout, 50);
4473
4479
  this.#tasks = new MCPTaskClient({
4474
4480
  request: this.#request.bind(this),
4475
4481
  timeout: this.#timeout
@@ -4491,9 +4497,6 @@ var MCPClient = class {
4491
4497
  get tasks() {
4492
4498
  return this.#tasks;
4493
4499
  }
4494
- on(event, handler) {
4495
- this.#emitter.on(event, handler);
4496
- }
4497
4500
  async connect() {
4498
4501
  for (;;) {
4499
4502
  const closing = this.#disconnecting;
@@ -4526,7 +4529,7 @@ var MCPClient = class {
4526
4529
  }
4527
4530
  }
4528
4531
  async discover() {
4529
- const received = await this.#request("server/discover", void 0, this.#probe, this.#version ?? this.#offer);
4532
+ const received = await this.#request("server/discover", void 0, this.#timeout, this.#version ?? this.#offer);
4530
4533
  const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(received));
4531
4534
  if (!owned.success) throw new MCPError("MCP server returned a malformed discovery result", JSONRPC_INVALID_PARAMS);
4532
4535
  const result = owned.value;
@@ -4736,7 +4739,13 @@ var MCPClient = class {
4736
4739
  await this.#initialize(generation, MCP_PROTOCOL_VERSION);
4737
4740
  return;
4738
4741
  }
4739
- const version = inferVersion(discovery.supportedVersions);
4742
+ let version;
4743
+ if (this.#pin === void 0) version = inferVersion(discovery.supportedVersions);
4744
+ else if (discovery.supportedVersions.includes(this.#pin)) version = this.#pin;
4745
+ else throw new MCPError("MCP server does not support the pinned protocol version", MCP_UNSUPPORTED_VERSION, {
4746
+ supported: discovery.supportedVersions,
4747
+ requested: this.#pin
4748
+ });
4740
4749
  if (version === void 0) throw new MCPError("MCP server supports no compatible protocol version", MCP_UNSUPPORTED_VERSION, { supported: discovery.supportedVersions });
4741
4750
  if (generation !== this.#generation) throw new Error("MCP client disconnected");
4742
4751
  this.#version = version;
@@ -4812,6 +4821,10 @@ var MCPClient = class {
4812
4821
  if (protocol === void 0) throw new Error("MCP server returned no protocol version");
4813
4822
  if (!(0, _orkestrel_contract.isString)(protocol)) throw new Error("MCP server returned a malformed protocol version");
4814
4823
  if (!isMCPVersion(protocol) || inferEra(protocol) !== "legacy") throw new Error(`MCP server negotiated unsupported protocol version '${protocol}'`);
4824
+ if (this.#pin !== void 0 && protocol !== this.#pin) throw new MCPError("MCP server negotiated a different protocol version than the client pinned", MCP_UNSUPPORTED_VERSION, {
4825
+ requested: this.#pin,
4826
+ negotiated: protocol
4827
+ });
4815
4828
  if (generation !== this.#generation) throw new Error("MCP client disconnected");
4816
4829
  await Promise.race([this.#transport.send({
4817
4830
  jsonrpc: "2.0",
@@ -4845,7 +4858,7 @@ var MCPClient = class {
4845
4858
  //#endregion
4846
4859
  //#region src/core/factories.ts
4847
4860
  /**
4848
- * Create a transport-agnostic Model Context Protocol server — exposes a live
4861
+ * Creates a transport-agnostic Model Context Protocol server — exposes a live
4849
4862
  * {@link import('@orkestrel/tool').ToolManagerInterface} and an optional
4850
4863
  * {@link import('./types.js').MCPResourceManagerInterface},
4851
4864
  * {@link import('./types.js').MCPPromptManagerInterface}, and
@@ -4858,7 +4871,7 @@ var MCPClient = class {
4858
4871
  * and no model. The {@link import('@orkestrel/tool').ToolManagerInterface} already
4859
4872
  * isolates a thrown tool into a `success: false` result (surfaced as an MCP
4860
4873
  * `isError: true` tool result), so a misbehaving tool never crashes a dispatch. Subscribe to the
4861
- * `request` event via `server.emitter.on('request', …)` for tracing.
4874
+ * `request` event through `server.emitter.on('request', …)` for tracing.
4862
4875
  *
4863
4876
  * @param options - `identity` (the server identity), `tools` (the live tool
4864
4877
  * registry), optional `resources` (the consumer-owned resource registry), optional
@@ -4870,7 +4883,8 @@ var MCPClient = class {
4870
4883
  *
4871
4884
  * @example
4872
4885
  * ```ts
4873
- * import { createMCPServer, createTool, createToolManager } from '@src/core'
4886
+ * import { createMCPServer } from '@orkestrel/mcp'
4887
+ * import { createTool, createToolManager } from '@orkestrel/tool'
4874
4888
  *
4875
4889
  * const tools = createToolManager()
4876
4890
  * tools.add(createTool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
@@ -4887,7 +4901,7 @@ function createMCPServer(options) {
4887
4901
  return new MCPServer(options);
4888
4902
  }
4889
4903
  /**
4890
- * Decorate one MCP server with the fixed legacy method translation.
4904
+ * Decorates one MCP server with the fixed legacy method translation.
4891
4905
  *
4892
4906
  * @param server - The sole modern dispatcher and handshake identity source
4893
4907
  * @returns A dispatcher accepting both modern and legacy invocations
@@ -4899,7 +4913,7 @@ function createMCPLegacy(server) {
4899
4913
  });
4900
4914
  }
4901
4915
  /**
4902
- * Create a transport-agnostic Model Context Protocol CLIENT — connects to a REMOTE
4916
+ * Creates a transport-agnostic Model Context Protocol CLIENT — connects to a REMOTE
4903
4917
  * MCP server over an injected {@link import('./types.js').MCPClientTransportInterface},
4904
4918
  * runs the `initialize` handshake, and exposes the server's tools as local
4905
4919
  * {@link import('@orkestrel/tool').ToolInterface}s an agent can run.
@@ -4913,8 +4927,7 @@ function createMCPLegacy(server) {
4913
4927
  * locally, so an agent's {@link import('@orkestrel/tool').ToolManagerInterface}
4914
4928
  * isolates it). The transport is injected — a concrete one (the HTTP transport over
4915
4929
  * `fetch`) lives in the published server environment; the client itself is provider-agnostic. Subscribe
4916
- * to `connect` / `disconnect` / `notification` via `client.on(...)` (or
4917
- * `client.emitter.on(...)`).
4930
+ * to `connect` / `disconnect` / `notification` through `client.emitter.on(...)`.
4918
4931
  *
4919
4932
  * @param options - `transport` (the carrier; REQUIRED), an optional `identity`
4920
4933
  * (the client identity), `timeout` (the per-request deadline), and the reserved `on`
@@ -4923,7 +4936,7 @@ function createMCPLegacy(server) {
4923
4936
  *
4924
4937
  * @example
4925
4938
  * ```ts
4926
- * import { createMCPClient } from '@src/core'
4939
+ * import { createMCPClient } from '@orkestrel/mcp'
4927
4940
  * import { createHTTPClientTransport } from '@orkestrel/mcp/server'
4928
4941
  *
4929
4942
  * const client = createMCPClient({
@@ -4938,7 +4951,7 @@ function createMCPClient(options) {
4938
4951
  return new MCPClient(options);
4939
4952
  }
4940
4953
  /**
4941
- * Adapt an {@link MCPTransportInterface} (the environment-agnostic duplex message
4954
+ * Adapts an {@link MCPTransportInterface} (the environment-agnostic duplex message
4942
4955
  * channel) into a {@link MCPClientTransportInterface} — the additive bridge that lets
4943
4956
  * `createMCPClient` run over the new port without any change to `MCPClient`'s
4944
4957
  * existing shape.
@@ -4946,7 +4959,7 @@ function createMCPClient(options) {
4946
4959
  * @remarks
4947
4960
  * Hand the RESULT to `createMCPClient({ transport })`, then pass the SAME
4948
4961
  * `transport` to {@link import('./helpers.js').bindClient} to complete the inbound
4949
- * wiring: `send` serializes each outbound {@link JSONRPCMessage} and writes it via
4962
+ * wiring: `send` serializes each outbound {@link JSONRPCMessage} and writes it through
4950
4963
  * `transport.send`; `close` closes the underlying
4951
4964
  * `transport`; `start` is a no-op (the duplex channel is already open by the time
4952
4965
  * it is handed in — there is no separate connect step at this layer); `session` is
@@ -4990,7 +5003,6 @@ exports.DEFAULT_MCP_CACHE_TTL = DEFAULT_MCP_CACHE_TTL;
4990
5003
  exports.DEFAULT_MCP_CLIENT_NAME = DEFAULT_MCP_CLIENT_NAME;
4991
5004
  exports.DEFAULT_MCP_CLIENT_VERSION = DEFAULT_MCP_CLIENT_VERSION;
4992
5005
  exports.DEFAULT_MCP_LIMITS = DEFAULT_MCP_LIMITS;
4993
- exports.DEFAULT_MCP_PROBE_TIMEOUT = DEFAULT_MCP_PROBE_TIMEOUT;
4994
5006
  exports.DEFAULT_MCP_REQUEST_TIMEOUT = DEFAULT_MCP_REQUEST_TIMEOUT;
4995
5007
  exports.EMPTY_MCP_ARGUMENTS = EMPTY_MCP_ARGUMENTS;
4996
5008
  exports.JSONRPC_INTERNAL_ERROR = JSONRPC_INTERNAL_ERROR;