@orkestrel/mcp 0.0.30 → 0.0.32

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.
@@ -275,9 +275,12 @@ var DEFAULT_MCP_SUBSCRIPTION_CAPACITY = 64;
275
275
  * optional structured context.
276
276
  *
277
277
  * @remarks
278
- * {@link MCPClient} throws this error for a remote JSON-RPC `error` response and for a
279
- * locally detected protocol incompatibility. Local lifecycle and transport conditions such
280
- * as disconnects and request timeouts remain plain `Error`s. For a remote response, `context`
278
+ * {@link MCPClient} throws this error for a remote JSON-RPC `error` response, for a locally
279
+ * detected protocol incompatibility, and for a session-bound request refused because the
280
+ * client holds no connection — that one carries `-32600`, so a caller separates "reconnect"
281
+ * from "retry" without reading a message. Local lifecycle and transport conditions that
282
+ * settle a request already in flight, such as a disconnect and a request timeout, remain
283
+ * plain `Error`s. For a remote response, `context`
281
284
  * carries the optional `error.data` unchanged and is `undefined` when the peer omitted it.
282
285
  * This includes the modern reserved paths: `-32020` carries no context, `-32021` may carry
283
286
  * `requiredCapabilities`, and `-32022` carries the peer's `supported` revisions and
