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