@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.
- package/dist/src/browser/index.d.ts +786 -0
- package/dist/src/browser/index.js +636 -4
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +271 -102
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +144 -32
- package/dist/src/core/index.d.ts +144 -32
- package/dist/src/core/index.js +269 -104
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1 -1
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +2 -2
- package/dist/src/server/index.js.map +1 -1
- package/package.json +15 -15
package/dist/src/core/index.cjs
CHANGED
|
@@ -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
|
|
279
|
-
*
|
|
280
|
-
*
|
|
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
|
-
|
|
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.
|
|
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 (!
|
|
745
|
-
if (!
|
|
746
|
-
if (breadth !== 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 (!
|
|
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 (
|
|
877
|
-
if (
|
|
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) || !
|
|
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 (!
|
|
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)
|
|
1118
|
-
|
|
1119
|
-
|
|
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,
|
|
1132
|
+
function buildToolCall(request, args) {
|
|
1136
1133
|
const name = request.params?.["name"];
|
|
1137
|
-
if (
|
|
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
|
|
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
|
|
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" &&
|
|
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
|
|
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
|
-
* `
|
|
1456
|
-
* `capabilities.
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
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 (
|
|
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) && (!
|
|
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) ||
|
|
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) ||
|
|
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) ||
|
|
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) ||
|
|
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
|
|
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
|
|
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) ||
|
|
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
|
|
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"])) &&
|
|
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
|
|
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) ||
|
|
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
|
|
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) || !
|
|
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) && (!
|
|
3324
|
-
if (!(0, _orkestrel_contract.isUndefined)(names) && ((0, _orkestrel_contract.isUndefined)(choices) || !
|
|
3325
|
-
return !(!(0, _orkestrel_contract.isUndefined)(titled) && (!
|
|
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) && (!
|
|
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) && (!
|
|
3337
|
-
if (!(0, _orkestrel_contract.isUndefined)(titled) && (!
|
|
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) ||
|
|
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) ||
|
|
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 (
|
|
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) && !(
|
|
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" && !
|
|
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 (
|
|
3600
|
-
if (
|
|
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 (!
|
|
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 (
|
|
3620
|
-
if (
|
|
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 (!
|
|
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 (!
|
|
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 (!(
|
|
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 (!
|
|
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) || !
|
|
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,
|
|
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) ||
|
|
5297
|
-
const result = await this.#execute(request, call, options
|
|
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,
|
|
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,
|
|
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
|
|
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 || !
|
|
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,
|
|
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
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
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
|
|
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
|
|
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
|