@@ -323,11 +326,7 @@ var MCPError = class extends Error {
323
326
  * ```
324
327
  */
325
328
  function isMCPError(value) {
326
- try {
327
- return value instanceof MCPError;
328
- } catch {
329
- return false;
330
- }
329
+ return (0, _orkestrel_contract.isInstance)(value, MCPError);
331
330
  }
332
331
  //#endregion
333
332
  //#region src/core/cloners.ts
@@ -578,7 +577,7 @@ function parseMCPInputState(value) {
578
577
  const name = parsed["name"];
579
578
  const digest = parsed["digest"];
580
579
  const state = parsed["state"];
581
- if (!(0, _orkestrel_contract.isString)(principal) || principal.length === 0 || !(0, _orkestrel_contract.isNumber)(expiry) || !Number.isFinite(expiry)) return;
580
+ if (!(0, _orkestrel_contract.isString)(principal) || principal.length === 0 || !(0, _orkestrel_contract.isFiniteNumber)(expiry)) return;
582
581
  if (!isJSONRPCId(id)) return void 0;
583
582
  if (!(0, _orkestrel_contract.isString)(version) || !(0, _orkestrel_contract.isString)(method) || !(0, _orkestrel_contract.isString)(name)) return void 0;
584
583
  if (!(0, _orkestrel_contract.isString)(digest) || !(0, _orkestrel_contract.isUndefined)(state) && !(0, _orkestrel_contract.isJSONValue)(state)) return void 0;
@@ -741,9 +740,9 @@ function serializeJSON(value, limits) {
741
740
  const limit = limits.bytes;
742
741
  const depth = limits.depth;
743
742
  const breadth = limits.keys;
744
- if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 0) return void 0;
745
- if (!Number.isFinite(depth) || !Number.isInteger(depth) || depth < 0) return void 0;
746
- if (breadth !== void 0 && (!Number.isFinite(breadth) || !Number.isInteger(breadth) || breadth < 0)) return void 0;
743
+ if (!(0, _orkestrel_contract.isInteger)(limit) || limit < 0) return void 0;
744
+ if (!(0, _orkestrel_contract.isInteger)(depth) || depth < 0) return void 0;
745
+ if (breadth !== void 0 && (!(0, _orkestrel_contract.isInteger)(breadth) || breadth < 0)) return void 0;
747
746
  let bytes = 0;
748
747
  let keys = 0;
749
748
  const chunks = [];
@@ -857,7 +856,7 @@ function serializeJSON(value, limits) {
857
856
  continue;
858
857
  }
859
858
  if ((0, _orkestrel_contract.isNumber)(entry)) {
860
- if (!Number.isFinite(entry)) return void 0;
859
+ if (!(0, _orkestrel_contract.isFiniteNumber)(entry)) return void 0;
861
860
  const text = JSON.stringify(entry);
862
861
  if (!(0, _orkestrel_contract.isString)(text)) return void 0;
863
862
  bytes += text.length;
@@ -873,10 +872,10 @@ function serializeJSON(value, limits) {
873
872
  });
874
873
  continue;
875
874
  }
876
- if (typeof entry !== "object" || ancestors.has(entry)) return void 0;
877
- if (Array.isArray(entry)) {
875
+ if (!(0, _orkestrel_contract.isObject)(entry) || ancestors.has(entry)) return void 0;
876
+ if ((0, _orkestrel_contract.isArray)(entry)) {
878
877
  const lengthDescriptor = Reflect.getOwnPropertyDescriptor(entry, "length");
879
- if (lengthDescriptor === void 0 || !Object.hasOwn(lengthDescriptor, "value") || !(0, _orkestrel_contract.isNumber)(lengthDescriptor.value) || !Number.isInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > 4294967295 || lengthDescriptor.enumerable === true || lengthDescriptor.configurable === true) return void 0;
878
+ if (lengthDescriptor === void 0 || !Object.hasOwn(lengthDescriptor, "value") || !(0, _orkestrel_contract.isNumber)(lengthDescriptor.value) || !(0, _orkestrel_contract.isInteger)(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > 4294967295 || lengthDescriptor.enumerable === true || lengthDescriptor.configurable === true) return void 0;
880
879
  const length = lengthDescriptor.value;
881
880
  keys += length;
882
881
  bytes += 2 + (length === 0 ? 0 : length - 1);
@@ -893,7 +892,7 @@ function serializeJSON(value, limits) {
893
892
  continue;
894
893
  }
895
894
  const index = Number(name);
896
- if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== name) return void 0;
895
+ if (!(0, _orkestrel_contract.isInteger)(index) || index < 0 || index >= length || String(index) !== name) return void 0;
897
896
  }
898
897
  if (!foundLength) return void 0;
899
898
  const values = [];
@@ -1114,10 +1113,9 @@ function buildCallOutcome(name, result) {
1114
1113
  if ((0, _orkestrel_contract.isRecord)(result) && result["isError"] === true) throw new Error(text.length > 0 ? text : `MCP tool '${name}' failed`);
1115
1114
  let value;
1116
1115
  if ((0, _orkestrel_contract.isRecord)(result) && Object.hasOwn(result, "structuredContent")) value = result["structuredContent"];
1117
- else if (text.length > 0) try {
1118
- value = JSON.parse(text);
1119
- } catch {
1120
- value = text;
1116
+ else if (text.length > 0) {
1117
+ const parsed = (0, _orkestrel_contract.parseJSON)(text);
1118
+ value = parsed === void 0 ? text : parsed;
1121
1119
  }
1122
1120
  return Object.freeze({
1123
1121
  resultType: "complete",
@@ -1128,19 +1126,17 @@ function buildCallOutcome(name, result) {
1128
1126
  * Builds the canonical Tool call for one validated MCP `tools/call` request.
1129
1127
  *
1130
1128
  * @param request - The original MCP request
1131
- * @param caller - Optional consumer-asserted caller context
1132
1129
  * @param args - Optional once-validated modern arguments; omission retains legacy normalization
1133
1130
  * @returns The canonical call, or `undefined` when the name is invalid
1134
1131
  */
1135
- function buildToolCall(request, caller, args) {
1132
+ function buildToolCall(request, args) {
1136
1133
  const name = request.params?.["name"];
1137
- if (typeof name !== "string") return void 0;
1134
+ if (!(0, _orkestrel_contract.isString)(name)) return void 0;
1138
1135
  const rawArguments = request.params?.["arguments"];
1139
1136
  return {
1140
1137
  id: String(request.id),
1141
1138
  name,
1142
- arguments: args ?? ((0, _orkestrel_contract.isRecord)(rawArguments) ? rawArguments : {}),
1143
- ...caller === void 0 ? {} : { caller }
1139
+ arguments: args ?? ((0, _orkestrel_contract.isRecord)(rawArguments) ? rawArguments : {})
1144
1140
  };
1145
1141
  }
1146
1142
  /**
@@ -1231,7 +1227,8 @@ function buildMethodOptions(options, lifetime) {
1231
1227
  *
1232
1228
  * @remarks
1233
1229
  * Each {@link import('@orkestrel/tool').ToolDefinition} carries through its
1234
- * `name` and (when present) `description`; its open JSON-Schema `parameters`
1230
+ * `name`, optional `title` and `description`, and mapped annotation hints;
1231
+ * its open JSON-Schema `parameters`
1235
1232
  * becomes `inputSchema`, defaulting to an empty object schema (`{ type: 'object' }`)
1236
1233
  * when a tool declares none (MCP requires an `inputSchema`).
1237
1234
  *
@@ -1240,14 +1237,50 @@ function buildMethodOptions(options, lifetime) {
1240
1237
  */
1241
1238
  function buildToolDescriptors(manager) {
1242
1239
  return manager.definitions().map((definition) => {
1243
- const descriptor = {
1240
+ const annotations = definition.annotations === void 0 ? {} : toolAnnotationsToMCP(definition.annotations);
1241
+ return {
1244
1242
  name: definition.name,
1245
- inputSchema: definition.parameters ?? { type: "object" }
1243
+ inputSchema: definition.parameters ?? { type: "object" },
1244
+ ...definition.title === void 0 ? {} : { title: definition.title },
1245
+ ...definition.description === void 0 ? {} : { description: definition.description },
1246
+ ...Object.keys(annotations).length === 0 ? {} : { annotations }
1246
1247
  };
1247
- if (definition.description !== void 0) descriptor.description = definition.description;
1248
- return descriptor;
1249
1248
  });
1250
1249
  }
1250
+ /**
1251
+ * Projects domain tool annotations onto MCP wire hints without inventing defaults.
1252
+ *
1253
+ * @param annotations - The authored domain annotations
1254
+ * @returns The mapped hints; `untrusted` has no MCP counterpart
1255
+ *
1256
+ * @example
1257
+ * ```ts
1258
+ * toolAnnotationsToMCP({ pure: true, consequential: false }) // { readOnlyHint: true, destructiveHint: false }
1259
+ * ```
1260
+ */
1261
+ function toolAnnotationsToMCP(annotations) {
1262
+ return {
1263
+ ...annotations.pure === void 0 ? {} : { readOnlyHint: annotations.pure },
1264
+ ...annotations.consequential === void 0 ? {} : { destructiveHint: annotations.consequential }
1265
+ };
1266
+ }
1267
+ /**
1268
+ * Projects MCP wire hints onto domain tool annotations without inventing defaults.
1269
+ *
1270
+ * @param annotations - The validated MCP wire annotations
1271
+ * @returns The mapped annotations; unmapped wire hints are omitted
1272
+ *
1273
+ * @example
1274
+ * ```ts
1275
+ * mcpAnnotationsToTool({ readOnlyHint: false, destructiveHint: true }) // { pure: false, consequential: true }
1276
+ * ```
1277
+ */
1278
+ function mcpAnnotationsToTool(annotations) {
1279
+ return {
1280
+ ...annotations.readOnlyHint === void 0 ? {} : { pure: annotations.readOnlyHint },
1281
+ ...annotations.destructiveHint === void 0 ? {} : { consequential: annotations.destructiveHint }
1282
+ };
1283
+ }
1251
1284
  function buildModernResult(result, identity, ttl, scope) {
1252
1285
  const currentMetadata = (0, _orkestrel_contract.isRecord)(result) ? result["_meta"] : void 0;
1253
1286
  if (!(0, _orkestrel_contract.isJSONValue)(identity)) throw new TypeError("MCP identity must be exact JSON");
@@ -1283,7 +1316,7 @@ function modernResultToLegacy(result) {
1283
1316
  const projected = {};
1284
1317
  for (const [key, value] of Object.entries(result)) {
1285
1318
  if (key === "resultType" || key === "ttlMs" || key === "cacheScope") continue;
1286
- if (key === "content" && Array.isArray(value)) {
1319
+ if (key === "content" && (0, _orkestrel_contract.isArray)(value)) {
1287
1320
  projected[key] = value.map((entry) => (0, _orkestrel_contract.isRecord)(entry) && entry["type"] === "text" && (0, _orkestrel_contract.isString)(entry["text"]) ? {
1288
1321
  type: "text",
1289
1322
  text: entry["text"]
@@ -1396,7 +1429,7 @@ function matchesSubscriptionNotification(notification, filter) {
1396
1429
  if (notification.method === "notifications/resources/list_changed") return filter.resourcesListChanged === true;
1397
1430
  if (notification.method === "notifications/resources/updated") {
1398
1431
  const uri = notification.params?.["uri"];
1399
- return typeof uri === "string" && filter.resourceSubscriptions?.includes(uri) === true;
1432
+ return (0, _orkestrel_contract.isString)(uri) && filter.resourceSubscriptions?.includes(uri) === true;
1400
1433
  }
1401
1434
  if (notification.method === "notifications/tasks") return isMCPTaskNotification(notification) && filter.taskIds?.includes(notification.params.taskId) === true;
1402
1435
  return false;
@@ -1452,10 +1485,11 @@ function buildSubscriptionResult(id, identity) {
1452
1485
  * @remarks
1453
1486
  * `capabilities.resources` and `capabilities.prompts` appear only for servers with their
1454
1487
  * respective managers and derive notification flags from the configured subscription filter.
1455
- * `capabilities.completions` is independent and appears only with a completion provider.
1456
- * `capabilities.extensions` appears only for a server that configured the extension it
1457
- * would name. An advertisement is a promise a client is entitled to act on, so a server
1458
- * with no `task` policy omits the member entirely rather than advertising an empty
1488
+ * `tools.listChanged` is always advertised because the server produces the family from its
1489
+ * own registry. `capabilities.completions` is independent and appears only with a completion
1490
+ * provider. `capabilities.extensions` appears only for a server that configured the
1491
+ * extension it would name. An advertisement is a promise a client is entitled to act on, so
1492
+ * a server with no `task` policy omits the member entirely rather than advertising an empty
1459
1493
  * record — and its discovery answer stays byte-for-byte what it was before the extension
1460
1494
  * existed.
1461
1495
  *
@@ -1466,7 +1500,7 @@ function buildDiscoverResult(options) {
1466
1500
  return buildModernResult({
1467
1501
  supportedVersions: SUPPORTED_MODERN_PROTOCOL_VERSIONS,
1468
1502
  capabilities: {
1469
- tools: {},
1503
+ tools: { listChanged: true },
1470
1504
  ...options.resources === void 0 ? {} : { resources: {
1471
1505
  ...options.subscription?.notifications.resourceSubscriptions === void 0 ? {} : { subscribe: true },
1472
1506
  ...options.subscription?.notifications.resourcesListChanged === true ? { listChanged: true } : {}
@@ -2109,7 +2143,7 @@ function bindServer(server, transport) {
2109
2143
  try {
2110
2144
  const answer = await server.handle(message, { signal: request.signal });
2111
2145
  if (answer === void 0) return;
2112
- if (typeof answer === "string") {
2146
+ if ((0, _orkestrel_contract.isString)(answer)) {
2113
2147
  if (!request.signal.aborted) await transport.send(answer);
2114
2148
  } else if (request.signal.aborted) await answer[Symbol.asyncDispose]();
2115
2149
  else await sendStream(answer, transport);
@@ -2510,7 +2544,7 @@ function isMCPAnnotations(value) {
2510
2544
  const audience = annotations["audience"];
2511
2545
  const priority = annotations["priority"];
2512
2546
  const lastModified = annotations["lastModified"];
2513
- if (!(0, _orkestrel_contract.isUndefined)(audience) && (!Array.isArray(audience) || !audience.every((role) => role === "user" || role === "assistant"))) return false;
2547
+ if (!(0, _orkestrel_contract.isUndefined)(audience) && (!(0, _orkestrel_contract.isArray)(audience) || !audience.every((role) => role === "user" || role === "assistant"))) return false;
2514
2548
  if (!(0, _orkestrel_contract.isUndefined)(priority) && (!(0, _orkestrel_contract.isFiniteNumber)(priority) || priority < 0 || priority > 1)) return false;
2515
2549
  return (0, _orkestrel_contract.isUndefined)(lastModified) || (0, _orkestrel_contract.isString)(lastModified);
2516
2550
  } catch {
@@ -2532,7 +2566,7 @@ function isMCPIcon(value) {
2532
2566
  const mimeType = icon["mimeType"];
2533
2567
  const sizes = icon["sizes"];
2534
2568
  const theme = icon["theme"];
2535
- return ((0, _orkestrel_contract.isUndefined)(mimeType) || (0, _orkestrel_contract.isString)(mimeType)) && ((0, _orkestrel_contract.isUndefined)(sizes) || Array.isArray(sizes) && sizes.every((size) => (0, _orkestrel_contract.isString)(size))) && ((0, _orkestrel_contract.isUndefined)(theme) || theme === "light" || theme === "dark");
2569
+ return ((0, _orkestrel_contract.isUndefined)(mimeType) || (0, _orkestrel_contract.isString)(mimeType)) && ((0, _orkestrel_contract.isUndefined)(sizes) || (0, _orkestrel_contract.isArray)(sizes) && sizes.every((size) => (0, _orkestrel_contract.isString)(size))) && ((0, _orkestrel_contract.isUndefined)(theme) || theme === "light" || theme === "dark");
2536
2570
  } catch {
2537
2571
  return false;
2538
2572
  }
@@ -2548,7 +2582,7 @@ function isMCPIdentity(value) {
2548
2582
  const description = identity["description"];
2549
2583
  const website = identity["websiteUrl"];
2550
2584
  const icons = identity["icons"];
2551
- return ((0, _orkestrel_contract.isUndefined)(title) || (0, _orkestrel_contract.isString)(title)) && ((0, _orkestrel_contract.isUndefined)(description) || (0, _orkestrel_contract.isString)(description)) && ((0, _orkestrel_contract.isUndefined)(website) || isAbsoluteURI(website)) && ((0, _orkestrel_contract.isUndefined)(icons) || Array.isArray(icons) && icons.every((icon) => isMCPIcon(icon)));
2585
+ return ((0, _orkestrel_contract.isUndefined)(title) || (0, _orkestrel_contract.isString)(title)) && ((0, _orkestrel_contract.isUndefined)(description) || (0, _orkestrel_contract.isString)(description)) && ((0, _orkestrel_contract.isUndefined)(website) || isAbsoluteURI(website)) && ((0, _orkestrel_contract.isUndefined)(icons) || (0, _orkestrel_contract.isArray)(icons) && icons.every((icon) => isMCPIcon(icon)));
2552
2586
  } catch {
2553
2587
  return false;
2554
2588
  }
@@ -2659,12 +2693,34 @@ function isMCPResource(value) {
2659
2693
  const resource = owned.value;
2660
2694
  const icons = resource["icons"];
2661
2695
  const size = resource["size"];
2662
- return isAbsoluteURI(resource["uri"]) && (0, _orkestrel_contract.isString)(resource["name"]) && ((0, _orkestrel_contract.isUndefined)(resource["title"]) || (0, _orkestrel_contract.isString)(resource["title"])) && ((0, _orkestrel_contract.isUndefined)(resource["description"]) || (0, _orkestrel_contract.isString)(resource["description"])) && ((0, _orkestrel_contract.isUndefined)(resource["mimeType"]) || (0, _orkestrel_contract.isString)(resource["mimeType"])) && ((0, _orkestrel_contract.isUndefined)(resource["annotations"]) || isMCPAnnotations(resource["annotations"])) && ((0, _orkestrel_contract.isUndefined)(size) || (0, _orkestrel_contract.isInteger)(size) && size >= 0) && ((0, _orkestrel_contract.isUndefined)(icons) || Array.isArray(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(resource["_meta"]) || isMCPMetaObject(resource["_meta"]));
2696
+ return isAbsoluteURI(resource["uri"]) && (0, _orkestrel_contract.isString)(resource["name"]) && ((0, _orkestrel_contract.isUndefined)(resource["title"]) || (0, _orkestrel_contract.isString)(resource["title"])) && ((0, _orkestrel_contract.isUndefined)(resource["description"]) || (0, _orkestrel_contract.isString)(resource["description"])) && ((0, _orkestrel_contract.isUndefined)(resource["mimeType"]) || (0, _orkestrel_contract.isString)(resource["mimeType"])) && ((0, _orkestrel_contract.isUndefined)(resource["annotations"]) || isMCPAnnotations(resource["annotations"])) && ((0, _orkestrel_contract.isUndefined)(size) || (0, _orkestrel_contract.isInteger)(size) && size >= 0) && ((0, _orkestrel_contract.isUndefined)(icons) || (0, _orkestrel_contract.isArray)(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(resource["_meta"]) || isMCPMetaObject(resource["_meta"]));
2663
2697
  } catch {
2664
2698
  return false;
2665
2699
  }
2666
2700
  }
2667
2701
  /**
2702
+ * Checks whether a value carries valid consumed MCP 2026-07-28 tool annotation hints.
2703
+ *
2704
+ * @remarks
2705
+ * Validates only `readOnlyHint` and `destructiveHint`; other wire fields are not consumed, so
2706
+ * the open `objectOf` combinator carries them through unread. Every declared member is
2707
+ * optional, a hostile read answers `false` rather than throwing, and an array is refused.
2708
+ *
2709
+ * @param value - The unknown wire annotations
2710
+ * @returns True if consumed annotation fields are optional booleans; false otherwise
2711
+ *
2712
+ * @example
2713
+ * ```ts
2714
+ * isMCPToolAnnotations({ readOnlyHint: false }) // true
2715
+ * ```
2716
+ */
2717
+ function isMCPToolAnnotations(value) {
2718
+ return (0, _orkestrel_contract.objectOf)({
2719
+ readOnlyHint: _orkestrel_contract.isBoolean,
2720
+ destructiveHint: _orkestrel_contract.isBoolean
2721
+ }, true)(value);
2722
+ }
2723
+ /**
2668
2724
  * Determines whether a value is one resource-template descriptor.
2669
2725
  *
2670
2726
  * @remarks
@@ -2680,7 +2736,7 @@ function isMCPResourceTemplate(value) {
2680
2736
  try {
2681
2737
  const template = owned.value;
2682
2738
  const icons = template["icons"];
2683
- return (0, _orkestrel_contract.isString)(template["uriTemplate"]) && template["uriTemplate"].length > 0 && (0, _orkestrel_contract.isString)(template["name"]) && ((0, _orkestrel_contract.isUndefined)(template["title"]) || (0, _orkestrel_contract.isString)(template["title"])) && ((0, _orkestrel_contract.isUndefined)(template["description"]) || (0, _orkestrel_contract.isString)(template["description"])) && ((0, _orkestrel_contract.isUndefined)(template["mimeType"]) || (0, _orkestrel_contract.isString)(template["mimeType"])) && ((0, _orkestrel_contract.isUndefined)(template["annotations"]) || isMCPAnnotations(template["annotations"])) && ((0, _orkestrel_contract.isUndefined)(icons) || Array.isArray(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(template["_meta"]) || isMCPMetaObject(template["_meta"]));
2739
+ return (0, _orkestrel_contract.isString)(template["uriTemplate"]) && template["uriTemplate"].length > 0 && (0, _orkestrel_contract.isString)(template["name"]) && ((0, _orkestrel_contract.isUndefined)(template["title"]) || (0, _orkestrel_contract.isString)(template["title"])) && ((0, _orkestrel_contract.isUndefined)(template["description"]) || (0, _orkestrel_contract.isString)(template["description"])) && ((0, _orkestrel_contract.isUndefined)(template["mimeType"]) || (0, _orkestrel_contract.isString)(template["mimeType"])) && ((0, _orkestrel_contract.isUndefined)(template["annotations"]) || isMCPAnnotations(template["annotations"])) && ((0, _orkestrel_contract.isUndefined)(icons) || (0, _orkestrel_contract.isArray)(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(template["_meta"]) || isMCPMetaObject(template["_meta"]));
2684
2740
  } catch {
2685
2741
  return false;
2686
2742
  }
@@ -2720,7 +2776,7 @@ function isMCPResourcePage(value) {
2720
2776
  if (!owned.success) return false;
2721
2777
  const resources = owned.value["resources"];
2722
2778
  const cursor = owned.value["nextCursor"];
2723
- return Array.isArray(resources) && resources.every((resource) => isMCPResource(resource)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2779
+ return (0, _orkestrel_contract.isArray)(resources) && resources.every((resource) => isMCPResource(resource)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2724
2780
  }
2725
2781
  /**
2726
2782
  * Determines whether a value is one consumer-owned resource-template page.
@@ -2733,7 +2789,7 @@ function isMCPResourceTemplatePage(value) {
2733
2789
  if (!owned.success) return false;
2734
2790
  const templates = owned.value["resourceTemplates"];
2735
2791
  const cursor = owned.value["nextCursor"];
2736
- return Array.isArray(templates) && templates.every((template) => isMCPResourceTemplate(template)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2792
+ return (0, _orkestrel_contract.isArray)(templates) && templates.every((template) => isMCPResourceTemplate(template)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2737
2793
  }
2738
2794
  /**
2739
2795
  * Determines whether a value is a string-valued MCP argument record.
@@ -2769,7 +2825,7 @@ function isMCPPrompt(value) {
2769
2825
  const prompt = owned.value;
2770
2826
  const argumentsValue = prompt["arguments"];
2771
2827
  const icons = prompt["icons"];
2772
- 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"]));
2828
+ 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) || (0, _orkestrel_contract.isArray)(argumentsValue) && argumentsValue.every((argument) => isMCPPromptArgument(argument))) && ((0, _orkestrel_contract.isUndefined)(icons) || (0, _orkestrel_contract.isArray)(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(prompt["_meta"]) || isMCPMetaObject(prompt["_meta"]));
2773
2829
  }
2774
2830
  /**
2775
2831
  * Determines whether a value is one prompt message with existing rich content.
@@ -2792,7 +2848,7 @@ function isMCPPromptPage(value) {
2792
2848
  if (!owned.success) return false;
2793
2849
  const prompts = owned.value["prompts"];
2794
2850
  const cursor = owned.value["nextCursor"];
2795
- return Array.isArray(prompts) && prompts.every((prompt) => isMCPPrompt(prompt)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2851
+ return (0, _orkestrel_contract.isArray)(prompts) && prompts.every((prompt) => isMCPPrompt(prompt)) && ((0, _orkestrel_contract.isUndefined)(cursor) || (0, _orkestrel_contract.isString)(cursor));
2796
2852
  }
2797
2853
  /**
2798
2854
  * Determines whether a value is one complete `prompts/get` result.
@@ -2805,7 +2861,7 @@ function isMCPPromptGetResult(value) {
2805
2861
  if (!owned.success) return false;
2806
2862
  const result = owned.value;
2807
2863
  const messages = result["messages"];
2808
- 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"]));
2864
+ return result["resultType"] === "complete" && ((0, _orkestrel_contract.isUndefined)(result["description"]) || (0, _orkestrel_contract.isString)(result["description"])) && (0, _orkestrel_contract.isArray)(messages) && messages.every((message) => isMCPPromptMessage(message)) && ((0, _orkestrel_contract.isUndefined)(result["_meta"]) || isMCPResultMetaObject(result["_meta"]));
2809
2865
  }
2810
2866
  /**
2811
2867
  * Determines whether a value is a prompt or resource-template completion reference.
@@ -2850,7 +2906,7 @@ function isMCPCompletion(value) {
2850
2906
  const completion = owned.value;
2851
2907
  const values = completion["values"];
2852
2908
  const total = completion["total"];
2853
- 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"]));
2909
+ return (0, _orkestrel_contract.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"]));
2854
2910
  }
2855
2911
  /**
2856
2912
  * Determines whether a value is one complete, capped `completion/complete` result.
@@ -2888,7 +2944,7 @@ function isMCPContent(value) {
2888
2944
  case "resource_link": {
2889
2945
  const icons = content["icons"];
2890
2946
  const size = content["size"];
2891
- return (0, _orkestrel_contract.isString)(content["name"]) && isAbsoluteURI(content["uri"]) && ((0, _orkestrel_contract.isUndefined)(content["title"]) || (0, _orkestrel_contract.isString)(content["title"])) && ((0, _orkestrel_contract.isUndefined)(content["description"]) || (0, _orkestrel_contract.isString)(content["description"])) && ((0, _orkestrel_contract.isUndefined)(content["mimeType"]) || (0, _orkestrel_contract.isString)(content["mimeType"])) && ((0, _orkestrel_contract.isUndefined)(icons) || Array.isArray(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(size) || (0, _orkestrel_contract.isInteger)(size) && size >= 0);
2947
+ return (0, _orkestrel_contract.isString)(content["name"]) && isAbsoluteURI(content["uri"]) && ((0, _orkestrel_contract.isUndefined)(content["title"]) || (0, _orkestrel_contract.isString)(content["title"])) && ((0, _orkestrel_contract.isUndefined)(content["description"]) || (0, _orkestrel_contract.isString)(content["description"])) && ((0, _orkestrel_contract.isUndefined)(content["mimeType"]) || (0, _orkestrel_contract.isString)(content["mimeType"])) && ((0, _orkestrel_contract.isUndefined)(icons) || (0, _orkestrel_contract.isArray)(icons) && icons.every((icon) => isMCPIcon(icon))) && ((0, _orkestrel_contract.isUndefined)(size) || (0, _orkestrel_contract.isInteger)(size) && size >= 0);
2892
2948
  }
2893
2949
  default: return false;
2894
2950
  }
@@ -2962,7 +3018,7 @@ function isMCPCallResult(value) {
2962
3018
  const content = result["content"];
2963
3019
  const error = result["isError"];
2964
3020
  const metadata = result["_meta"];
2965
- return Array.isArray(content) && content.every((entry) => isMCPContent(entry)) && ((0, _orkestrel_contract.isUndefined)(error) || (0, _orkestrel_contract.isBoolean)(error)) && ((0, _orkestrel_contract.isUndefined)(metadata) || isMCPResultMetaObject(metadata));
3021
+ return (0, _orkestrel_contract.isArray)(content) && content.every((entry) => isMCPContent(entry)) && ((0, _orkestrel_contract.isUndefined)(error) || (0, _orkestrel_contract.isBoolean)(error)) && ((0, _orkestrel_contract.isUndefined)(metadata) || isMCPResultMetaObject(metadata));
2966
3022
  } catch {
2967
3023
  return false;
2968
3024
  }
@@ -3151,7 +3207,7 @@ function isMCPTaskNotification(value) {
3151
3207
  * ```
3152
3208
  */
3153
3209
  function isBoundedString(value, bytes) {
3154
- if (!(0, _orkestrel_contract.isString)(value) || !Number.isFinite(bytes) || !Number.isInteger(bytes) || bytes < 0) return false;
3210
+ if (!(0, _orkestrel_contract.isString)(value) || !(0, _orkestrel_contract.isInteger)(bytes) || bytes < 0) return false;
3155
3211
  let measured = 0;
3156
3212
  for (let index = 0; index < value.length; index += 1) {
3157
3213
  const code = value.charCodeAt(index);
@@ -3273,6 +3329,22 @@ function isMCPSubscriptionFilter(value) {
3273
3329
  return (0, _orkestrel_contract.isUndefined)(tasks) || (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)(tasks);
3274
3330
  }
3275
3331
  /**
3332
+ * Checks whether a subscription filter leaves the built-in tools family to the server.
3333
+ *
3334
+ * @param value - The unknown value to inspect
3335
+ * @returns True if the filter is valid and does not claim tools changes; false otherwise
3336
+ *
3337
+ * @example
3338
+ * ```ts
3339
+ * isMCPConsumerFilter({ promptsListChanged: true }) // true
3340
+ * isMCPConsumerFilter({ toolsListChanged: true }) // false
3341
+ * ```
3342
+ */
3343
+ function isMCPConsumerFilter(value) {
3344
+ const owned = (0, _orkestrel_contract.attempt)(() => (0, _orkestrel_contract.cloneJSONRecord)(value));
3345
+ return owned.success && isMCPSubscriptionFilter(owned.value) && owned.value["toolsListChanged"] !== true;
3346
+ }
3347
+ /**
3276
3348
  * Determines whether a value is a graceful `subscriptions/listen` result.
3277
3349
  *
3278
3350
  * @param value - The unknown value to inspect
@@ -3320,21 +3392,21 @@ function isMCPElicitFieldSchema(value) {
3320
3392
  const names = schema["enumNames"];
3321
3393
  const titled = schema["oneOf"];
3322
3394
  if (!(0, _orkestrel_contract.isUndefined)(minimum) && (!(0, _orkestrel_contract.isInteger)(minimum) || minimum < 0) || !(0, _orkestrel_contract.isUndefined)(maximum) && (!(0, _orkestrel_contract.isInteger)(maximum) || maximum < 0) || !(0, _orkestrel_contract.isUndefined)(fallback) && !(0, _orkestrel_contract.isString)(fallback) || !(0, _orkestrel_contract.isUndefined)(format) && format !== "uri" && format !== "email" && format !== "date" && format !== "date-time") return false;
3323
- if (!(0, _orkestrel_contract.isUndefined)(choices) && (!Array.isArray(choices) || !choices.every(_orkestrel_contract.isString))) return false;
3324
- if (!(0, _orkestrel_contract.isUndefined)(names) && ((0, _orkestrel_contract.isUndefined)(choices) || !Array.isArray(names) || !names.every(_orkestrel_contract.isString))) return false;
3325
- return !(!(0, _orkestrel_contract.isUndefined)(titled) && (!Array.isArray(titled) || !titled.every((choice) => isJSONObject(choice) && (0, _orkestrel_contract.isString)(choice["const"]) && (0, _orkestrel_contract.isString)(choice["title"]))));
3395
+ if (!(0, _orkestrel_contract.isUndefined)(choices) && (!(0, _orkestrel_contract.isArray)(choices) || !choices.every(_orkestrel_contract.isString))) return false;
3396
+ if (!(0, _orkestrel_contract.isUndefined)(names) && ((0, _orkestrel_contract.isUndefined)(choices) || !(0, _orkestrel_contract.isArray)(names) || !names.every(_orkestrel_contract.isString))) return false;
3397
+ return !(!(0, _orkestrel_contract.isUndefined)(titled) && (!(0, _orkestrel_contract.isArray)(titled) || !titled.every((choice) => isJSONObject(choice) && (0, _orkestrel_contract.isString)(choice["const"]) && (0, _orkestrel_contract.isString)(choice["title"]))));
3326
3398
  }
3327
3399
  if (schema["type"] !== "array") return false;
3328
3400
  const minimum = schema["minItems"];
3329
3401
  const maximum = schema["maxItems"];
3330
3402
  const items = schema["items"];
3331
- if (!(0, _orkestrel_contract.isUndefined)(minimum) && (!(0, _orkestrel_contract.isInteger)(minimum) || minimum < 0) || !(0, _orkestrel_contract.isUndefined)(maximum) && (!(0, _orkestrel_contract.isInteger)(maximum) || maximum < 0) || !(0, _orkestrel_contract.isUndefined)(fallback) && (!Array.isArray(fallback) || !fallback.every((item) => (0, _orkestrel_contract.isString)(item))) || !isJSONObject(items)) return false;
3403
+ if (!(0, _orkestrel_contract.isUndefined)(minimum) && (!(0, _orkestrel_contract.isInteger)(minimum) || minimum < 0) || !(0, _orkestrel_contract.isUndefined)(maximum) && (!(0, _orkestrel_contract.isInteger)(maximum) || maximum < 0) || !(0, _orkestrel_contract.isUndefined)(fallback) && (!(0, _orkestrel_contract.isArray)(fallback) || !fallback.every((item) => (0, _orkestrel_contract.isString)(item))) || !isJSONObject(items)) return false;
3332
3404
  const itemType = items["type"];
3333
3405
  const choices = items["enum"];
3334
3406
  const titled = items["anyOf"];
3335
3407
  if (!(0, _orkestrel_contract.isUndefined)(itemType) && itemType !== "string") return false;
3336
- if (!(0, _orkestrel_contract.isUndefined)(choices) && (!Array.isArray(choices) || !choices.every(_orkestrel_contract.isString))) return false;
3337
- if (!(0, _orkestrel_contract.isUndefined)(titled) && (!Array.isArray(titled) || !titled.every((choice) => isJSONObject(choice) && (0, _orkestrel_contract.isString)(choice["const"]) && (0, _orkestrel_contract.isString)(choice["title"])))) return false;
3408
+ if (!(0, _orkestrel_contract.isUndefined)(choices) && (!(0, _orkestrel_contract.isArray)(choices) || !choices.every(_orkestrel_contract.isString))) return false;
3409
+ if (!(0, _orkestrel_contract.isUndefined)(titled) && (!(0, _orkestrel_contract.isArray)(titled) || !titled.every((choice) => isJSONObject(choice) && (0, _orkestrel_contract.isString)(choice["const"]) && (0, _orkestrel_contract.isString)(choice["title"])))) return false;
3338
3410
  return itemType === "string" && !(0, _orkestrel_contract.isUndefined)(choices) || !(0, _orkestrel_contract.isUndefined)(titled);
3339
3411
  } catch {
3340
3412
  return false;
@@ -3369,7 +3441,7 @@ function isMCPElicitSchema(value) {
3369
3441
  const dialect = schema["$schema"];
3370
3442
  if (!(0, _orkestrel_contract.isUndefined)(dialect) && !(0, _orkestrel_contract.isString)(dialect)) return false;
3371
3443
  const required = schema["required"];
3372
- return ((0, _orkestrel_contract.isUndefined)(required) || Array.isArray(required) && required.every((name) => (0, _orkestrel_contract.isString)(name))) && Object.values(properties).every((property) => isMCPElicitFieldSchema(property));
3444
+ return ((0, _orkestrel_contract.isUndefined)(required) || (0, _orkestrel_contract.isArray)(required) && required.every((name) => (0, _orkestrel_contract.isString)(name))) && Object.values(properties).every((property) => isMCPElicitFieldSchema(property));
3373
3445
  } catch {
3374
3446
  return false;
3375
3447
  }
@@ -3512,7 +3584,7 @@ function isMCPElicitResult(value) {
3512
3584
  if (action !== "accept") return (0, _orkestrel_contract.isUndefined)(content);
3513
3585
  if ((0, _orkestrel_contract.isUndefined)(content)) return true;
3514
3586
  if (!isJSONObject(content)) return false;
3515
- return Object.values(content).every((item) => (0, _orkestrel_contract.isString)(item) || (0, _orkestrel_contract.isFiniteNumber)(item) || (0, _orkestrel_contract.isBoolean)(item) || Array.isArray(item) && item.every((entry) => (0, _orkestrel_contract.isString)(entry)));
3587
+ return Object.values(content).every((item) => (0, _orkestrel_contract.isString)(item) || (0, _orkestrel_contract.isFiniteNumber)(item) || (0, _orkestrel_contract.isBoolean)(item) || (0, _orkestrel_contract.isArray)(item) && item.every((entry) => (0, _orkestrel_contract.isString)(entry)));
3516
3588
  } catch {
3517
3589
  return false;
3518
3590
  }
@@ -3567,11 +3639,11 @@ function isElicitContent(value, schema) {
3567
3639
  const properties = declared.value["properties"];
3568
3640
  const required = declared.value["required"];
3569
3641
  if (!isJSONObject(properties)) return false;
3570
- if (Array.isArray(required)) {
3642
+ if ((0, _orkestrel_contract.isArray)(required)) {
3571
3643
  for (const name of required) if (!(0, _orkestrel_contract.isString)(name) || !Object.hasOwn(content, name)) return false;
3572
3644
  }
3573
3645
  for (const [name, item] of Object.entries(content)) {
3574
- if (!(0, _orkestrel_contract.isString)(item) && !(0, _orkestrel_contract.isFiniteNumber)(item) && !(0, _orkestrel_contract.isBoolean)(item) && !(Array.isArray(item) && item.every((entry) => (0, _orkestrel_contract.isString)(entry)))) return false;
3646
+ if (!(0, _orkestrel_contract.isString)(item) && !(0, _orkestrel_contract.isFiniteNumber)(item) && !(0, _orkestrel_contract.isBoolean)(item) && !((0, _orkestrel_contract.isArray)(item) && item.every((entry) => (0, _orkestrel_contract.isString)(entry)))) return false;
3575
3647
  if (!Object.hasOwn(properties, name)) continue;
3576
3648
  const field = properties[name];
3577
3649
  if (!isJSONObject(field)) return false;
@@ -3584,7 +3656,7 @@ function isElicitContent(value, schema) {
3584
3656
  const minimum = field["minimum"];
3585
3657
  const maximum = field["maximum"];
3586
3658
  if (!(0, _orkestrel_contract.isFiniteNumber)(item)) return false;
3587
- if (declaredType === "integer" && !Number.isInteger(item)) return false;
3659
+ if (declaredType === "integer" && !(0, _orkestrel_contract.isInteger)(item)) return false;
3588
3660
  if ((0, _orkestrel_contract.isFiniteNumber)(minimum) && item < minimum) return false;
3589
3661
  if ((0, _orkestrel_contract.isFiniteNumber)(maximum) && item > maximum) return false;
3590
3662
  continue;
@@ -3596,8 +3668,8 @@ function isElicitContent(value, schema) {
3596
3668
  const maximum = field["maxLength"];
3597
3669
  const format = field["format"];
3598
3670
  if (!(0, _orkestrel_contract.isString)(item)) return false;
3599
- if (Array.isArray(choices) && !choices.includes(item)) return false;
3600
- if (Array.isArray(titled) && !titled.some((choice) => isJSONObject(choice) && choice["const"] === item)) return false;
3671
+ if ((0, _orkestrel_contract.isArray)(choices) && !choices.includes(item)) return false;
3672
+ if ((0, _orkestrel_contract.isArray)(titled) && !titled.some((choice) => isJSONObject(choice) && choice["const"] === item)) return false;
3601
3673
  const length = Array.from(item).length;
3602
3674
  if ((0, _orkestrel_contract.isInteger)(minimum) && length < minimum) return false;
3603
3675
  if ((0, _orkestrel_contract.isInteger)(maximum) && length > maximum) return false;
@@ -3610,14 +3682,14 @@ function isElicitContent(value, schema) {
3610
3682
  const items = field["items"];
3611
3683
  const minimum = field["minItems"];
3612
3684
  const maximum = field["maxItems"];
3613
- if (!Array.isArray(item) || !item.every((entry) => (0, _orkestrel_contract.isString)(entry))) return false;
3685
+ if (!(0, _orkestrel_contract.isArray)(item) || !item.every((entry) => (0, _orkestrel_contract.isString)(entry))) return false;
3614
3686
  if ((0, _orkestrel_contract.isInteger)(minimum) && item.length < minimum) return false;
3615
3687
  if ((0, _orkestrel_contract.isInteger)(maximum) && item.length > maximum) return false;
3616
3688
  if (!isJSONObject(items)) return false;
3617
3689
  const choices = items["enum"];
3618
3690
  const titled = items["anyOf"];
3619
- if (Array.isArray(choices) && !item.every((entry) => choices.includes(entry))) return false;
3620
- if (Array.isArray(titled) && !item.every((entry) => titled.some((choice) => isJSONObject(choice) && choice["const"] === entry))) return false;
3691
+ if ((0, _orkestrel_contract.isArray)(choices) && !item.every((entry) => choices.includes(entry))) return false;
3692
+ if ((0, _orkestrel_contract.isArray)(titled) && !item.every((entry) => titled.some((choice) => isJSONObject(choice) && choice["const"] === entry))) return false;
3621
3693
  }
3622
3694
  return true;
3623
3695
  } catch {
@@ -3678,7 +3750,7 @@ function isMCPRootResult(value) {
3678
3750
  const result = owned.value;
3679
3751
  const roots = result["roots"];
3680
3752
  const metadata = result["_meta"];
3681
- if (!Array.isArray(roots) || !roots.every((root) => isMCPRoot(root))) return false;
3753
+ if (!(0, _orkestrel_contract.isArray)(roots) || !roots.every((root) => isMCPRoot(root))) return false;
3682
3754
  return (0, _orkestrel_contract.isUndefined)(metadata) || isMCPMetaObject(metadata);
3683
3755
  } catch {
3684
3756
  return false;
@@ -3716,7 +3788,7 @@ function isMCPSampleContent(value) {
3716
3788
  if (block["type"] === "tool_result") {
3717
3789
  const carried = block["content"];
3718
3790
  const failed = block["isError"];
3719
- if (!Array.isArray(carried) || !carried.every((entry) => isMCPContent(entry))) return false;
3791
+ if (!(0, _orkestrel_contract.isArray)(carried) || !carried.every((entry) => isMCPContent(entry))) return false;
3720
3792
  if (!(0, _orkestrel_contract.isUndefined)(failed) && !(0, _orkestrel_contract.isBoolean)(failed)) return false;
3721
3793
  return (0, _orkestrel_contract.isString)(block["toolUseId"]);
3722
3794
  }
@@ -3765,7 +3837,7 @@ function isMCPSampleResult(value) {
3765
3837
  const metadata = result["_meta"];
3766
3838
  if (role !== "user" && role !== "assistant") return false;
3767
3839
  if (!(0, _orkestrel_contract.isString)(result["model"])) return false;
3768
- if (!(Array.isArray(content) ? content : [content]).every((block) => isMCPSampleContent(block))) return false;
3840
+ if (!((0, _orkestrel_contract.isArray)(content) ? content : [content]).every((block) => isMCPSampleContent(block))) return false;
3769
3841
  if (!(0, _orkestrel_contract.isUndefined)(reason) && !(0, _orkestrel_contract.isString)(reason)) return false;
3770
3842
  return (0, _orkestrel_contract.isUndefined)(metadata) || isMCPMetaObject(metadata);
3771
3843
  } catch {
@@ -5015,6 +5087,7 @@ var MCPServer = class {
5015
5087
  #limits;
5016
5088
  #subscriptions = 0;
5017
5089
  constructor(options) {
5090
+ if (options.subscription !== void 0 && !isMCPConsumerFilter(options.subscription.notifications)) throw new MCPError("The consumer filter must be a valid filter that omits tools changes, because the server produces that family from its registry", JSONRPC_INVALID_PARAMS);
5018
5091
  this.#emitter = new _orkestrel_emitter.Emitter({
5019
5092
  ...options.on !== void 0 ? { on: options.on } : {},
5020
5093
  ...options.error !== void 0 ? { error: options.error } : {}
@@ -5177,7 +5250,7 @@ var MCPServer = class {
5177
5250
  const input = captured[0];
5178
5251
  return this.#forward(input, request);
5179
5252
  }
5180
- if (!Array.isArray(captured[0]) || !captured[0].every((entry) => isMCPResourceContents(entry))) return buildJSONRPCError(request.id, JSONRPC_INTERNAL_ERROR, "Server resource manager returned invalid or oversized contents");
5253
+ if (!(0, _orkestrel_contract.isArray)(captured[0]) || !captured[0].every((entry) => isMCPResourceContents(entry))) return buildJSONRPCError(request.id, JSONRPC_INTERNAL_ERROR, "Server resource manager returned invalid or oversized contents");
5181
5254
  const result = buildModernResult({ contents: captured[0] }, this.#options.identity, this.#options.cache?.ttl ?? 6e4, this.#options.cache?.scope);
5182
5255
  return buildJSONRPCResult(request.id, result);
5183
5256
  }
@@ -5260,7 +5333,7 @@ var MCPServer = class {
5260
5333
  const generated = await manager.complete(params, options);
5261
5334
  if (generated === void 0) return buildJSONRPCError(request.id, JSONRPC_INVALID_PARAMS, "Completion reference not found");
5262
5335
  const candidate = generated;
5263
- if (!(0, _orkestrel_contract.isRecord)(candidate) || !Array.isArray(candidate["values"])) return buildJSONRPCError(request.id, JSONRPC_INTERNAL_ERROR, "Server completion manager returned invalid or oversized candidates");
5336
+ if (!(0, _orkestrel_contract.isRecord)(candidate) || !(0, _orkestrel_contract.isArray)(candidate["values"])) return buildJSONRPCError(request.id, JSONRPC_INTERNAL_ERROR, "Server completion manager returned invalid or oversized candidates");
5264
5337
  const values = candidate["values"];
5265
5338
  const truncated = values.length > 100;
5266
5339
  const captured = snapshotJSON({
@@ -5287,14 +5360,14 @@ var MCPServer = class {
5287
5360
  const args = (0, _orkestrel_contract.isRecord)(rawArguments) ? rawArguments : EMPTY_MCP_ARGUMENTS;
5288
5361
  const input = await this.#input(request, args, options);
5289
5362
  if (input !== void 0) return input;
5290
- const call = buildToolCall(request, options.caller, args);
5363
+ const call = buildToolCall(request, args);
5291
5364
  if (call === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: a string `name` is required");
5292
5365
  const deferred = await this.#defer(request, call, options);
5293
5366
  if (deferred !== void 0) return deferred;
5294
5367
  const token = params?.["_meta"];
5295
5368
  const progressToken = (0, _orkestrel_contract.isRecord)(token) ? token["progressToken"] : void 0;
5296
- if (this.#options.execution !== void 0 && ((0, _orkestrel_contract.isString)(progressToken) || typeof progressToken === "number" && Number.isInteger(progressToken))) return this.#progress(request, call, progressToken, options);
5297
- const result = await this.#execute(request, call, options.signal);
5369
+ if (this.#options.execution !== void 0 && ((0, _orkestrel_contract.isString)(progressToken) || (0, _orkestrel_contract.isInteger)(progressToken))) return this.#progress(request, call, progressToken, options);
5370
+ const result = await this.#execute(request, call, options);
5298
5371
  return "jsonrpc" in result ? result : buildJSONRPCResult(id, result);
5299
5372
  }
5300
5373
  async #defer(request, call, options) {
@@ -5338,7 +5411,7 @@ var MCPServer = class {
5338
5411
  keys: this.#limits.keys,
5339
5412
  depth: this.#limits.depth
5340
5413
  }, signal);
5341
- const execution = this.#execute(request, call, signal, reporter);
5414
+ const execution = this.#execute(request, call, options, reporter);
5342
5415
  try {
5343
5416
  while (true) {
5344
5417
  const outcome = await Promise.race([reporter.take(), execution]);
@@ -5356,14 +5429,19 @@ var MCPServer = class {
5356
5429
  reporter.stop();
5357
5430
  }
5358
5431
  }
5359
- async #execute(request, call, signal, progress) {
5432
+ async #execute(request, call, options, progress) {
5433
+ const { signal, caller } = options;
5360
5434
  let result;
5361
5435
  try {
5362
- result = this.#options.execution === void 0 ? await this.#options.tools.execute(call) : await this.#options.execution({
5436
+ result = this.#options.execution === void 0 ? await this.#options.tools.execute(call, {
5437
+ signal,
5438
+ ...caller === void 0 ? {} : { caller }
5439
+ }) : await this.#options.execution({
5363
5440
  request,
5364
5441
  call,
5365
5442
  tools: this.#options.tools,
5366
5443
  signal,
5444
+ ...caller === void 0 ? {} : { caller },
5367
5445
  ...progress === void 0 ? {} : { progress }
5368
5446
  });
5369
5447
  } catch (error) {
@@ -5501,7 +5579,7 @@ var MCPServer = class {
5501
5579
  bytes: this.#limits.message,
5502
5580
  depth: this.#limits.depth
5503
5581
  });
5504
- if (configured === void 0 || context === void 0 || !(0, _orkestrel_contract.isString)(principal) || principal.length === 0 || !Number.isFinite(configured.ttl) || configured.ttl <= 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: input policy returned an invalid round or continuation context");
5582
+ if (configured === void 0 || context === void 0 || !(0, _orkestrel_contract.isString)(principal) || principal.length === 0 || !(0, _orkestrel_contract.isFiniteNumber)(configured.ttl) || configured.ttl <= 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: input policy returned an invalid round or continuation context");
5505
5583
  if (previous !== void 0 && previous <= Date.now()) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
5506
5584
  const expiry = Date.now() + configured.ttl;
5507
5585
  const protectedState = {
@@ -5551,7 +5629,10 @@ var MCPServer = class {
5551
5629
  const task = this.#options.task;
5552
5630
  const configured = this.#options.subscription;
5553
5631
  const tasks = task !== void 0 && configured !== void 0;
5554
- let notifications = buildSubscriptionFilter(requested, configured?.notifications ?? {}, tasks);
5632
+ let notifications = buildSubscriptionFilter(requested, {
5633
+ ...configured?.notifications,
5634
+ toolsListChanged: true
5635
+ }, tasks);
5555
5636
  const requestedTaskIds = notifications.taskIds;
5556
5637
  if (requestedTaskIds !== void 0) {
5557
5638
  const resolved = [];
@@ -5564,19 +5645,22 @@ var MCPServer = class {
5564
5645
  taskIds: resolved
5565
5646
  } : rest;
5566
5647
  }
5648
+ const state = {
5649
+ frame: void 0,
5650
+ failure: void 0,
5651
+ iterator: void 0
5652
+ };
5653
+ const source = new ReadableStream({
5654
+ start: this.#startSubscription.bind(this, notifications, options, configured, state),
5655
+ pull: this.#pullSubscription.bind(this, notifications, options, state)
5656
+ });
5567
5657
  yield buildSubscriptionAcknowledgement(notifications, id);
5568
- if (configured !== void 0) {
5569
- const iterator = (await configured.producer(notifications, options))[Symbol.asyncIterator]();
5570
- options.signal.addEventListener("abort", () => void iterator.return?.(void 0)?.catch(() => void 0), { once: true });
5571
- for (let next = await iterator.next(); next.done !== true; next = await iterator.next()) {
5572
- const owned = parseJSONRPCMessage(next.value, {
5573
- bytes: this.#limits.message,
5574
- depth: this.#limits.depth
5575
- });
5576
- if (owned === void 0 || !isJSONRPCNotification(owned)) continue;
5577
- if (matchesSubscriptionNotification(owned, notifications)) yield stampSubscriptionNotification(owned, id);
5578
- }
5658
+ for await (const notification of source) {
5659
+ if (notification === void 0) continue;
5660
+ if (notification === state.frame) state.frame = void 0;
5661
+ yield stampSubscriptionNotification(notification, id);
5579
5662
  }
5663
+ if (state.failure !== void 0) throw state.failure.error;
5580
5664
  return buildSubscriptionResult(id, this.#options.identity);
5581
5665
  } catch (error) {
5582
5666
  if (options.signal.aborted) throw error;
@@ -5585,6 +5669,72 @@ var MCPServer = class {
5585
5669
  slot.abort();
5586
5670
  }
5587
5671
  }
5672
+ async #startSubscription(notifications, options, configured, state, controller) {
5673
+ const emitter = this.#options.tools.emitter;
5674
+ const changed = this.#change.bind(this, controller, options.signal, state);
5675
+ if (options.signal.aborted) {
5676
+ controller.error(options.signal.reason);
5677
+ return;
5678
+ }
5679
+ if (notifications.toolsListChanged === true && !emitter.destroyed) {
5680
+ emitter.on("add", changed);
5681
+ emitter.on("remove", changed);
5682
+ emitter.on("clear", changed);
5683
+ }
5684
+ options.signal.addEventListener("abort", () => {
5685
+ emitter.off("add", changed);
5686
+ emitter.off("remove", changed);
5687
+ emitter.off("clear", changed);
5688
+ const iterator = state.iterator;
5689
+ state.iterator = void 0;
5690
+ if (iterator !== void 0) this.#releaseProducer(iterator, options.signal.reason);
5691
+ controller.error(options.signal.reason);
5692
+ }, { once: true });
5693
+ try {
5694
+ if (configured === void 0) return;
5695
+ const iterator = (await configured.producer(notifications, options))[Symbol.asyncIterator]();
5696
+ if (options.signal.aborted) this.#releaseProducer(iterator, options.signal.reason);
5697
+ else state.iterator = iterator;
5698
+ } catch (error) {
5699
+ this.#failSubscription(state, options.signal, controller, error);
5700
+ }
5701
+ }
5702
+ #change(controller, signal, state) {
5703
+ if (signal.aborted || state.failure !== void 0 || state.frame !== void 0) return;
5704
+ const notification = {
5705
+ jsonrpc: "2.0",
5706
+ method: "notifications/tools/list_changed"
5707
+ };
5708
+ state.frame = notification;
5709
+ controller.enqueue(notification);
5710
+ }
5711
+ async #pullSubscription(notifications, options, state, controller) {
5712
+ try {
5713
+ if (options.signal.aborted) return;
5714
+ const next = await state.iterator?.next();
5715
+ if (options.signal.aborted) return;
5716
+ if (next === void 0 || next.done === true) {
5717
+ state.iterator = void 0;
5718
+ if (notifications.toolsListChanged !== true) controller.close();
5719
+ return;
5720
+ }
5721
+ const owned = parseJSONRPCMessage(next.value, {
5722
+ bytes: this.#limits.message,
5723
+ depth: this.#limits.depth
5724
+ });
5725
+ controller.enqueue(owned !== void 0 && isJSONRPCNotification(owned) && owned.method !== "notifications/tools/list_changed" && matchesSubscriptionNotification(owned, notifications) ? owned : void 0);
5726
+ } catch (error) {
5727
+ this.#failSubscription(state, options.signal, controller, error);
5728
+ }
5729
+ }
5730
+ #failSubscription(state, signal, controller, error) {
5731
+ if (signal.aborted) return;
5732
+ state.failure = { error };
5733
+ controller.close();
5734
+ }
5735
+ #releaseProducer(iterator, reason) {
5736
+ iterator.return?.(reason)?.catch(() => void 0);
5737
+ }
5588
5738
  #readTaskId(request) {
5589
5739
  const id = request.id;
5590
5740
  const context = parseRequestContext(request, {
@@ -5952,9 +6102,11 @@ var MCPClient = class {
5952
6102
  async *#openSubscription(notifications, signal, capacity) {
5953
6103
  signal.throwIfAborted();
5954
6104
  if (!(0, _orkestrel_contract.isInteger)(capacity) || capacity < 1) throw new MCPError("MCP subscription capacity must be a positive integer", JSONRPC_INVALID_PARAMS);
6105
+ const method = "subscriptions/listen";
6106
+ const opening = this.#refuse(method);
6107
+ if (opening !== void 0) throw opening;
5955
6108
  this.#nextId += 1;
5956
6109
  const id = this.#nextId;
5957
- const method = "subscriptions/listen";
5958
6110
  const modern = this.#version;
5959
6111
  const request = {
5960
6112
  jsonrpc: "2.0",
@@ -6002,7 +6154,16 @@ var MCPClient = class {
6002
6154
  this.#cancelSubscription(id, /* @__PURE__ */ new Error("MCP subscription closed by its consumer"));
6003
6155
  }
6004
6156
  }
