toolception 0.5.3 → 0.5.4
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/index.js +298 -293
- package/dist/index.js.map +1 -1
- package/dist/meta/registerMetaTools.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
var
|
|
1
|
+
var te = Object.defineProperty;
|
|
2
2
|
var L = (r) => {
|
|
3
3
|
throw TypeError(r);
|
|
4
4
|
};
|
|
5
|
-
var
|
|
6
|
-
var d = (r, e,
|
|
7
|
-
var b = (r, e,
|
|
8
|
-
var m = (r, e,
|
|
5
|
+
var se = (r, e, t) => e in r ? te(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
6
|
+
var d = (r, e, t) => se(r, typeof e != "symbol" ? e + "" : e, t), oe = (r, e, t) => e.has(r) || L("Cannot " + t);
|
|
7
|
+
var b = (r, e, t) => e.has(r) ? L("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, t);
|
|
8
|
+
var m = (r, e, t) => (oe(r, e, "access private method"), t);
|
|
9
9
|
import { z as y } from "zod";
|
|
10
10
|
import j from "fastify";
|
|
11
11
|
import k from "@fastify/cors";
|
|
12
12
|
import { randomUUID as T } from "node:crypto";
|
|
13
13
|
import { StreamableHTTPServerTransport as D } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
14
|
-
import { isInitializeRequest as
|
|
14
|
+
import { isInitializeRequest as O } from "@modelcontextprotocol/sdk/types.js";
|
|
15
15
|
const R = {
|
|
16
16
|
dynamic: [
|
|
17
17
|
"dynamic-tool-discovery",
|
|
@@ -28,13 +28,13 @@ class re {
|
|
|
28
28
|
toolsets: e.keys?.toolsets ?? R.toolsets
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
resolveMode(e,
|
|
32
|
-
return this.isDynamicEnabled(
|
|
31
|
+
resolveMode(e, t) {
|
|
32
|
+
return this.isDynamicEnabled(t) ? "DYNAMIC" : this.getToolsetsString(t) ? "STATIC" : this.isDynamicEnabled(e) ? "DYNAMIC" : this.getToolsetsString(e) ? "STATIC" : null;
|
|
33
33
|
}
|
|
34
|
-
parseCommaSeparatedToolSets(e,
|
|
34
|
+
parseCommaSeparatedToolSets(e, t) {
|
|
35
35
|
if (!e || typeof e != "string") return [];
|
|
36
|
-
const
|
|
37
|
-
for (const n of
|
|
36
|
+
const s = e.split(",").map((n) => n.trim()).filter((n) => n.length > 0), o = new Set(Object.keys(t)), i = [];
|
|
37
|
+
for (const n of s)
|
|
38
38
|
o.has(n) ? i.push(n) : console.warn(
|
|
39
39
|
`Invalid toolset '${n}' ignored. Available: ${Array.from(
|
|
40
40
|
o
|
|
@@ -42,32 +42,32 @@ class re {
|
|
|
42
42
|
);
|
|
43
43
|
return i;
|
|
44
44
|
}
|
|
45
|
-
getModulesForToolSets(e,
|
|
46
|
-
const
|
|
45
|
+
getModulesForToolSets(e, t) {
|
|
46
|
+
const s = /* @__PURE__ */ new Set();
|
|
47
47
|
for (const o of e) {
|
|
48
|
-
const i =
|
|
49
|
-
i && (i.modules || []).forEach((n) =>
|
|
48
|
+
const i = t[o];
|
|
49
|
+
i && (i.modules || []).forEach((n) => s.add(n));
|
|
50
50
|
}
|
|
51
|
-
return Array.from(
|
|
51
|
+
return Array.from(s);
|
|
52
52
|
}
|
|
53
|
-
validateToolsetName(e,
|
|
53
|
+
validateToolsetName(e, t) {
|
|
54
54
|
if (!e || typeof e != "string")
|
|
55
55
|
return {
|
|
56
56
|
isValid: !1,
|
|
57
57
|
error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${Object.keys(
|
|
58
|
-
|
|
58
|
+
t
|
|
59
59
|
).join(", ")}`
|
|
60
60
|
};
|
|
61
|
-
const
|
|
62
|
-
return
|
|
61
|
+
const s = e.trim();
|
|
62
|
+
return s.length === 0 ? {
|
|
63
63
|
isValid: !1,
|
|
64
64
|
error: `Empty toolset name provided. Available toolsets: ${Object.keys(
|
|
65
|
-
|
|
65
|
+
t
|
|
66
66
|
).join(", ")}`
|
|
67
|
-
} : s
|
|
67
|
+
} : t[s] ? { isValid: !0, sanitized: s } : {
|
|
68
68
|
isValid: !1,
|
|
69
|
-
error: `Toolset '${
|
|
70
|
-
|
|
69
|
+
error: `Toolset '${s}' not found. Available toolsets: ${Object.keys(
|
|
70
|
+
t
|
|
71
71
|
).join(", ")}`
|
|
72
72
|
};
|
|
73
73
|
}
|
|
@@ -78,37 +78,37 @@ class re {
|
|
|
78
78
|
* @param catalog - The toolset catalog to validate against
|
|
79
79
|
* @returns Validation result with modules array if valid
|
|
80
80
|
*/
|
|
81
|
-
validateToolsetModules(e,
|
|
81
|
+
validateToolsetModules(e, t) {
|
|
82
82
|
try {
|
|
83
83
|
for (const o of e)
|
|
84
|
-
if (!
|
|
84
|
+
if (!t[o])
|
|
85
85
|
return {
|
|
86
86
|
isValid: !1,
|
|
87
87
|
error: `Toolset '${o}' not found in catalog`
|
|
88
88
|
};
|
|
89
|
-
return { isValid: !0, modules: this.getModulesForToolSets(e,
|
|
90
|
-
} catch (
|
|
89
|
+
return { isValid: !0, modules: this.getModulesForToolSets(e, t) };
|
|
90
|
+
} catch (s) {
|
|
91
91
|
return {
|
|
92
92
|
isValid: !1,
|
|
93
|
-
error: `Error resolving modules for ${e.join(", ")}: ${
|
|
93
|
+
error: `Error resolving modules for ${e.join(", ")}: ${s instanceof Error ? s.message : "Unknown error"}`
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
isDynamicEnabled(e) {
|
|
98
98
|
if (!e) return !1;
|
|
99
|
-
for (const
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
99
|
+
for (const t of this.keys.dynamic) {
|
|
100
|
+
const s = e[t];
|
|
101
|
+
if (s === !0 || typeof s == "string" && s.trim().toLowerCase() === "true")
|
|
102
102
|
return !0;
|
|
103
103
|
}
|
|
104
104
|
return !1;
|
|
105
105
|
}
|
|
106
106
|
getToolsetsString(e) {
|
|
107
107
|
if (e)
|
|
108
|
-
for (const
|
|
109
|
-
const
|
|
110
|
-
if (typeof
|
|
111
|
-
return
|
|
108
|
+
for (const t of this.keys.toolsets) {
|
|
109
|
+
const s = e[t];
|
|
110
|
+
if (typeof s == "string" && s.trim().length > 0)
|
|
111
|
+
return s;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -132,30 +132,30 @@ class ie {
|
|
|
132
132
|
", "
|
|
133
133
|
)}`
|
|
134
134
|
};
|
|
135
|
-
const
|
|
136
|
-
return
|
|
135
|
+
const t = e.trim();
|
|
136
|
+
return t.length === 0 ? {
|
|
137
137
|
isValid: !1,
|
|
138
138
|
error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(
|
|
139
139
|
", "
|
|
140
140
|
)}`
|
|
141
|
-
} : this.catalog[
|
|
141
|
+
} : this.catalog[t] ? { isValid: !0, sanitized: t } : {
|
|
142
142
|
isValid: !1,
|
|
143
|
-
error: `Toolset '${
|
|
143
|
+
error: `Toolset '${t}' not found. Available toolsets: ${this.getAvailableToolsets().join(
|
|
144
144
|
", "
|
|
145
145
|
)}`
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
-
async resolveToolsForToolsets(e,
|
|
149
|
-
const
|
|
148
|
+
async resolveToolsForToolsets(e, t) {
|
|
149
|
+
const s = [];
|
|
150
150
|
for (const o of e) {
|
|
151
151
|
const i = this.catalog[o];
|
|
152
|
-
if (i && (Array.isArray(i.tools) && i.tools.length > 0 &&
|
|
152
|
+
if (i && (Array.isArray(i.tools) && i.tools.length > 0 && s.push(...i.tools), Array.isArray(i.modules) && i.modules.length > 0))
|
|
153
153
|
for (const n of i.modules) {
|
|
154
154
|
const l = this.moduleLoaders[n];
|
|
155
155
|
if (l)
|
|
156
156
|
try {
|
|
157
|
-
const a = await l(
|
|
158
|
-
Array.isArray(a) && a.length > 0 &&
|
|
157
|
+
const a = await l(t);
|
|
158
|
+
Array.isArray(a) && a.length > 0 && s.push(...a);
|
|
159
159
|
} catch (a) {
|
|
160
160
|
console.warn(
|
|
161
161
|
`Module loader '${n}' failed for toolset '${o}':`,
|
|
@@ -164,18 +164,18 @@ class ie {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
return
|
|
167
|
+
return s;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
class N extends Error {
|
|
171
|
-
constructor(
|
|
172
|
-
super(
|
|
171
|
+
constructor(t, s, o, i) {
|
|
172
|
+
super(t);
|
|
173
173
|
d(this, "code");
|
|
174
174
|
d(this, "details");
|
|
175
|
-
this.name = "ToolingError", this.code =
|
|
175
|
+
this.name = "ToolingError", this.code = s, this.details = o;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
class
|
|
178
|
+
class z {
|
|
179
179
|
constructor(e = {}) {
|
|
180
180
|
d(this, "options");
|
|
181
181
|
d(this, "names", /* @__PURE__ */ new Set());
|
|
@@ -184,8 +184,8 @@ class O {
|
|
|
184
184
|
namespaceWithToolset: e.namespaceWithToolset ?? !0
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
|
-
getSafeName(e,
|
|
188
|
-
return !this.options.namespaceWithToolset ||
|
|
187
|
+
getSafeName(e, t) {
|
|
188
|
+
return !this.options.namespaceWithToolset || t.startsWith(`${e}.`) ? t : `${e}.${t}`;
|
|
189
189
|
}
|
|
190
190
|
has(e) {
|
|
191
191
|
return this.names.has(e);
|
|
@@ -198,20 +198,20 @@ class O {
|
|
|
198
198
|
);
|
|
199
199
|
this.names.add(e);
|
|
200
200
|
}
|
|
201
|
-
addForToolset(e,
|
|
202
|
-
this.add(
|
|
203
|
-
const
|
|
204
|
-
|
|
201
|
+
addForToolset(e, t) {
|
|
202
|
+
this.add(t);
|
|
203
|
+
const s = this.toolsetToNames.get(e) ?? /* @__PURE__ */ new Set();
|
|
204
|
+
s.add(t), this.toolsetToNames.set(e, s);
|
|
205
205
|
}
|
|
206
|
-
mapAndValidate(e,
|
|
207
|
-
return
|
|
208
|
-
const o = this.getSafeName(e,
|
|
206
|
+
mapAndValidate(e, t) {
|
|
207
|
+
return t.map((s) => {
|
|
208
|
+
const o = this.getSafeName(e, s.name);
|
|
209
209
|
if (this.has(o))
|
|
210
210
|
throw new N(
|
|
211
211
|
`Tool name collision for '${o}'`,
|
|
212
212
|
"E_TOOL_NAME_CONFLICT"
|
|
213
213
|
);
|
|
214
|
-
return { ...
|
|
214
|
+
return { ...s, name: o };
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
217
|
list() {
|
|
@@ -219,8 +219,8 @@ class O {
|
|
|
219
219
|
}
|
|
220
220
|
listByToolset() {
|
|
221
221
|
const e = {};
|
|
222
|
-
for (const [
|
|
223
|
-
e[
|
|
222
|
+
for (const [t, s] of this.toolsetToNames.entries())
|
|
223
|
+
e[t] = Array.from(s);
|
|
224
224
|
return e;
|
|
225
225
|
}
|
|
226
226
|
}
|
|
@@ -233,7 +233,7 @@ class ne {
|
|
|
233
233
|
d(this, "exposurePolicy");
|
|
234
234
|
d(this, "toolRegistry");
|
|
235
235
|
d(this, "activeToolsets", /* @__PURE__ */ new Set());
|
|
236
|
-
this.server = e.server, this.resolver = e.resolver, this.context = e.context, this.onToolsListChanged = e.onToolsListChanged, this.exposurePolicy = e.exposurePolicy, this.toolRegistry = e.toolRegistry ?? new
|
|
236
|
+
this.server = e.server, this.resolver = e.resolver, this.context = e.context, this.onToolsListChanged = e.onToolsListChanged, this.exposurePolicy = e.exposurePolicy, this.toolRegistry = e.toolRegistry ?? new z({ namespaceWithToolset: !0 });
|
|
237
237
|
}
|
|
238
238
|
/**
|
|
239
239
|
* Sends a tool list change notification if configured.
|
|
@@ -268,14 +268,14 @@ class ne {
|
|
|
268
268
|
* @param skipNotification - If true, skips the tool list change notification (for batch operations)
|
|
269
269
|
* @returns Result object with success status and message
|
|
270
270
|
*/
|
|
271
|
-
async enableToolset(e,
|
|
272
|
-
const
|
|
273
|
-
if (!
|
|
271
|
+
async enableToolset(e, t = !1) {
|
|
272
|
+
const s = this.resolver.validateToolsetName(e);
|
|
273
|
+
if (!s.isValid || !s.sanitized)
|
|
274
274
|
return {
|
|
275
275
|
success: !1,
|
|
276
|
-
message:
|
|
276
|
+
message: s.error || "Unknown validation error"
|
|
277
277
|
};
|
|
278
|
-
const o =
|
|
278
|
+
const o = s.sanitized;
|
|
279
279
|
if (this.activeToolsets.has(o))
|
|
280
280
|
return {
|
|
281
281
|
success: !1,
|
|
@@ -298,7 +298,7 @@ class ne {
|
|
|
298
298
|
for (const c of a)
|
|
299
299
|
this.registerSingleTool(c, o), n.push(c.name);
|
|
300
300
|
}
|
|
301
|
-
return this.activeToolsets.add(o),
|
|
301
|
+
return this.activeToolsets.add(o), t || await this.notifyToolsChanged(), {
|
|
302
302
|
success: !0,
|
|
303
303
|
message: `Toolset '${o}' enabled successfully. Registered ${l?.length ?? 0} tools.`
|
|
304
304
|
};
|
|
@@ -338,13 +338,13 @@ class ne {
|
|
|
338
338
|
* @param toolsetKey - The toolset key for tracking
|
|
339
339
|
* @private
|
|
340
340
|
*/
|
|
341
|
-
registerSingleTool(e,
|
|
341
|
+
registerSingleTool(e, t) {
|
|
342
342
|
this.server.tool(
|
|
343
343
|
e.name,
|
|
344
344
|
e.description,
|
|
345
345
|
e.inputSchema,
|
|
346
|
-
async (
|
|
347
|
-
), this.toolRegistry.addForToolset(
|
|
346
|
+
async (s) => await e.handler(s)
|
|
347
|
+
), this.toolRegistry.addForToolset(t, e.name);
|
|
348
348
|
}
|
|
349
349
|
/**
|
|
350
350
|
* Disables a toolset by name.
|
|
@@ -353,21 +353,21 @@ class ne {
|
|
|
353
353
|
* @returns Result object with success status and message
|
|
354
354
|
*/
|
|
355
355
|
async disableToolset(e) {
|
|
356
|
-
const
|
|
357
|
-
if (!
|
|
356
|
+
const t = this.resolver.validateToolsetName(e);
|
|
357
|
+
if (!t.isValid || !t.sanitized) {
|
|
358
358
|
const o = Array.from(this.activeToolsets).join(", ") || "none";
|
|
359
359
|
return {
|
|
360
360
|
success: !1,
|
|
361
|
-
message: `${
|
|
361
|
+
message: `${t.error || "Unknown validation error"} Active toolsets: ${o}`
|
|
362
362
|
};
|
|
363
363
|
}
|
|
364
|
-
const
|
|
365
|
-
return this.activeToolsets.has(
|
|
364
|
+
const s = t.sanitized;
|
|
365
|
+
return this.activeToolsets.has(s) ? (this.activeToolsets.delete(s), await this.notifyToolsChanged(), {
|
|
366
366
|
success: !0,
|
|
367
|
-
message: `Toolset '${
|
|
367
|
+
message: `Toolset '${s}' disabled successfully. Individual tools remain registered due to MCP limitations.`
|
|
368
368
|
}) : {
|
|
369
369
|
success: !1,
|
|
370
|
-
message: `Toolset '${
|
|
370
|
+
message: `Toolset '${s}' is not currently active. Active toolsets: ${Array.from(this.activeToolsets).join(", ") || "none"}`
|
|
371
371
|
};
|
|
372
372
|
}
|
|
373
373
|
getStatus() {
|
|
@@ -388,21 +388,21 @@ class ne {
|
|
|
388
388
|
* @returns Result object with overall success status and individual results
|
|
389
389
|
*/
|
|
390
390
|
async enableToolsets(e) {
|
|
391
|
-
const
|
|
391
|
+
const t = [];
|
|
392
392
|
for (const n of e)
|
|
393
393
|
try {
|
|
394
394
|
const l = await this.enableToolset(n, !0);
|
|
395
|
-
|
|
395
|
+
t.push({ name: n, ...l });
|
|
396
396
|
} catch (l) {
|
|
397
|
-
|
|
397
|
+
t.push({
|
|
398
398
|
name: n,
|
|
399
399
|
success: !1,
|
|
400
400
|
message: l instanceof Error ? l.message : "Unknown error",
|
|
401
401
|
code: "E_INTERNAL"
|
|
402
402
|
});
|
|
403
403
|
}
|
|
404
|
-
const
|
|
405
|
-
return o && await this.notifyToolsChanged(), { success:
|
|
404
|
+
const s = t.every((n) => n.success), o = t.some((n) => n.success), i = s ? "All toolsets enabled" : o ? "Some toolsets failed to enable" : "All toolsets failed to enable";
|
|
405
|
+
return o && await this.notifyToolsChanged(), { success: s, results: t, message: i };
|
|
406
406
|
}
|
|
407
407
|
/**
|
|
408
408
|
* Enables all available toolsets in a batch operation.
|
|
@@ -413,11 +413,12 @@ class ne {
|
|
|
413
413
|
return this.enableToolsets(e);
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
|
-
function ae(r, e,
|
|
417
|
-
(
|
|
416
|
+
function ae(r, e, t) {
|
|
417
|
+
(t?.mode ?? "DYNAMIC") === "DYNAMIC" && (r.tool(
|
|
418
418
|
"enable_toolset",
|
|
419
419
|
"Enable a toolset by name",
|
|
420
420
|
{ name: y.string().describe("Toolset name") },
|
|
421
|
+
{ destructiveHint: !0, idempotentHint: !0 },
|
|
421
422
|
async (o) => {
|
|
422
423
|
const i = await e.enableToolset(o.name);
|
|
423
424
|
return {
|
|
@@ -428,6 +429,7 @@ function ae(r, e, s) {
|
|
|
428
429
|
"disable_toolset",
|
|
429
430
|
"Disable a toolset by name (state only)",
|
|
430
431
|
{ name: y.string().describe("Toolset name") },
|
|
432
|
+
{ destructiveHint: !0, idempotentHint: !0 },
|
|
431
433
|
async (o) => {
|
|
432
434
|
const i = await e.disableToolset(o.name);
|
|
433
435
|
return {
|
|
@@ -438,6 +440,7 @@ function ae(r, e, s) {
|
|
|
438
440
|
"list_toolsets",
|
|
439
441
|
"List available toolsets with active status and definitions",
|
|
440
442
|
{},
|
|
443
|
+
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
441
444
|
async () => {
|
|
442
445
|
const o = e.getAvailableToolsets(), i = e.getStatus().toolsetToTools, n = o.map((l) => {
|
|
443
446
|
const a = e.getToolsetDefinition(l);
|
|
@@ -463,6 +466,7 @@ function ae(r, e, s) {
|
|
|
463
466
|
"describe_toolset",
|
|
464
467
|
"Describe a toolset with definition, active status and tools",
|
|
465
468
|
{ name: y.string().describe("Toolset name") },
|
|
469
|
+
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
466
470
|
async (o) => {
|
|
467
471
|
const i = e.getToolsetDefinition(o.name), n = e.getStatus().toolsetToTools;
|
|
468
472
|
if (!i)
|
|
@@ -493,6 +497,7 @@ function ae(r, e, s) {
|
|
|
493
497
|
"list_tools",
|
|
494
498
|
"List currently registered tool names (best effort)",
|
|
495
499
|
{},
|
|
500
|
+
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
496
501
|
async () => {
|
|
497
502
|
const o = e.getStatus(), i = {
|
|
498
503
|
tools: o.tools,
|
|
@@ -513,12 +518,12 @@ class A {
|
|
|
513
518
|
d(this, "initPromise");
|
|
514
519
|
d(this, "initError", null);
|
|
515
520
|
this.toolsetValidator = new re();
|
|
516
|
-
const
|
|
517
|
-
this.mode =
|
|
521
|
+
const t = e.startup ?? {}, s = this.resolveStartupConfig(t, e.catalog);
|
|
522
|
+
this.mode = s.mode, this.resolver = new ie({
|
|
518
523
|
catalog: e.catalog,
|
|
519
524
|
moduleLoaders: e.moduleLoaders
|
|
520
525
|
});
|
|
521
|
-
const o = new
|
|
526
|
+
const o = new z({
|
|
522
527
|
namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
|
|
523
528
|
});
|
|
524
529
|
this.manager = new ne({
|
|
@@ -529,7 +534,7 @@ class A {
|
|
|
529
534
|
exposurePolicy: e.exposurePolicy,
|
|
530
535
|
toolRegistry: o
|
|
531
536
|
}), e.registerMetaTools !== !1 && ae(e.server, this.manager, { mode: this.mode });
|
|
532
|
-
const i =
|
|
537
|
+
const i = s.toolsets;
|
|
533
538
|
this.initPromise = this.initializeToolsets(i);
|
|
534
539
|
}
|
|
535
540
|
/**
|
|
@@ -542,8 +547,8 @@ class A {
|
|
|
542
547
|
async initializeToolsets(e) {
|
|
543
548
|
try {
|
|
544
549
|
e === "ALL" ? await this.manager.enableToolsets(this.resolver.getAvailableToolsets()) : Array.isArray(e) && e.length > 0 && await this.manager.enableToolsets(e);
|
|
545
|
-
} catch (
|
|
546
|
-
this.initError =
|
|
550
|
+
} catch (t) {
|
|
551
|
+
this.initError = t instanceof Error ? t : new Error(String(t)), console.error("Failed to initialize toolsets:", this.initError);
|
|
547
552
|
}
|
|
548
553
|
}
|
|
549
554
|
/**
|
|
@@ -563,19 +568,19 @@ class A {
|
|
|
563
568
|
async isReady() {
|
|
564
569
|
return await this.initPromise, this.initError === null;
|
|
565
570
|
}
|
|
566
|
-
resolveStartupConfig(e,
|
|
571
|
+
resolveStartupConfig(e, t) {
|
|
567
572
|
if (e.mode) {
|
|
568
573
|
if (e.mode === "DYNAMIC" && e.toolsets)
|
|
569
574
|
return console.warn("startup.toolsets provided but ignored in DYNAMIC mode"), { mode: "DYNAMIC" };
|
|
570
575
|
if (e.mode === "STATIC") {
|
|
571
576
|
if (e.toolsets === "ALL")
|
|
572
577
|
return { mode: "STATIC", toolsets: "ALL" };
|
|
573
|
-
const
|
|
574
|
-
for (const i of
|
|
575
|
-
const { isValid: n, sanitized: l, error: a } = this.toolsetValidator.validateToolsetName(i,
|
|
578
|
+
const s = Array.isArray(e.toolsets) ? e.toolsets : [], o = [];
|
|
579
|
+
for (const i of s) {
|
|
580
|
+
const { isValid: n, sanitized: l, error: a } = this.toolsetValidator.validateToolsetName(i, t);
|
|
576
581
|
n && l ? o.push(l) : a && console.warn(a);
|
|
577
582
|
}
|
|
578
|
-
if (
|
|
583
|
+
if (s.length > 0 && o.length === 0)
|
|
579
584
|
throw new Error(
|
|
580
585
|
"STATIC mode requires valid toolsets or 'ALL'; none were valid"
|
|
581
586
|
);
|
|
@@ -585,16 +590,16 @@ class A {
|
|
|
585
590
|
}
|
|
586
591
|
if (e.toolsets === "ALL") return { mode: "STATIC", toolsets: "ALL" };
|
|
587
592
|
if (Array.isArray(e.toolsets) && e.toolsets.length > 0) {
|
|
588
|
-
const
|
|
593
|
+
const s = [];
|
|
589
594
|
for (const o of e.toolsets) {
|
|
590
|
-
const { isValid: i, sanitized: n, error: l } = this.toolsetValidator.validateToolsetName(o,
|
|
591
|
-
i && n ?
|
|
595
|
+
const { isValid: i, sanitized: n, error: l } = this.toolsetValidator.validateToolsetName(o, t);
|
|
596
|
+
i && n ? s.push(n) : l && console.warn(l);
|
|
592
597
|
}
|
|
593
|
-
if (
|
|
598
|
+
if (s.length === 0)
|
|
594
599
|
throw new Error(
|
|
595
600
|
"STATIC mode requires valid toolsets or 'ALL'; none were valid"
|
|
596
601
|
);
|
|
597
|
-
return { mode: "STATIC", toolsets:
|
|
602
|
+
return { mode: "STATIC", toolsets: s };
|
|
598
603
|
}
|
|
599
604
|
return { mode: "DYNAMIC" };
|
|
600
605
|
}
|
|
@@ -616,8 +621,8 @@ class V {
|
|
|
616
621
|
// Use ReturnType<typeof setInterval> for cross-env typings without NodeJS namespace
|
|
617
622
|
d(this, "pruneInterval");
|
|
618
623
|
this.maxSize = e.maxSize ?? 1e3, this.ttlMs = e.ttlMs ?? 1e3 * 60 * 60, this.onEvict = e.onEvict;
|
|
619
|
-
const
|
|
620
|
-
this.pruneInterval = setInterval(() => this.pruneExpired(),
|
|
624
|
+
const t = e.pruneIntervalMs ?? 1e3 * 60 * 10;
|
|
625
|
+
this.pruneInterval = setInterval(() => this.pruneExpired(), t);
|
|
621
626
|
}
|
|
622
627
|
getEntryCount() {
|
|
623
628
|
return this.storage.size;
|
|
@@ -629,13 +634,13 @@ class V {
|
|
|
629
634
|
return this.ttlMs;
|
|
630
635
|
}
|
|
631
636
|
get(e) {
|
|
632
|
-
const
|
|
633
|
-
return
|
|
637
|
+
const t = this.storage.get(e);
|
|
638
|
+
return t ? Date.now() - t.lastAccessed > this.ttlMs ? (this.delete(e), null) : (t.lastAccessed = Date.now(), this.storage.delete(e), this.storage.set(e, t), t.resource) : null;
|
|
634
639
|
}
|
|
635
|
-
set(e,
|
|
640
|
+
set(e, t) {
|
|
636
641
|
this.storage.size >= this.maxSize && this.evictLeastRecentlyUsed();
|
|
637
|
-
const
|
|
638
|
-
this.storage.set(e,
|
|
642
|
+
const s = { resource: t, lastAccessed: Date.now() };
|
|
643
|
+
this.storage.set(e, s);
|
|
639
644
|
}
|
|
640
645
|
/**
|
|
641
646
|
* Removes an entry from the cache.
|
|
@@ -643,8 +648,8 @@ class V {
|
|
|
643
648
|
* @param key - The key to remove
|
|
644
649
|
*/
|
|
645
650
|
delete(e) {
|
|
646
|
-
const
|
|
647
|
-
|
|
651
|
+
const t = this.storage.get(e);
|
|
652
|
+
t && (this.storage.delete(e), m(this, w, E).call(this, e, t.resource));
|
|
648
653
|
}
|
|
649
654
|
/**
|
|
650
655
|
* Stops the background pruning interval and optionally clears all entries.
|
|
@@ -660,8 +665,8 @@ class V {
|
|
|
660
665
|
clear() {
|
|
661
666
|
const e = Array.from(this.storage.entries());
|
|
662
667
|
this.storage.clear();
|
|
663
|
-
for (const [
|
|
664
|
-
m(this, w, E).call(this,
|
|
668
|
+
for (const [t, s] of e)
|
|
669
|
+
m(this, w, E).call(this, t, s.resource);
|
|
665
670
|
}
|
|
666
671
|
/**
|
|
667
672
|
* Evicts the least recently used entry from the cache.
|
|
@@ -676,11 +681,11 @@ class V {
|
|
|
676
681
|
* @private
|
|
677
682
|
*/
|
|
678
683
|
pruneExpired() {
|
|
679
|
-
const e = Date.now(),
|
|
680
|
-
for (const [
|
|
681
|
-
e - o.lastAccessed > this.ttlMs &&
|
|
682
|
-
for (const
|
|
683
|
-
this.delete(
|
|
684
|
+
const e = Date.now(), t = [];
|
|
685
|
+
for (const [s, o] of this.storage.entries())
|
|
686
|
+
e - o.lastAccessed > this.ttlMs && t.push(s);
|
|
687
|
+
for (const s of t)
|
|
688
|
+
this.delete(s);
|
|
684
689
|
}
|
|
685
690
|
}
|
|
686
691
|
w = new WeakSet(), /**
|
|
@@ -689,20 +694,20 @@ w = new WeakSet(), /**
|
|
|
689
694
|
* @param resource - The resource being evicted
|
|
690
695
|
* @private
|
|
691
696
|
*/
|
|
692
|
-
E = function(e,
|
|
697
|
+
E = function(e, t) {
|
|
693
698
|
if (this.onEvict)
|
|
694
699
|
try {
|
|
695
|
-
const
|
|
696
|
-
|
|
700
|
+
const s = this.onEvict(e, t);
|
|
701
|
+
s instanceof Promise && s.catch((o) => {
|
|
697
702
|
console.warn(`Error in cache eviction callback for key '${e}':`, o);
|
|
698
703
|
});
|
|
699
|
-
} catch (
|
|
700
|
-
console.warn(`Error in cache eviction callback for key '${e}':`,
|
|
704
|
+
} catch (s) {
|
|
705
|
+
console.warn(`Error in cache eviction callback for key '${e}':`, s);
|
|
701
706
|
}
|
|
702
707
|
};
|
|
703
|
-
function F(r, e,
|
|
708
|
+
function F(r, e, t, s) {
|
|
704
709
|
const o = ["/mcp", "/healthz", "/tools", "/.well-known/mcp-config"];
|
|
705
|
-
for (const i of
|
|
710
|
+
for (const i of t) {
|
|
706
711
|
const n = `${e}${i.path}`;
|
|
707
712
|
if (o.some(
|
|
708
713
|
(c) => n.startsWith(`${e}${c}`)
|
|
@@ -713,28 +718,28 @@ function F(r, e, s, t) {
|
|
|
713
718
|
continue;
|
|
714
719
|
}
|
|
715
720
|
const a = i.method.toLowerCase();
|
|
716
|
-
r[a](n, async (c,
|
|
721
|
+
r[a](n, async (c, u) => {
|
|
717
722
|
try {
|
|
718
723
|
const f = c.headers["mcp-client-id"]?.trim(), v = f && f.length > 0 ? f : `anon-${T()}`;
|
|
719
724
|
let x;
|
|
720
725
|
if (i.bodySchema) {
|
|
721
726
|
const g = i.bodySchema.safeParse(c.body);
|
|
722
727
|
if (!g.success)
|
|
723
|
-
return S(
|
|
728
|
+
return S(u, "body", g.error);
|
|
724
729
|
x = g.data;
|
|
725
730
|
}
|
|
726
731
|
let I = {};
|
|
727
732
|
if (i.querySchema) {
|
|
728
733
|
const g = i.querySchema.safeParse(c.query);
|
|
729
734
|
if (!g.success)
|
|
730
|
-
return S(
|
|
735
|
+
return S(u, "query", g.error);
|
|
731
736
|
I = g.data;
|
|
732
737
|
}
|
|
733
738
|
let M = {};
|
|
734
739
|
if (i.paramsSchema) {
|
|
735
740
|
const g = i.paramsSchema.safeParse(c.params);
|
|
736
741
|
if (!g.success)
|
|
737
|
-
return S(
|
|
742
|
+
return S(u, "params", g.error);
|
|
738
743
|
M = g.data;
|
|
739
744
|
}
|
|
740
745
|
const P = {
|
|
@@ -744,8 +749,8 @@ function F(r, e, s, t) {
|
|
|
744
749
|
headers: c.headers,
|
|
745
750
|
clientId: v
|
|
746
751
|
};
|
|
747
|
-
if (
|
|
748
|
-
const g = await
|
|
752
|
+
if (s?.contextExtractor) {
|
|
753
|
+
const g = await s.contextExtractor(c);
|
|
749
754
|
Object.assign(P, g);
|
|
750
755
|
}
|
|
751
756
|
const $ = await i.handler(P);
|
|
@@ -754,7 +759,7 @@ function F(r, e, s, t) {
|
|
|
754
759
|
return g.success ? g.data : (console.error(
|
|
755
760
|
`Response validation failed for ${i.method} ${i.path}:`,
|
|
756
761
|
g.error
|
|
757
|
-
),
|
|
762
|
+
), u.code(500), {
|
|
758
763
|
error: {
|
|
759
764
|
code: "RESPONSE_VALIDATION_ERROR",
|
|
760
765
|
message: "Internal server error: invalid response format"
|
|
@@ -766,7 +771,7 @@ function F(r, e, s, t) {
|
|
|
766
771
|
return console.error(
|
|
767
772
|
`Error in custom endpoint ${i.method} ${i.path}:`,
|
|
768
773
|
f
|
|
769
|
-
),
|
|
774
|
+
), u.code(500), {
|
|
770
775
|
error: {
|
|
771
776
|
code: "INTERNAL_ERROR",
|
|
772
777
|
message: f instanceof Error ? f.message : "Internal server error"
|
|
@@ -776,17 +781,17 @@ function F(r, e, s, t) {
|
|
|
776
781
|
});
|
|
777
782
|
}
|
|
778
783
|
}
|
|
779
|
-
function S(r, e,
|
|
784
|
+
function S(r, e, t) {
|
|
780
785
|
return r.code(400), {
|
|
781
786
|
error: {
|
|
782
787
|
code: "VALIDATION_ERROR",
|
|
783
788
|
message: `Validation failed for ${e}`,
|
|
784
|
-
details:
|
|
789
|
+
details: t.errors
|
|
785
790
|
}
|
|
786
791
|
};
|
|
787
792
|
}
|
|
788
793
|
class le {
|
|
789
|
-
constructor(e,
|
|
794
|
+
constructor(e, t, s = {}, o) {
|
|
790
795
|
d(this, "options");
|
|
791
796
|
d(this, "defaultManager");
|
|
792
797
|
d(this, "createBundle");
|
|
@@ -794,26 +799,26 @@ class le {
|
|
|
794
799
|
d(this, "configSchema");
|
|
795
800
|
// Per-client server bundles and per-client session transports
|
|
796
801
|
d(this, "clientCache", new V({
|
|
797
|
-
onEvict: (e,
|
|
798
|
-
this.cleanupBundle(
|
|
802
|
+
onEvict: (e, t) => {
|
|
803
|
+
this.cleanupBundle(t);
|
|
799
804
|
}
|
|
800
805
|
}));
|
|
801
|
-
this.defaultManager = e, this.createBundle =
|
|
802
|
-
host:
|
|
803
|
-
port:
|
|
804
|
-
basePath:
|
|
805
|
-
cors:
|
|
806
|
-
logger:
|
|
807
|
-
app:
|
|
808
|
-
customEndpoints:
|
|
806
|
+
this.defaultManager = e, this.createBundle = t, this.options = {
|
|
807
|
+
host: s.host ?? "0.0.0.0",
|
|
808
|
+
port: s.port ?? 3e3,
|
|
809
|
+
basePath: s.basePath ?? "/",
|
|
810
|
+
cors: s.cors ?? !0,
|
|
811
|
+
logger: s.logger ?? !1,
|
|
812
|
+
app: s.app,
|
|
813
|
+
customEndpoints: s.customEndpoints
|
|
809
814
|
}, this.configSchema = o;
|
|
810
815
|
}
|
|
811
816
|
async start() {
|
|
812
817
|
if (this.app) return;
|
|
813
818
|
const e = this.options.app ?? j({ logger: this.options.logger });
|
|
814
819
|
this.options.cors && await e.register(k, { origin: !0 });
|
|
815
|
-
const
|
|
816
|
-
e.get(`${
|
|
820
|
+
const t = this.options.basePath.endsWith("/") ? this.options.basePath.slice(0, -1) : this.options.basePath;
|
|
821
|
+
e.get(`${t}/healthz`, async () => ({ ok: !0 })), e.get(`${t}/tools`, async () => this.defaultManager.getStatus()), e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
817
822
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
818
823
|
title: "MCP Session Configuration",
|
|
819
824
|
description: "Schema for the /mcp endpoint configuration",
|
|
@@ -823,9 +828,9 @@ class le {
|
|
|
823
828
|
"x-mcp-version": "1.0",
|
|
824
829
|
"x-query-style": "dot+bracket"
|
|
825
830
|
})), e.post(
|
|
826
|
-
`${
|
|
827
|
-
async (
|
|
828
|
-
const i =
|
|
831
|
+
`${t}/mcp`,
|
|
832
|
+
async (s, o) => {
|
|
833
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : `anon-${T()}`, l = !n.startsWith("anon-");
|
|
829
834
|
let a = l ? this.clientCache.get(n) : null;
|
|
830
835
|
if (!a) {
|
|
831
836
|
const f = this.createBundle(), v = f.sessions;
|
|
@@ -835,20 +840,20 @@ class le {
|
|
|
835
840
|
sessions: v instanceof Map ? v : /* @__PURE__ */ new Map()
|
|
836
841
|
}, l && this.clientCache.set(n, a);
|
|
837
842
|
}
|
|
838
|
-
const c =
|
|
839
|
-
let
|
|
843
|
+
const c = s.headers["mcp-session-id"];
|
|
844
|
+
let u;
|
|
840
845
|
if (c && a.sessions.get(c))
|
|
841
|
-
|
|
842
|
-
else if (!c &&
|
|
846
|
+
u = a.sessions.get(c);
|
|
847
|
+
else if (!c && O(s.body)) {
|
|
843
848
|
const f = T();
|
|
844
|
-
|
|
849
|
+
u = new D({
|
|
845
850
|
sessionIdGenerator: () => f,
|
|
846
851
|
onsessioninitialized: (v) => {
|
|
847
|
-
a.sessions.set(v,
|
|
852
|
+
a.sessions.set(v, u);
|
|
848
853
|
}
|
|
849
854
|
});
|
|
850
855
|
try {
|
|
851
|
-
await a.server.connect(
|
|
856
|
+
await a.server.connect(u);
|
|
852
857
|
} catch {
|
|
853
858
|
return o.code(500), {
|
|
854
859
|
jsonrpc: "2.0",
|
|
@@ -856,8 +861,8 @@ class le {
|
|
|
856
861
|
id: null
|
|
857
862
|
};
|
|
858
863
|
}
|
|
859
|
-
|
|
860
|
-
|
|
864
|
+
u.onclose = () => {
|
|
865
|
+
u?.sessionId && a.sessions.delete(u.sessionId);
|
|
861
866
|
};
|
|
862
867
|
} else
|
|
863
868
|
return o.code(400), {
|
|
@@ -865,28 +870,28 @@ class le {
|
|
|
865
870
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
866
871
|
id: null
|
|
867
872
|
};
|
|
868
|
-
return await
|
|
869
|
-
|
|
873
|
+
return await u.handleRequest(
|
|
874
|
+
s.raw,
|
|
870
875
|
o.raw,
|
|
871
|
-
|
|
876
|
+
s.body
|
|
872
877
|
), o;
|
|
873
878
|
}
|
|
874
|
-
), e.get(`${
|
|
875
|
-
const i =
|
|
879
|
+
), e.get(`${t}/mcp`, async (s, o) => {
|
|
880
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
|
|
876
881
|
if (!n)
|
|
877
882
|
return o.code(400), "Missing mcp-client-id";
|
|
878
883
|
const l = this.clientCache.get(n);
|
|
879
884
|
if (!l)
|
|
880
885
|
return o.code(400), "Invalid or expired client";
|
|
881
|
-
const a =
|
|
886
|
+
const a = s.headers["mcp-session-id"];
|
|
882
887
|
if (!a)
|
|
883
888
|
return o.code(400), "Missing mcp-session-id";
|
|
884
889
|
const c = l.sessions.get(a);
|
|
885
|
-
return c ? (await c.handleRequest(
|
|
890
|
+
return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
|
|
886
891
|
}), e.delete(
|
|
887
|
-
`${
|
|
888
|
-
async (
|
|
889
|
-
const i =
|
|
892
|
+
`${t}/mcp`,
|
|
893
|
+
async (s, o) => {
|
|
894
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "", l = s.headers["mcp-session-id"];
|
|
890
895
|
if (!n || !l)
|
|
891
896
|
return o.code(400), {
|
|
892
897
|
jsonrpc: "2.0",
|
|
@@ -914,7 +919,7 @@ class le {
|
|
|
914
919
|
}
|
|
915
920
|
return o.code(204).send(), o;
|
|
916
921
|
}
|
|
917
|
-
), this.options.customEndpoints && this.options.customEndpoints.length > 0 && F(e,
|
|
922
|
+
), this.options.customEndpoints && this.options.customEndpoints.length > 0 && F(e, t, this.options.customEndpoints), this.options.app || await e.listen({ host: this.options.host, port: this.options.port }), this.app = e;
|
|
918
923
|
}
|
|
919
924
|
/**
|
|
920
925
|
* Stops the Fastify server and cleans up all resources.
|
|
@@ -930,13 +935,13 @@ class le {
|
|
|
930
935
|
* @private
|
|
931
936
|
*/
|
|
932
937
|
cleanupBundle(e) {
|
|
933
|
-
for (const [
|
|
938
|
+
for (const [t, s] of e.sessions.entries())
|
|
934
939
|
try {
|
|
935
|
-
typeof
|
|
936
|
-
console.warn(`Error closing session ${
|
|
940
|
+
typeof s.close == "function" && s.close().catch((o) => {
|
|
941
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
937
942
|
});
|
|
938
943
|
} catch (o) {
|
|
939
|
-
console.warn(`Error closing session ${
|
|
944
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
940
945
|
}
|
|
941
946
|
e.sessions.clear();
|
|
942
947
|
}
|
|
@@ -964,9 +969,9 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
964
969
|
const e = r.startup?.mode ?? "DYNAMIC";
|
|
965
970
|
if (typeof r.createServer != "function")
|
|
966
971
|
throw new Error("createMcpServer: `createServer` (factory) is required");
|
|
967
|
-
const
|
|
972
|
+
const t = r.createServer(), s = (a) => typeof a?.server?.notification == "function", o = (a) => typeof a?.notifyToolsListChanged == "function", i = async (a) => {
|
|
968
973
|
try {
|
|
969
|
-
if (
|
|
974
|
+
if (s(a)) {
|
|
970
975
|
await a.server.notification({
|
|
971
976
|
method: "notifications/tools/list_changed"
|
|
972
977
|
});
|
|
@@ -979,12 +984,12 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
979
984
|
console.warn("Failed to send tools list changed notification:", c);
|
|
980
985
|
}
|
|
981
986
|
}, n = new A({
|
|
982
|
-
server:
|
|
987
|
+
server: t,
|
|
983
988
|
catalog: r.catalog,
|
|
984
989
|
moduleLoaders: r.moduleLoaders,
|
|
985
990
|
exposurePolicy: r.exposurePolicy,
|
|
986
991
|
context: r.context,
|
|
987
|
-
notifyToolsListChanged: async () => i(
|
|
992
|
+
notifyToolsListChanged: async () => i(t),
|
|
988
993
|
startup: r.startup,
|
|
989
994
|
registerMetaTools: r.registerMetaTools !== void 0 ? r.registerMetaTools : e === "DYNAMIC"
|
|
990
995
|
});
|
|
@@ -993,7 +998,7 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
993
998
|
n.getManager(),
|
|
994
999
|
() => {
|
|
995
1000
|
if (e === "STATIC")
|
|
996
|
-
return { server:
|
|
1001
|
+
return { server: t, orchestrator: n };
|
|
997
1002
|
const a = r.createServer(), c = new A({
|
|
998
1003
|
server: a,
|
|
999
1004
|
catalog: r.catalog,
|
|
@@ -1010,7 +1015,7 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
1010
1015
|
r.configSchema
|
|
1011
1016
|
);
|
|
1012
1017
|
return {
|
|
1013
|
-
server:
|
|
1018
|
+
server: t,
|
|
1014
1019
|
start: async () => {
|
|
1015
1020
|
await l.start();
|
|
1016
1021
|
},
|
|
@@ -1020,15 +1025,15 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
1020
1025
|
};
|
|
1021
1026
|
}
|
|
1022
1027
|
function de(r) {
|
|
1023
|
-
|
|
1028
|
+
ue(r), he(r), fe(r), me(r);
|
|
1024
1029
|
}
|
|
1025
|
-
function
|
|
1030
|
+
function ue(r) {
|
|
1026
1031
|
if (!r || typeof r != "object")
|
|
1027
1032
|
throw new Error(
|
|
1028
1033
|
"Permission configuration is required for createPermissionBasedMcpServer"
|
|
1029
1034
|
);
|
|
1030
1035
|
}
|
|
1031
|
-
function
|
|
1036
|
+
function he(r) {
|
|
1032
1037
|
if (!r.source)
|
|
1033
1038
|
throw new Error('Permission source must be either "headers" or "config"');
|
|
1034
1039
|
if (r.source !== "headers" && r.source !== "config")
|
|
@@ -1060,13 +1065,13 @@ function me(r) {
|
|
|
1060
1065
|
throw new Error("headerName must be a non-empty string");
|
|
1061
1066
|
}
|
|
1062
1067
|
function ge(r) {
|
|
1063
|
-
for (const [e,
|
|
1064
|
-
if (!Array.isArray(
|
|
1068
|
+
for (const [e, t] of Object.entries(r))
|
|
1069
|
+
if (!Array.isArray(t))
|
|
1065
1070
|
throw new Error(
|
|
1066
1071
|
`staticMap value for client "${e}" must be an array of toolset names`
|
|
1067
1072
|
);
|
|
1068
1073
|
}
|
|
1069
|
-
var p, _,
|
|
1074
|
+
var p, _, H, B, Y, W;
|
|
1070
1075
|
class pe {
|
|
1071
1076
|
/**
|
|
1072
1077
|
* Creates a new PermissionResolver instance.
|
|
@@ -1091,23 +1096,23 @@ class pe {
|
|
|
1091
1096
|
* @param headers - Optional request headers (required for header-based permissions)
|
|
1092
1097
|
* @returns Array of toolset names the client is allowed to access
|
|
1093
1098
|
*/
|
|
1094
|
-
resolvePermissions(e,
|
|
1099
|
+
resolvePermissions(e, t) {
|
|
1095
1100
|
if (this.cache.has(e))
|
|
1096
1101
|
return this.cache.get(e);
|
|
1097
|
-
let
|
|
1102
|
+
let s;
|
|
1098
1103
|
try {
|
|
1099
|
-
this.config.source === "headers" ?
|
|
1104
|
+
this.config.source === "headers" ? s = m(this, p, _).call(this, t) : s = m(this, p, B).call(this, e), Array.isArray(s) || (console.warn(
|
|
1100
1105
|
`Permission resolution returned non-array for client ${e}, using empty permissions`
|
|
1101
|
-
),
|
|
1106
|
+
), s = []), s = s.filter(
|
|
1102
1107
|
(o) => typeof o == "string" && o.trim().length > 0
|
|
1103
1108
|
);
|
|
1104
1109
|
} catch (o) {
|
|
1105
1110
|
console.error(
|
|
1106
1111
|
`Unexpected error resolving permissions for client ${e}:`,
|
|
1107
1112
|
o
|
|
1108
|
-
),
|
|
1113
|
+
), s = [];
|
|
1109
1114
|
}
|
|
1110
|
-
return this.cache.set(e,
|
|
1115
|
+
return this.cache.set(e, s), s;
|
|
1111
1116
|
}
|
|
1112
1117
|
/**
|
|
1113
1118
|
* Invalidates cached permissions for a specific client.
|
|
@@ -1137,15 +1142,15 @@ p = new WeakSet(), /**
|
|
|
1137
1142
|
_ = function(e) {
|
|
1138
1143
|
if (!e)
|
|
1139
1144
|
return [];
|
|
1140
|
-
const
|
|
1141
|
-
if (!
|
|
1145
|
+
const t = m(this, p, H).call(this, e, this.normalizedHeaderName);
|
|
1146
|
+
if (!t)
|
|
1142
1147
|
return [];
|
|
1143
1148
|
try {
|
|
1144
|
-
return
|
|
1145
|
-
} catch (
|
|
1149
|
+
return t.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1150
|
+
} catch (s) {
|
|
1146
1151
|
return console.warn(
|
|
1147
1152
|
`Failed to parse permission header '${this.normalizedHeaderName}':`,
|
|
1148
|
-
|
|
1153
|
+
s
|
|
1149
1154
|
), [];
|
|
1150
1155
|
}
|
|
1151
1156
|
}, /**
|
|
@@ -1156,11 +1161,11 @@ _ = function(e) {
|
|
|
1156
1161
|
* @returns The header value if found, undefined otherwise
|
|
1157
1162
|
* @private
|
|
1158
1163
|
*/
|
|
1159
|
-
|
|
1160
|
-
if (e[
|
|
1161
|
-
return e[
|
|
1162
|
-
for (const [
|
|
1163
|
-
if (
|
|
1164
|
+
H = function(e, t) {
|
|
1165
|
+
if (e[t] !== void 0)
|
|
1166
|
+
return e[t];
|
|
1167
|
+
for (const [s, o] of Object.entries(e))
|
|
1168
|
+
if (s.toLowerCase() === t)
|
|
1164
1169
|
return o;
|
|
1165
1170
|
}, /**
|
|
1166
1171
|
* Resolves permissions from server-side configuration.
|
|
@@ -1170,16 +1175,16 @@ B = function(e, s) {
|
|
|
1170
1175
|
* @returns Array of toolset names from configuration
|
|
1171
1176
|
* @private
|
|
1172
1177
|
*/
|
|
1173
|
-
|
|
1178
|
+
B = function(e) {
|
|
1174
1179
|
if (this.config.resolver) {
|
|
1175
|
-
const
|
|
1176
|
-
if (
|
|
1177
|
-
return
|
|
1180
|
+
const t = m(this, p, Y).call(this, e);
|
|
1181
|
+
if (t !== null)
|
|
1182
|
+
return t;
|
|
1178
1183
|
}
|
|
1179
1184
|
if (this.config.staticMap) {
|
|
1180
|
-
const
|
|
1181
|
-
if (
|
|
1182
|
-
return
|
|
1185
|
+
const t = m(this, p, W).call(this, e);
|
|
1186
|
+
if (t !== null)
|
|
1187
|
+
return t;
|
|
1183
1188
|
}
|
|
1184
1189
|
return this.config.defaultPermissions || [];
|
|
1185
1190
|
}, /**
|
|
@@ -1191,14 +1196,14 @@ H = function(e) {
|
|
|
1191
1196
|
*/
|
|
1192
1197
|
Y = function(e) {
|
|
1193
1198
|
try {
|
|
1194
|
-
const
|
|
1195
|
-
return Array.isArray(
|
|
1199
|
+
const t = this.config.resolver(e);
|
|
1200
|
+
return Array.isArray(t) ? t : (console.warn(
|
|
1196
1201
|
`Permission resolver returned non-array for client ${e}, using fallback`
|
|
1197
1202
|
), null);
|
|
1198
|
-
} catch (
|
|
1199
|
-
const
|
|
1203
|
+
} catch (t) {
|
|
1204
|
+
const s = t instanceof Error ? t.message : String(t);
|
|
1200
1205
|
return console.warn(
|
|
1201
|
-
`Permission resolver declined client ${e} (${
|
|
1206
|
+
`Permission resolver declined client ${e} (${s}), trying fallback`
|
|
1202
1207
|
), null;
|
|
1203
1208
|
}
|
|
1204
1209
|
}, /**
|
|
@@ -1209,24 +1214,24 @@ Y = function(e) {
|
|
|
1209
1214
|
* @private
|
|
1210
1215
|
*/
|
|
1211
1216
|
W = function(e) {
|
|
1212
|
-
const
|
|
1213
|
-
return
|
|
1217
|
+
const t = this.config.staticMap[e];
|
|
1218
|
+
return t !== void 0 ? Array.isArray(t) ? t : [] : null;
|
|
1214
1219
|
};
|
|
1215
1220
|
function ye(r, e) {
|
|
1216
|
-
return async (
|
|
1217
|
-
const
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
), o = r(
|
|
1221
|
-
if (
|
|
1222
|
-
const a = await i.enableToolsets(
|
|
1221
|
+
return async (t) => {
|
|
1222
|
+
const s = e.resolvePermissions(
|
|
1223
|
+
t.clientId,
|
|
1224
|
+
t.headers
|
|
1225
|
+
), o = r(s), i = o.orchestrator.getManager(), n = [], l = [];
|
|
1226
|
+
if (s.length > 0) {
|
|
1227
|
+
const a = await i.enableToolsets(s);
|
|
1223
1228
|
for (const c of a.results)
|
|
1224
1229
|
c.success ? n.push(c.name) : (l.push(c.name), console.warn(
|
|
1225
|
-
`Failed to enable toolset '${c.name}' for client '${
|
|
1230
|
+
`Failed to enable toolset '${c.name}' for client '${t.clientId}': ${c.message}`
|
|
1226
1231
|
));
|
|
1227
1232
|
if (n.length === 0 && l.length > 0)
|
|
1228
1233
|
throw new Error(
|
|
1229
|
-
`All requested toolsets failed to enable for client '${
|
|
1234
|
+
`All requested toolsets failed to enable for client '${t.clientId}'. Requested: [${s.join(", ")}]. Check that toolset names in permissions match the catalog.`
|
|
1230
1235
|
);
|
|
1231
1236
|
}
|
|
1232
1237
|
return {
|
|
@@ -1237,7 +1242,7 @@ function ye(r, e) {
|
|
|
1237
1242
|
};
|
|
1238
1243
|
};
|
|
1239
1244
|
}
|
|
1240
|
-
var
|
|
1245
|
+
var h, U, q, J, G, K, Z, Q, X, C, ee;
|
|
1241
1246
|
class ve {
|
|
1242
1247
|
/**
|
|
1243
1248
|
* Creates a new PermissionAwareFastifyTransport instance.
|
|
@@ -1246,8 +1251,8 @@ class ve {
|
|
|
1246
1251
|
* @param options - Transport configuration options
|
|
1247
1252
|
* @param configSchema - Optional JSON schema for configuration discovery
|
|
1248
1253
|
*/
|
|
1249
|
-
constructor(e,
|
|
1250
|
-
b(this,
|
|
1254
|
+
constructor(e, t, s = {}, o) {
|
|
1255
|
+
b(this, h);
|
|
1251
1256
|
d(this, "options");
|
|
1252
1257
|
d(this, "defaultManager");
|
|
1253
1258
|
d(this, "createPermissionAwareBundle");
|
|
@@ -1255,18 +1260,18 @@ class ve {
|
|
|
1255
1260
|
d(this, "configSchema");
|
|
1256
1261
|
// Per-client server bundles and per-client session transports
|
|
1257
1262
|
d(this, "clientCache", new V({
|
|
1258
|
-
onEvict: (e,
|
|
1259
|
-
m(this,
|
|
1263
|
+
onEvict: (e, t) => {
|
|
1264
|
+
m(this, h, U).call(this, t);
|
|
1260
1265
|
}
|
|
1261
1266
|
}));
|
|
1262
|
-
this.defaultManager = e, this.createPermissionAwareBundle =
|
|
1263
|
-
host:
|
|
1264
|
-
port:
|
|
1265
|
-
basePath:
|
|
1266
|
-
cors:
|
|
1267
|
-
logger:
|
|
1268
|
-
app:
|
|
1269
|
-
customEndpoints:
|
|
1267
|
+
this.defaultManager = e, this.createPermissionAwareBundle = t, this.options = {
|
|
1268
|
+
host: s.host ?? "0.0.0.0",
|
|
1269
|
+
port: s.port ?? 3e3,
|
|
1270
|
+
basePath: s.basePath ?? "/",
|
|
1271
|
+
cors: s.cors ?? !0,
|
|
1272
|
+
logger: s.logger ?? !1,
|
|
1273
|
+
app: s.app,
|
|
1274
|
+
customEndpoints: s.customEndpoints
|
|
1270
1275
|
}, this.configSchema = o;
|
|
1271
1276
|
}
|
|
1272
1277
|
/**
|
|
@@ -1277,10 +1282,10 @@ class ve {
|
|
|
1277
1282
|
if (this.app) return;
|
|
1278
1283
|
const e = this.options.app ?? j({ logger: this.options.logger });
|
|
1279
1284
|
this.options.cors && await e.register(k, { origin: !0 });
|
|
1280
|
-
const
|
|
1281
|
-
m(this,
|
|
1282
|
-
contextExtractor: async (
|
|
1283
|
-
const o = m(this,
|
|
1285
|
+
const t = m(this, h, q).call(this, this.options.basePath);
|
|
1286
|
+
m(this, h, J).call(this, e, t), m(this, h, G).call(this, e, t), m(this, h, K).call(this, e, t), m(this, h, Z).call(this, e, t), m(this, h, Q).call(this, e, t), m(this, h, X).call(this, e, t), this.options.customEndpoints && this.options.customEndpoints.length > 0 && F(e, t, this.options.customEndpoints, {
|
|
1287
|
+
contextExtractor: async (s) => {
|
|
1288
|
+
const o = m(this, h, C).call(this, s);
|
|
1284
1289
|
try {
|
|
1285
1290
|
const i = await this.createPermissionAwareBundle(o);
|
|
1286
1291
|
return {
|
|
@@ -1306,20 +1311,20 @@ class ve {
|
|
|
1306
1311
|
this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
|
|
1307
1312
|
}
|
|
1308
1313
|
}
|
|
1309
|
-
|
|
1314
|
+
h = new WeakSet(), /**
|
|
1310
1315
|
* Cleans up resources associated with a client bundle.
|
|
1311
1316
|
* Closes all sessions within the bundle.
|
|
1312
1317
|
* @param bundle - The client bundle to clean up
|
|
1313
1318
|
* @private
|
|
1314
1319
|
*/
|
|
1315
1320
|
U = function(e) {
|
|
1316
|
-
for (const [
|
|
1321
|
+
for (const [t, s] of e.sessions.entries())
|
|
1317
1322
|
try {
|
|
1318
|
-
typeof
|
|
1319
|
-
console.warn(`Error closing session ${
|
|
1323
|
+
typeof s.close == "function" && s.close().catch((o) => {
|
|
1324
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
1320
1325
|
});
|
|
1321
1326
|
} catch (o) {
|
|
1322
|
-
console.warn(`Error closing session ${
|
|
1327
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
1323
1328
|
}
|
|
1324
1329
|
e.sessions.clear();
|
|
1325
1330
|
}, /**
|
|
@@ -1336,24 +1341,24 @@ q = function(e) {
|
|
|
1336
1341
|
* @param base - Base path for routes
|
|
1337
1342
|
* @private
|
|
1338
1343
|
*/
|
|
1339
|
-
J = function(e,
|
|
1340
|
-
e.get(`${
|
|
1344
|
+
J = function(e, t) {
|
|
1345
|
+
e.get(`${t}/healthz`, async () => ({ ok: !0 }));
|
|
1341
1346
|
}, /**
|
|
1342
1347
|
* Registers the tools status endpoint.
|
|
1343
1348
|
* @param app - Fastify instance
|
|
1344
1349
|
* @param base - Base path for routes
|
|
1345
1350
|
* @private
|
|
1346
1351
|
*/
|
|
1347
|
-
G = function(e,
|
|
1348
|
-
e.get(`${
|
|
1352
|
+
G = function(e, t) {
|
|
1353
|
+
e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
|
|
1349
1354
|
}, /**
|
|
1350
1355
|
* Registers the MCP configuration discovery endpoint.
|
|
1351
1356
|
* @param app - Fastify instance
|
|
1352
1357
|
* @param base - Base path for routes
|
|
1353
1358
|
* @private
|
|
1354
1359
|
*/
|
|
1355
|
-
K = function(e,
|
|
1356
|
-
e.get(`${
|
|
1360
|
+
K = function(e, t) {
|
|
1361
|
+
e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
1357
1362
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1358
1363
|
title: "MCP Session Configuration",
|
|
1359
1364
|
description: "Schema for the /mcp endpoint configuration",
|
|
@@ -1370,40 +1375,40 @@ K = function(e, s) {
|
|
|
1370
1375
|
* @param base - Base path for routes
|
|
1371
1376
|
* @private
|
|
1372
1377
|
*/
|
|
1373
|
-
Z = function(e,
|
|
1378
|
+
Z = function(e, t) {
|
|
1374
1379
|
e.post(
|
|
1375
|
-
`${
|
|
1376
|
-
async (
|
|
1377
|
-
const i = m(this,
|
|
1380
|
+
`${t}/mcp`,
|
|
1381
|
+
async (s, o) => {
|
|
1382
|
+
const i = m(this, h, C).call(this, s), n = !i.clientId.startsWith("anon-");
|
|
1378
1383
|
let l = n ? this.clientCache.get(i.clientId) : null;
|
|
1379
1384
|
if (!l)
|
|
1380
1385
|
try {
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
`Client ${i.clientId} had ${
|
|
1386
|
+
const u = await this.createPermissionAwareBundle(i);
|
|
1387
|
+
u.failedToolsets.length > 0 && console.warn(
|
|
1388
|
+
`Client ${i.clientId} had ${u.failedToolsets.length} toolsets fail to enable: [${u.failedToolsets.join(", ")}]. Successfully enabled: [${u.allowedToolsets.join(", ")}]`
|
|
1384
1389
|
);
|
|
1385
|
-
const f =
|
|
1390
|
+
const f = u.sessions;
|
|
1386
1391
|
l = {
|
|
1387
|
-
server:
|
|
1388
|
-
orchestrator:
|
|
1389
|
-
allowedToolsets:
|
|
1390
|
-
failedToolsets:
|
|
1392
|
+
server: u.server,
|
|
1393
|
+
orchestrator: u.orchestrator,
|
|
1394
|
+
allowedToolsets: u.allowedToolsets,
|
|
1395
|
+
failedToolsets: u.failedToolsets,
|
|
1391
1396
|
sessions: f instanceof Map ? f : /* @__PURE__ */ new Map()
|
|
1392
1397
|
}, n && this.clientCache.set(i.clientId, l);
|
|
1393
|
-
} catch (
|
|
1398
|
+
} catch (u) {
|
|
1394
1399
|
return console.error(
|
|
1395
1400
|
`Failed to create permission-aware bundle for client ${i.clientId}:`,
|
|
1396
|
-
|
|
1397
|
-
), o.code(403), m(this,
|
|
1401
|
+
u
|
|
1402
|
+
), o.code(403), m(this, h, ee).call(this, "Access denied");
|
|
1398
1403
|
}
|
|
1399
|
-
const a =
|
|
1404
|
+
const a = s.headers["mcp-session-id"];
|
|
1400
1405
|
let c;
|
|
1401
1406
|
if (a && l.sessions.get(a))
|
|
1402
1407
|
c = l.sessions.get(a);
|
|
1403
|
-
else if (!a &&
|
|
1404
|
-
const
|
|
1408
|
+
else if (!a && O(s.body)) {
|
|
1409
|
+
const u = T();
|
|
1405
1410
|
c = new D({
|
|
1406
|
-
sessionIdGenerator: () =>
|
|
1411
|
+
sessionIdGenerator: () => u,
|
|
1407
1412
|
onsessioninitialized: (f) => {
|
|
1408
1413
|
l.sessions.set(f, c);
|
|
1409
1414
|
}
|
|
@@ -1427,9 +1432,9 @@ Z = function(e, s) {
|
|
|
1427
1432
|
id: null
|
|
1428
1433
|
};
|
|
1429
1434
|
return await c.handleRequest(
|
|
1430
|
-
|
|
1435
|
+
s.raw,
|
|
1431
1436
|
o.raw,
|
|
1432
|
-
|
|
1437
|
+
s.body
|
|
1433
1438
|
), o;
|
|
1434
1439
|
}
|
|
1435
1440
|
);
|
|
@@ -1439,19 +1444,19 @@ Z = function(e, s) {
|
|
|
1439
1444
|
* @param base - Base path for routes
|
|
1440
1445
|
* @private
|
|
1441
1446
|
*/
|
|
1442
|
-
Q = function(e,
|
|
1443
|
-
e.get(`${
|
|
1444
|
-
const i =
|
|
1447
|
+
Q = function(e, t) {
|
|
1448
|
+
e.get(`${t}/mcp`, async (s, o) => {
|
|
1449
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
|
|
1445
1450
|
if (!n)
|
|
1446
1451
|
return o.code(400), "Missing mcp-client-id";
|
|
1447
1452
|
const l = this.clientCache.get(n);
|
|
1448
1453
|
if (!l)
|
|
1449
1454
|
return o.code(400), "Invalid or expired client";
|
|
1450
|
-
const a =
|
|
1455
|
+
const a = s.headers["mcp-session-id"];
|
|
1451
1456
|
if (!a)
|
|
1452
1457
|
return o.code(400), "Missing mcp-session-id";
|
|
1453
1458
|
const c = l.sessions.get(a);
|
|
1454
|
-
return c ? (await c.handleRequest(
|
|
1459
|
+
return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
|
|
1455
1460
|
});
|
|
1456
1461
|
}, /**
|
|
1457
1462
|
* Registers the DELETE /mcp endpoint for session termination.
|
|
@@ -1459,11 +1464,11 @@ Q = function(e, s) {
|
|
|
1459
1464
|
* @param base - Base path for routes
|
|
1460
1465
|
* @private
|
|
1461
1466
|
*/
|
|
1462
|
-
X = function(e,
|
|
1467
|
+
X = function(e, t) {
|
|
1463
1468
|
e.delete(
|
|
1464
|
-
`${
|
|
1465
|
-
async (
|
|
1466
|
-
const i =
|
|
1469
|
+
`${t}/mcp`,
|
|
1470
|
+
async (s, o) => {
|
|
1471
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "", l = s.headers["mcp-session-id"];
|
|
1467
1472
|
if (!n || !l)
|
|
1468
1473
|
return o.code(400), {
|
|
1469
1474
|
jsonrpc: "2.0",
|
|
@@ -1500,10 +1505,10 @@ X = function(e, s) {
|
|
|
1500
1505
|
* @private
|
|
1501
1506
|
*/
|
|
1502
1507
|
C = function(e) {
|
|
1503
|
-
const
|
|
1508
|
+
const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${T()}`, o = {};
|
|
1504
1509
|
for (const [i, n] of Object.entries(e.headers))
|
|
1505
1510
|
typeof n == "string" && (o[i] = n);
|
|
1506
|
-
return { clientId:
|
|
1511
|
+
return { clientId: s, headers: o };
|
|
1507
1512
|
}, /**
|
|
1508
1513
|
* Creates a safe error response that doesn't expose unauthorized toolset information.
|
|
1509
1514
|
* Used for permission-related errors to prevent information leakage.
|
|
@@ -1512,11 +1517,11 @@ C = function(e) {
|
|
|
1512
1517
|
* @returns JSON-RPC error response object
|
|
1513
1518
|
* @private
|
|
1514
1519
|
*/
|
|
1515
|
-
ee = function(e = "Access denied",
|
|
1520
|
+
ee = function(e = "Access denied", t = -32e3) {
|
|
1516
1521
|
return {
|
|
1517
1522
|
jsonrpc: "2.0",
|
|
1518
1523
|
error: {
|
|
1519
|
-
code:
|
|
1524
|
+
code: t,
|
|
1520
1525
|
message: e
|
|
1521
1526
|
},
|
|
1522
1527
|
id: null
|
|
@@ -1552,8 +1557,8 @@ async function Me(r) {
|
|
|
1552
1557
|
);
|
|
1553
1558
|
const e = Te(
|
|
1554
1559
|
r.exposurePolicy
|
|
1555
|
-
),
|
|
1556
|
-
server:
|
|
1560
|
+
), t = new pe(r.permissions), s = r.createServer(), o = new A({
|
|
1561
|
+
server: s,
|
|
1557
1562
|
catalog: r.catalog,
|
|
1558
1563
|
moduleLoaders: r.moduleLoaders,
|
|
1559
1564
|
exposurePolicy: e,
|
|
@@ -1579,7 +1584,7 @@ async function Me(r) {
|
|
|
1579
1584
|
});
|
|
1580
1585
|
return { server: a, orchestrator: c };
|
|
1581
1586
|
},
|
|
1582
|
-
|
|
1587
|
+
t
|
|
1583
1588
|
), n = new ve(
|
|
1584
1589
|
o.getManager(),
|
|
1585
1590
|
i,
|
|
@@ -1587,7 +1592,7 @@ async function Me(r) {
|
|
|
1587
1592
|
r.configSchema
|
|
1588
1593
|
);
|
|
1589
1594
|
return {
|
|
1590
|
-
server:
|
|
1595
|
+
server: s,
|
|
1591
1596
|
start: async () => {
|
|
1592
1597
|
await n.start();
|
|
1593
1598
|
},
|
|
@@ -1595,7 +1600,7 @@ async function Me(r) {
|
|
|
1595
1600
|
try {
|
|
1596
1601
|
await n.stop();
|
|
1597
1602
|
} finally {
|
|
1598
|
-
|
|
1603
|
+
t.clearCache();
|
|
1599
1604
|
}
|
|
1600
1605
|
}
|
|
1601
1606
|
};
|