6157
+ #refuse(method) {
6158
+ if (method === "server/discover") return void 0;
6159
+ if (this.#connected) return void 0;
6160
+ const inflight = this.#connecting;
6161
+ if (inflight !== void 0 && inflight.generation === this.#generation) return void 0;
6162
+ return new MCPError(`MCP client is not connected, so '${method}' was not issued`, JSONRPC_INVALID_REQUEST);
6163
+ }
6005
6164
  #request(method, params, deadline, version, options) {
6165
+ const refusal = this.#refuse(method);
6166
+ if (refusal !== void 0) return Promise.reject(refusal);
6006
6167
  this.#nextId += 1;
6007
6168
  const id = this.#nextId;
6008
6169
  const timeout = deadline;
@@ -6056,7 +6217,7 @@ var MCPClient = class {
6056
6217
  ...report === void 0 ? {} : { progress: report }
6057
6218
  });
6058
6219
  this.#transport.send(request).catch((error) => {
6059
- this.#settle(id, error instanceof Error ? error : new Error(String(error)), true);
6220
+ this.#settle(id, (0, _orkestrel_contract.isError)(error) ? error : new Error(String(error)), true);
6060
6221
  });
6061
6222
  });
6062
6223
  }
@@ -6144,16 +6305,20 @@ var MCPClient = class {
6144
6305
  #tool(name, descriptor) {
6145
6306
  const inputSchema = descriptor["inputSchema"];
6146
6307
  const description = descriptor["description"];
6147
- const options = {
6308
+ const title = descriptor["title"];
6309
+ const annotations = descriptor["annotations"];
6310
+ const projected = isMCPToolAnnotations(annotations) ? mcpAnnotationsToTool(annotations) : {};
6311
+ return new _orkestrel_tool.Tool({
6148
6312
  name,
6313
+ ...(0, _orkestrel_contract.isString)(title) ? { title } : {},
6314
+ ...(0, _orkestrel_contract.isString)(description) ? { description } : {},
6315
+ ...(0, _orkestrel_contract.isRecord)(inputSchema) ? { parameters: inputSchema } : {},
6316
+ ...Object.keys(projected).length === 0 ? {} : { annotations: projected },
6149
6317
  execute: this.#execute.bind(this, name)
6150
- };
6151
- if ((0, _orkestrel_contract.isString)(description)) options.description = description;
6152
- if ((0, _orkestrel_contract.isRecord)(inputSchema)) options.parameters = inputSchema;
6153
- return new _orkestrel_tool.Tool(options);
6318
+ });
6154
6319
  }
6155
- async #execute(name, args) {
6156
- const outcome = await this.call(name, args);
6320
+ async #execute(name, args, context) {
6321
+ const outcome = await this.call(name, args, { signal: context.signal });
6157
6322
  if (outcome.resultType !== "complete") throw new Error(`MCP tool '${name}' answered '${outcome.resultType}' and has no inline value`);
6158
6323
  return outcome.value;
6159
6324
  }
@@ -6809,6 +6974,7 @@ exports.isMCPCompletion = isMCPCompletion;
6809
6974
  exports.isMCPCompletionParams = isMCPCompletionParams;
6810
6975
  exports.isMCPCompletionReference = isMCPCompletionReference;
6811
6976
  exports.isMCPCompletionResult = isMCPCompletionResult;
6977
+ exports.isMCPConsumerFilter = isMCPConsumerFilter;
6812
6978
  exports.isMCPContent = isMCPContent;
6813
6979
  exports.isMCPElicitFieldSchema = isMCPElicitFieldSchema;
6814
6980
  exports.isMCPElicitForm = isMCPElicitForm;
@@ -6859,6 +7025,7 @@ exports.isMCPTaskNotification = isMCPTaskNotification;
6859
7025
  exports.isMCPTaskResult = isMCPTaskResult;
6860
7026
  exports.isMCPTaskStatus = isMCPTaskStatus;
6861
7027
  exports.isMCPTextResource = isMCPTextResource;
7028
+ exports.isMCPToolAnnotations = isMCPToolAnnotations;
6862
7029
  exports.isMCPVersion = isMCPVersion;
6863
7030
  exports.isModernRequest = isModernRequest;
6864
7031
  exports.isRFC3339Date = isRFC3339Date;
@@ -6868,6 +7035,7 @@ exports.legacyInvocationToModern = legacyInvocationToModern;
6868
7035
  exports.legacyResultToModern = legacyResultToModern;
6869
7036
  exports.matchesResultType = matchesResultType;
6870
7037
  exports.matchesSubscriptionNotification = matchesSubscriptionNotification;
7038
+ exports.mcpAnnotationsToTool = mcpAnnotationsToTool;
6871
7039
  exports.modernInvocationToLegacy = modernInvocationToLegacy;
6872
7040
  exports.modernResultToLegacy = modernResultToLegacy;
6873
7041
  exports.parseJSONRPCMessage = parseJSONRPCMessage;
@@ -6883,5 +7051,6 @@ exports.snapshotToolResult = snapshotToolResult;
6883
7051
  exports.stampSubscriptionNotification = stampSubscriptionNotification;
6884
7052
  exports.supportsFormElicitation = supportsFormElicitation;
6885
7053
  exports.supportsTask = supportsTask;
7054
+ exports.toolAnnotationsToMCP = toolAnnotationsToMCP;
6886
7055
 
6887
7056
  //# sourceMappingURL=index.cjs.map