toolception 0.6.0 → 0.6.1
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
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
1
|
+
var re = Object.defineProperty;
|
|
2
|
+
var j = (o) => {
|
|
3
3
|
throw TypeError(o);
|
|
4
4
|
};
|
|
5
|
-
var
|
|
6
|
-
var d = (o, e, t) =>
|
|
7
|
-
var
|
|
8
|
-
var m = (o, e, t) => (
|
|
5
|
+
var ie = (o, e, t) => e in o ? re(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
|
|
6
|
+
var d = (o, e, t) => ie(o, typeof e != "symbol" ? e + "" : e, t), ne = (o, e, t) => e.has(o) || j("Cannot " + t);
|
|
7
|
+
var A = (o, e, t) => e.has(o) ? j("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(o) : e.set(o, t);
|
|
8
|
+
var m = (o, e, t) => (ne(o, e, "access private method"), t);
|
|
9
9
|
import { z as v } from "zod";
|
|
10
10
|
import N from "fastify";
|
|
11
|
-
import
|
|
12
|
-
import { randomUUID as
|
|
13
|
-
import { StreamableHTTPServerTransport as
|
|
14
|
-
import { isInitializeRequest as
|
|
11
|
+
import D from "@fastify/cors";
|
|
12
|
+
import { randomUUID as S, createHash as ae } from "node:crypto";
|
|
13
|
+
import { StreamableHTTPServerTransport as z } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
14
|
+
import { isInitializeRequest as _ } from "@modelcontextprotocol/sdk/types.js";
|
|
15
15
|
const R = {
|
|
16
16
|
dynamic: [
|
|
17
17
|
"dynamic-tool-discovery",
|
|
@@ -20,7 +20,7 @@ const R = {
|
|
|
20
20
|
],
|
|
21
21
|
toolsets: ["tool-sets", "toolSets", "FMP_TOOL_SETS"]
|
|
22
22
|
};
|
|
23
|
-
class
|
|
23
|
+
class le {
|
|
24
24
|
constructor(e = {}) {
|
|
25
25
|
d(this, "keys");
|
|
26
26
|
this.keys = {
|
|
@@ -112,10 +112,16 @@ class ne {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
|
|
115
|
+
const k = ["_meta"];
|
|
116
|
+
class ce {
|
|
116
117
|
constructor(e) {
|
|
117
118
|
d(this, "catalog");
|
|
118
119
|
d(this, "moduleLoaders");
|
|
120
|
+
for (const t of k)
|
|
121
|
+
if (t in e.catalog)
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Toolset key '${t}' is reserved for internal use and cannot be used in the catalog`
|
|
124
|
+
);
|
|
119
125
|
this.catalog = e.catalog, this.moduleLoaders = e.moduleLoaders ?? {};
|
|
120
126
|
}
|
|
121
127
|
getAvailableToolsets() {
|
|
@@ -138,6 +144,9 @@ class ae {
|
|
|
138
144
|
error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(
|
|
139
145
|
", "
|
|
140
146
|
)}`
|
|
147
|
+
} : k.includes(t) ? {
|
|
148
|
+
isValid: !1,
|
|
149
|
+
error: `Toolset key '${t}' is reserved for internal use`
|
|
141
150
|
} : this.catalog[t] ? { isValid: !0, sanitized: t } : {
|
|
142
151
|
isValid: !1,
|
|
143
152
|
error: `Toolset '${t}' not found. Available toolsets: ${this.getAvailableToolsets().join(
|
|
@@ -151,10 +160,10 @@ class ae {
|
|
|
151
160
|
const i = this.catalog[r];
|
|
152
161
|
if (i && (Array.isArray(i.tools) && i.tools.length > 0 && s.push(...i.tools), Array.isArray(i.modules) && i.modules.length > 0))
|
|
153
162
|
for (const n of i.modules) {
|
|
154
|
-
const
|
|
155
|
-
if (
|
|
163
|
+
const l = this.moduleLoaders[n];
|
|
164
|
+
if (l)
|
|
156
165
|
try {
|
|
157
|
-
const c = await
|
|
166
|
+
const c = await l(t);
|
|
158
167
|
Array.isArray(c) && c.length > 0 && s.push(...c);
|
|
159
168
|
} catch (c) {
|
|
160
169
|
console.warn(
|
|
@@ -167,7 +176,7 @@ class ae {
|
|
|
167
176
|
return s;
|
|
168
177
|
}
|
|
169
178
|
}
|
|
170
|
-
class
|
|
179
|
+
class O extends Error {
|
|
171
180
|
constructor(t, s, r, i) {
|
|
172
181
|
super(t);
|
|
173
182
|
d(this, "code");
|
|
@@ -175,7 +184,7 @@ class j extends Error {
|
|
|
175
184
|
this.name = "ToolingError", this.code = s, this.details = r;
|
|
176
185
|
}
|
|
177
186
|
}
|
|
178
|
-
class
|
|
187
|
+
class q {
|
|
179
188
|
constructor(e = {}) {
|
|
180
189
|
d(this, "options");
|
|
181
190
|
d(this, "names", /* @__PURE__ */ new Set());
|
|
@@ -192,7 +201,7 @@ class z {
|
|
|
192
201
|
}
|
|
193
202
|
add(e) {
|
|
194
203
|
if (this.names.has(e))
|
|
195
|
-
throw new
|
|
204
|
+
throw new O(
|
|
196
205
|
`Tool name collision: '${e}' already registered`,
|
|
197
206
|
"E_TOOL_NAME_CONFLICT"
|
|
198
207
|
);
|
|
@@ -207,7 +216,7 @@ class z {
|
|
|
207
216
|
return t.map((s) => {
|
|
208
217
|
const r = this.getSafeName(e, s.name);
|
|
209
218
|
if (this.has(r))
|
|
210
|
-
throw new
|
|
219
|
+
throw new O(
|
|
211
220
|
`Tool name collision for '${r}'`,
|
|
212
221
|
"E_TOOL_NAME_CONFLICT"
|
|
213
222
|
);
|
|
@@ -224,7 +233,7 @@ class z {
|
|
|
224
233
|
return e;
|
|
225
234
|
}
|
|
226
235
|
}
|
|
227
|
-
class
|
|
236
|
+
class de {
|
|
228
237
|
constructor(e) {
|
|
229
238
|
d(this, "server");
|
|
230
239
|
d(this, "resolver");
|
|
@@ -233,7 +242,7 @@ class le {
|
|
|
233
242
|
d(this, "exposurePolicy");
|
|
234
243
|
d(this, "toolRegistry");
|
|
235
244
|
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
|
|
245
|
+
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 q({ namespaceWithToolset: !0 });
|
|
237
246
|
}
|
|
238
247
|
/**
|
|
239
248
|
* Sends a tool list change notification if configured.
|
|
@@ -286,28 +295,28 @@ class le {
|
|
|
286
295
|
return { success: !1, message: i.message };
|
|
287
296
|
const n = [];
|
|
288
297
|
try {
|
|
289
|
-
const
|
|
298
|
+
const l = await this.resolver.resolveToolsForToolsets(
|
|
290
299
|
[r],
|
|
291
300
|
this.context
|
|
292
301
|
);
|
|
293
|
-
if (
|
|
302
|
+
if (l && l.length > 0) {
|
|
294
303
|
const c = this.toolRegistry.mapAndValidate(
|
|
295
304
|
r,
|
|
296
|
-
|
|
305
|
+
l
|
|
297
306
|
);
|
|
298
|
-
for (const
|
|
299
|
-
this.registerSingleTool(
|
|
307
|
+
for (const a of c)
|
|
308
|
+
this.registerSingleTool(a, r), n.push(a.name);
|
|
300
309
|
}
|
|
301
310
|
return this.activeToolsets.add(r), t || await this.notifyToolsChanged(), {
|
|
302
311
|
success: !0,
|
|
303
|
-
message: `Toolset '${r}' enabled successfully. Registered ${
|
|
312
|
+
message: `Toolset '${r}' enabled successfully. Registered ${l?.length ?? 0} tools.`
|
|
304
313
|
};
|
|
305
|
-
} catch (
|
|
314
|
+
} catch (l) {
|
|
306
315
|
return n.length > 0 && console.warn(
|
|
307
316
|
`Partial failure enabling toolset '${r}'. ${n.length} tools were registered but toolset activation failed. Tools remain registered due to MCP limitations: ${n.join(", ")}`
|
|
308
317
|
), {
|
|
309
318
|
success: !1,
|
|
310
|
-
message: `Failed to enable toolset '${r}': ${
|
|
319
|
+
message: `Failed to enable toolset '${r}': ${l instanceof Error ? l.message : "Unknown error"}`
|
|
311
320
|
};
|
|
312
321
|
}
|
|
313
322
|
}
|
|
@@ -397,13 +406,13 @@ class le {
|
|
|
397
406
|
const t = [];
|
|
398
407
|
for (const n of e)
|
|
399
408
|
try {
|
|
400
|
-
const
|
|
401
|
-
t.push({ name: n, ...
|
|
402
|
-
} catch (
|
|
409
|
+
const l = await this.enableToolset(n, !0);
|
|
410
|
+
t.push({ name: n, ...l });
|
|
411
|
+
} catch (l) {
|
|
403
412
|
t.push({
|
|
404
413
|
name: n,
|
|
405
414
|
success: !1,
|
|
406
|
-
message:
|
|
415
|
+
message: l instanceof Error ? l.message : "Unknown error",
|
|
407
416
|
code: "E_INTERNAL"
|
|
408
417
|
});
|
|
409
418
|
}
|
|
@@ -419,103 +428,104 @@ class le {
|
|
|
419
428
|
return this.enableToolsets(e);
|
|
420
429
|
}
|
|
421
430
|
}
|
|
422
|
-
|
|
423
|
-
|
|
431
|
+
const b = "_meta";
|
|
432
|
+
function ue(o, e, t, s) {
|
|
433
|
+
(s?.mode ?? "DYNAMIC") === "DYNAMIC" && (t.addForToolset(b, "enable_toolset"), o.tool(
|
|
424
434
|
"enable_toolset",
|
|
425
435
|
"Enable a toolset by name",
|
|
426
436
|
{ name: v.string().describe("Toolset name") },
|
|
427
437
|
{ destructiveHint: !0, idempotentHint: !0 },
|
|
428
|
-
async (
|
|
429
|
-
const
|
|
438
|
+
async (i) => {
|
|
439
|
+
const n = await e.enableToolset(i.name);
|
|
430
440
|
return {
|
|
431
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
441
|
+
content: [{ type: "text", text: JSON.stringify(n) }]
|
|
432
442
|
};
|
|
433
443
|
}
|
|
434
|
-
), o.tool(
|
|
444
|
+
), t.addForToolset(b, "disable_toolset"), o.tool(
|
|
435
445
|
"disable_toolset",
|
|
436
446
|
"Disable a toolset by name (state only)",
|
|
437
447
|
{ name: v.string().describe("Toolset name") },
|
|
438
448
|
{ destructiveHint: !0, idempotentHint: !0 },
|
|
439
|
-
async (
|
|
440
|
-
const
|
|
449
|
+
async (i) => {
|
|
450
|
+
const n = await e.disableToolset(i.name);
|
|
441
451
|
return {
|
|
442
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
452
|
+
content: [{ type: "text", text: JSON.stringify(n) }]
|
|
443
453
|
};
|
|
444
454
|
}
|
|
445
|
-
), o.tool(
|
|
455
|
+
), t.addForToolset(b, "list_toolsets"), o.tool(
|
|
446
456
|
"list_toolsets",
|
|
447
457
|
"List available toolsets with active status and definitions",
|
|
448
458
|
{},
|
|
449
459
|
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
450
460
|
async () => {
|
|
451
|
-
const
|
|
452
|
-
const
|
|
461
|
+
const i = e.getAvailableToolsets(), n = e.getStatus().toolsetToTools, l = i.map((c) => {
|
|
462
|
+
const a = e.getToolsetDefinition(c);
|
|
453
463
|
return {
|
|
454
|
-
key:
|
|
455
|
-
active: e.isActive(
|
|
456
|
-
definition:
|
|
457
|
-
name:
|
|
458
|
-
description:
|
|
459
|
-
modules:
|
|
460
|
-
decisionCriteria:
|
|
464
|
+
key: c,
|
|
465
|
+
active: e.isActive(c),
|
|
466
|
+
definition: a ? {
|
|
467
|
+
name: a.name,
|
|
468
|
+
description: a.description,
|
|
469
|
+
modules: a.modules ?? [],
|
|
470
|
+
decisionCriteria: a.decisionCriteria ?? void 0
|
|
461
471
|
} : null,
|
|
462
|
-
tools:
|
|
472
|
+
tools: n[c] ?? []
|
|
463
473
|
};
|
|
464
474
|
});
|
|
465
475
|
return {
|
|
466
476
|
content: [
|
|
467
|
-
{ type: "text", text: JSON.stringify({ toolsets:
|
|
477
|
+
{ type: "text", text: JSON.stringify({ toolsets: l }) }
|
|
468
478
|
]
|
|
469
479
|
};
|
|
470
480
|
}
|
|
471
|
-
), o.tool(
|
|
481
|
+
), t.addForToolset(b, "describe_toolset"), o.tool(
|
|
472
482
|
"describe_toolset",
|
|
473
483
|
"Describe a toolset with definition, active status and tools",
|
|
474
484
|
{ name: v.string().describe("Toolset name") },
|
|
475
485
|
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
476
|
-
async (
|
|
477
|
-
const
|
|
478
|
-
if (!
|
|
486
|
+
async (i) => {
|
|
487
|
+
const n = e.getToolsetDefinition(i.name), l = e.getStatus().toolsetToTools;
|
|
488
|
+
if (!n)
|
|
479
489
|
return {
|
|
480
490
|
content: [
|
|
481
491
|
{
|
|
482
492
|
type: "text",
|
|
483
|
-
text: JSON.stringify({ error: `Unknown toolset '${
|
|
493
|
+
text: JSON.stringify({ error: `Unknown toolset '${i.name}'` })
|
|
484
494
|
}
|
|
485
495
|
]
|
|
486
496
|
};
|
|
487
|
-
const
|
|
488
|
-
key:
|
|
489
|
-
active: e.isActive(
|
|
497
|
+
const c = {
|
|
498
|
+
key: i.name,
|
|
499
|
+
active: e.isActive(i.name),
|
|
490
500
|
definition: {
|
|
491
|
-
name:
|
|
492
|
-
description:
|
|
493
|
-
modules:
|
|
494
|
-
decisionCriteria:
|
|
501
|
+
name: n.name,
|
|
502
|
+
description: n.description,
|
|
503
|
+
modules: n.modules ?? [],
|
|
504
|
+
decisionCriteria: n.decisionCriteria ?? void 0
|
|
495
505
|
},
|
|
496
|
-
tools:
|
|
506
|
+
tools: l[i.name] ?? []
|
|
497
507
|
};
|
|
498
508
|
return {
|
|
499
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
509
|
+
content: [{ type: "text", text: JSON.stringify(c) }]
|
|
500
510
|
};
|
|
501
511
|
}
|
|
502
|
-
)), o.tool(
|
|
512
|
+
)), t.addForToolset(b, "list_tools"), o.tool(
|
|
503
513
|
"list_tools",
|
|
504
514
|
"List currently registered tool names (best effort)",
|
|
505
515
|
{},
|
|
506
516
|
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
507
517
|
async () => {
|
|
508
|
-
const
|
|
509
|
-
tools:
|
|
510
|
-
toolsetToTools:
|
|
518
|
+
const i = e.getStatus(), n = {
|
|
519
|
+
tools: i.tools,
|
|
520
|
+
toolsetToTools: i.toolsetToTools
|
|
511
521
|
};
|
|
512
522
|
return {
|
|
513
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
523
|
+
content: [{ type: "text", text: JSON.stringify(n) }]
|
|
514
524
|
};
|
|
515
525
|
}
|
|
516
526
|
);
|
|
517
527
|
}
|
|
518
|
-
class
|
|
528
|
+
class C {
|
|
519
529
|
constructor(e) {
|
|
520
530
|
d(this, "mode");
|
|
521
531
|
d(this, "resolver");
|
|
@@ -523,23 +533,23 @@ class A {
|
|
|
523
533
|
d(this, "toolsetValidator");
|
|
524
534
|
d(this, "initPromise");
|
|
525
535
|
d(this, "initError", null);
|
|
526
|
-
this.toolsetValidator = new
|
|
536
|
+
this.toolsetValidator = new le();
|
|
527
537
|
const t = e.startup ?? {}, s = this.resolveStartupConfig(t, e.catalog);
|
|
528
|
-
this.mode = s.mode, this.resolver = new
|
|
538
|
+
this.mode = s.mode, this.resolver = new ce({
|
|
529
539
|
catalog: e.catalog,
|
|
530
540
|
moduleLoaders: e.moduleLoaders
|
|
531
541
|
});
|
|
532
|
-
const r = new
|
|
542
|
+
const r = new q({
|
|
533
543
|
namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
|
|
534
544
|
});
|
|
535
|
-
this.manager = new
|
|
545
|
+
this.manager = new de({
|
|
536
546
|
server: e.server,
|
|
537
547
|
resolver: this.resolver,
|
|
538
548
|
context: e.context,
|
|
539
549
|
onToolsListChanged: e.notifyToolsListChanged,
|
|
540
550
|
exposurePolicy: e.exposurePolicy,
|
|
541
551
|
toolRegistry: r
|
|
542
|
-
}), e.registerMetaTools !== !1 &&
|
|
552
|
+
}), e.registerMetaTools !== !1 && ue(e.server, this.manager, r, { mode: this.mode });
|
|
543
553
|
const i = s.toolsets;
|
|
544
554
|
this.initPromise = this.initializeToolsets(i);
|
|
545
555
|
}
|
|
@@ -583,8 +593,8 @@ class A {
|
|
|
583
593
|
return { mode: "STATIC", toolsets: "ALL" };
|
|
584
594
|
const s = Array.isArray(e.toolsets) ? e.toolsets : [], r = [];
|
|
585
595
|
for (const i of s) {
|
|
586
|
-
const { isValid: n, sanitized:
|
|
587
|
-
n &&
|
|
596
|
+
const { isValid: n, sanitized: l, error: c } = this.toolsetValidator.validateToolsetName(i, t);
|
|
597
|
+
n && l ? r.push(l) : c && console.warn(c);
|
|
588
598
|
}
|
|
589
599
|
if (s.length > 0 && r.length === 0)
|
|
590
600
|
throw new Error(
|
|
@@ -598,8 +608,8 @@ class A {
|
|
|
598
608
|
if (Array.isArray(e.toolsets) && e.toolsets.length > 0) {
|
|
599
609
|
const s = [];
|
|
600
610
|
for (const r of e.toolsets) {
|
|
601
|
-
const { isValid: i, sanitized: n, error:
|
|
602
|
-
i && n ? s.push(n) :
|
|
611
|
+
const { isValid: i, sanitized: n, error: l } = this.toolsetValidator.validateToolsetName(r, t);
|
|
612
|
+
i && n ? s.push(n) : l && console.warn(l);
|
|
603
613
|
}
|
|
604
614
|
if (s.length === 0)
|
|
605
615
|
throw new Error(
|
|
@@ -616,10 +626,10 @@ class A {
|
|
|
616
626
|
return this.manager;
|
|
617
627
|
}
|
|
618
628
|
}
|
|
619
|
-
var x,
|
|
620
|
-
class
|
|
629
|
+
var x, P;
|
|
630
|
+
class F {
|
|
621
631
|
constructor(e = {}) {
|
|
622
|
-
|
|
632
|
+
A(this, x);
|
|
623
633
|
d(this, "storage", /* @__PURE__ */ new Map());
|
|
624
634
|
d(this, "maxSize");
|
|
625
635
|
d(this, "ttlMs");
|
|
@@ -655,7 +665,7 @@ class q {
|
|
|
655
665
|
*/
|
|
656
666
|
delete(e) {
|
|
657
667
|
const t = this.storage.get(e);
|
|
658
|
-
t && (this.storage.delete(e), m(this, x,
|
|
668
|
+
t && (this.storage.delete(e), m(this, x, P).call(this, e, t.resource));
|
|
659
669
|
}
|
|
660
670
|
/**
|
|
661
671
|
* Stops the background pruning interval and optionally clears all entries.
|
|
@@ -672,7 +682,7 @@ class q {
|
|
|
672
682
|
const e = Array.from(this.storage.entries());
|
|
673
683
|
this.storage.clear();
|
|
674
684
|
for (const [t, s] of e)
|
|
675
|
-
m(this, x,
|
|
685
|
+
m(this, x, P).call(this, t, s.resource);
|
|
676
686
|
}
|
|
677
687
|
/**
|
|
678
688
|
* Evicts the least recently used entry from the cache.
|
|
@@ -700,7 +710,7 @@ x = new WeakSet(), /**
|
|
|
700
710
|
* @param resource - The resource being evicted
|
|
701
711
|
* @private
|
|
702
712
|
*/
|
|
703
|
-
|
|
713
|
+
P = function(e, t) {
|
|
704
714
|
if (this.onEvict)
|
|
705
715
|
try {
|
|
706
716
|
const s = this.onEvict(e, t);
|
|
@@ -716,7 +726,7 @@ function V(o, e, t, s) {
|
|
|
716
726
|
for (const i of t) {
|
|
717
727
|
const n = `${e}${i.path}`;
|
|
718
728
|
if (r.some(
|
|
719
|
-
(
|
|
729
|
+
(a) => n.startsWith(`${e}${a}`)
|
|
720
730
|
)) {
|
|
721
731
|
console.warn(
|
|
722
732
|
`Custom endpoint ${i.method} ${i.path} conflicts with built-in MCP endpoint. Skipping registration.`
|
|
@@ -724,44 +734,44 @@ function V(o, e, t, s) {
|
|
|
724
734
|
continue;
|
|
725
735
|
}
|
|
726
736
|
const c = i.method.toLowerCase();
|
|
727
|
-
o[c](n, async (
|
|
737
|
+
o[c](n, async (a, u) => {
|
|
728
738
|
try {
|
|
729
|
-
const h =
|
|
739
|
+
const h = a.headers["mcp-client-id"]?.trim(), g = h && h.length > 0 ? h : `anon-${S()}`;
|
|
730
740
|
let w;
|
|
731
741
|
if (i.bodySchema) {
|
|
732
|
-
const y = i.bodySchema.safeParse(
|
|
742
|
+
const y = i.bodySchema.safeParse(a.body);
|
|
733
743
|
if (!y.success)
|
|
734
|
-
return
|
|
744
|
+
return E(u, "body", y.error);
|
|
735
745
|
w = y.data;
|
|
736
746
|
}
|
|
737
747
|
let T = {};
|
|
738
748
|
if (i.querySchema) {
|
|
739
|
-
const y = i.querySchema.safeParse(
|
|
749
|
+
const y = i.querySchema.safeParse(a.query);
|
|
740
750
|
if (!y.success)
|
|
741
|
-
return
|
|
751
|
+
return E(u, "query", y.error);
|
|
742
752
|
T = y.data;
|
|
743
753
|
}
|
|
744
|
-
let
|
|
754
|
+
let I = {};
|
|
745
755
|
if (i.paramsSchema) {
|
|
746
|
-
const y = i.paramsSchema.safeParse(
|
|
756
|
+
const y = i.paramsSchema.safeParse(a.params);
|
|
747
757
|
if (!y.success)
|
|
748
|
-
return
|
|
749
|
-
|
|
758
|
+
return E(u, "params", y.error);
|
|
759
|
+
I = y.data;
|
|
750
760
|
}
|
|
751
|
-
const
|
|
761
|
+
const $ = {
|
|
752
762
|
body: w,
|
|
753
763
|
query: T,
|
|
754
|
-
params:
|
|
755
|
-
headers:
|
|
764
|
+
params: I,
|
|
765
|
+
headers: a.headers,
|
|
756
766
|
clientId: g
|
|
757
767
|
};
|
|
758
768
|
if (s?.contextExtractor) {
|
|
759
|
-
const y = await s.contextExtractor(
|
|
760
|
-
Object.assign(
|
|
769
|
+
const y = await s.contextExtractor(a);
|
|
770
|
+
Object.assign($, y);
|
|
761
771
|
}
|
|
762
|
-
const
|
|
772
|
+
const L = await i.handler($);
|
|
763
773
|
if (i.responseSchema) {
|
|
764
|
-
const y = i.responseSchema.safeParse(
|
|
774
|
+
const y = i.responseSchema.safeParse(L);
|
|
765
775
|
return y.success ? y.data : (console.error(
|
|
766
776
|
`Response validation failed for ${i.method} ${i.path}:`,
|
|
767
777
|
y.error
|
|
@@ -772,7 +782,7 @@ function V(o, e, t, s) {
|
|
|
772
782
|
}
|
|
773
783
|
});
|
|
774
784
|
}
|
|
775
|
-
return
|
|
785
|
+
return L;
|
|
776
786
|
} catch (h) {
|
|
777
787
|
return console.error(
|
|
778
788
|
`Error in custom endpoint ${i.method} ${i.path}:`,
|
|
@@ -787,7 +797,7 @@ function V(o, e, t, s) {
|
|
|
787
797
|
});
|
|
788
798
|
}
|
|
789
799
|
}
|
|
790
|
-
function
|
|
800
|
+
function E(o, e, t) {
|
|
791
801
|
return o.code(400), {
|
|
792
802
|
error: {
|
|
793
803
|
code: "VALIDATION_ERROR",
|
|
@@ -796,7 +806,7 @@ function C(o, e, t) {
|
|
|
796
806
|
}
|
|
797
807
|
};
|
|
798
808
|
}
|
|
799
|
-
class
|
|
809
|
+
class he {
|
|
800
810
|
constructor(e, t, s = {}, r, i, n) {
|
|
801
811
|
d(this, "options");
|
|
802
812
|
d(this, "defaultManager");
|
|
@@ -806,7 +816,7 @@ class de {
|
|
|
806
816
|
d(this, "app", null);
|
|
807
817
|
d(this, "configSchema");
|
|
808
818
|
// Per-client server bundles and per-client session transports
|
|
809
|
-
d(this, "clientCache", new
|
|
819
|
+
d(this, "clientCache", new F({
|
|
810
820
|
onEvict: (e, t) => {
|
|
811
821
|
this.cleanupBundle(t);
|
|
812
822
|
}
|
|
@@ -824,7 +834,7 @@ class de {
|
|
|
824
834
|
async start() {
|
|
825
835
|
if (this.app) return;
|
|
826
836
|
const e = this.options.app ?? N({ logger: this.options.logger });
|
|
827
|
-
this.options.cors && await e.register(
|
|
837
|
+
this.options.cors && await e.register(D, { origin: !0 });
|
|
828
838
|
const t = this.options.basePath.endsWith("/") ? this.options.basePath.slice(0, -1) : this.options.basePath;
|
|
829
839
|
e.get(`${t}/healthz`, async () => ({ ok: !0 })), e.get(`${t}/tools`, async () => this.defaultManager.getStatus()), e.get(`${t}/.well-known/mcp-config`, async (s, r) => (r.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
830
840
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -838,26 +848,26 @@ class de {
|
|
|
838
848
|
})), e.post(
|
|
839
849
|
`${t}/mcp`,
|
|
840
850
|
async (s, r) => {
|
|
841
|
-
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : `anon-${
|
|
851
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : `anon-${S()}`, l = !n.startsWith("anon-"), { cacheKey: c, mergedContext: a } = this.resolveSessionContext(
|
|
842
852
|
s,
|
|
843
853
|
n
|
|
844
854
|
);
|
|
845
|
-
let u =
|
|
855
|
+
let u = l ? this.clientCache.get(c) : null;
|
|
846
856
|
if (!u) {
|
|
847
|
-
const w = this.createBundle(
|
|
857
|
+
const w = this.createBundle(a);
|
|
848
858
|
u = {
|
|
849
859
|
server: w.server,
|
|
850
860
|
orchestrator: w.orchestrator,
|
|
851
861
|
sessions: /* @__PURE__ */ new Map()
|
|
852
|
-
},
|
|
862
|
+
}, l && this.clientCache.set(c, u);
|
|
853
863
|
}
|
|
854
864
|
const h = s.headers["mcp-session-id"];
|
|
855
865
|
let g;
|
|
856
866
|
if (h && u.sessions.get(h))
|
|
857
867
|
g = u.sessions.get(h);
|
|
858
|
-
else if (!h &&
|
|
859
|
-
const w =
|
|
860
|
-
g = new
|
|
868
|
+
else if (!h && _(s.body)) {
|
|
869
|
+
const w = S();
|
|
870
|
+
g = new z({
|
|
861
871
|
sessionIdGenerator: () => w,
|
|
862
872
|
onsessioninitialized: (T) => {
|
|
863
873
|
u.sessions.set(T, g);
|
|
@@ -891,19 +901,19 @@ class de {
|
|
|
891
901
|
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
|
|
892
902
|
if (!n)
|
|
893
903
|
return r.code(400), "Missing mcp-client-id";
|
|
894
|
-
const
|
|
895
|
-
if (!
|
|
904
|
+
const l = this.clientCache.get(n);
|
|
905
|
+
if (!l)
|
|
896
906
|
return r.code(400), "Invalid or expired client";
|
|
897
907
|
const c = s.headers["mcp-session-id"];
|
|
898
908
|
if (!c)
|
|
899
909
|
return r.code(400), "Missing mcp-session-id";
|
|
900
|
-
const
|
|
901
|
-
return
|
|
910
|
+
const a = l.sessions.get(c);
|
|
911
|
+
return a ? (await a.handleRequest(s.raw, r.raw), r) : (r.code(400), "Invalid or expired session ID");
|
|
902
912
|
}), e.delete(
|
|
903
913
|
`${t}/mcp`,
|
|
904
914
|
async (s, r) => {
|
|
905
|
-
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "",
|
|
906
|
-
if (!n || !
|
|
915
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "", l = s.headers["mcp-session-id"];
|
|
916
|
+
if (!n || !l)
|
|
907
917
|
return r.code(400), {
|
|
908
918
|
jsonrpc: "2.0",
|
|
909
919
|
error: {
|
|
@@ -912,21 +922,21 @@ class de {
|
|
|
912
922
|
},
|
|
913
923
|
id: null
|
|
914
924
|
};
|
|
915
|
-
const c = this.clientCache.get(n),
|
|
916
|
-
if (!c || !
|
|
925
|
+
const c = this.clientCache.get(n), a = c?.sessions.get(l);
|
|
926
|
+
if (!c || !a)
|
|
917
927
|
return r.code(404), {
|
|
918
928
|
jsonrpc: "2.0",
|
|
919
929
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
920
930
|
id: null
|
|
921
931
|
};
|
|
922
932
|
try {
|
|
923
|
-
if (typeof
|
|
933
|
+
if (typeof a.close == "function")
|
|
924
934
|
try {
|
|
925
|
-
await
|
|
935
|
+
await a.close();
|
|
926
936
|
} catch {
|
|
927
937
|
}
|
|
928
938
|
} finally {
|
|
929
|
-
|
|
939
|
+
a?.sessionId ? c.sessions.delete(a.sessionId) : c.sessions.delete(l);
|
|
930
940
|
}
|
|
931
941
|
return r.code(204).send(), r;
|
|
932
942
|
}
|
|
@@ -1014,7 +1024,7 @@ class de {
|
|
|
1014
1024
|
return t;
|
|
1015
1025
|
}
|
|
1016
1026
|
}
|
|
1017
|
-
class
|
|
1027
|
+
class fe {
|
|
1018
1028
|
constructor(e) {
|
|
1019
1029
|
d(this, "config");
|
|
1020
1030
|
d(this, "queryParamName");
|
|
@@ -1147,25 +1157,25 @@ class ue {
|
|
|
1147
1157
|
for (const i of t)
|
|
1148
1158
|
s[i] = e[i];
|
|
1149
1159
|
const r = JSON.stringify(s);
|
|
1150
|
-
return
|
|
1160
|
+
return ae("sha256").update(r).digest("hex").slice(0, 16);
|
|
1151
1161
|
}
|
|
1152
1162
|
}
|
|
1153
1163
|
function K(o) {
|
|
1154
|
-
|
|
1164
|
+
me(o), ye(o), ge(o), pe(o), ve(o);
|
|
1155
1165
|
}
|
|
1156
|
-
function
|
|
1166
|
+
function me(o) {
|
|
1157
1167
|
if (!o || typeof o != "object")
|
|
1158
1168
|
throw new Error(
|
|
1159
1169
|
"Session context configuration must be an object"
|
|
1160
1170
|
);
|
|
1161
1171
|
}
|
|
1162
|
-
function
|
|
1172
|
+
function ye(o) {
|
|
1163
1173
|
if (o.enabled !== void 0 && typeof o.enabled != "boolean")
|
|
1164
1174
|
throw new Error(
|
|
1165
1175
|
`enabled must be a boolean, got ${typeof o.enabled}`
|
|
1166
1176
|
);
|
|
1167
1177
|
}
|
|
1168
|
-
function
|
|
1178
|
+
function ge(o) {
|
|
1169
1179
|
if (o.queryParam !== void 0) {
|
|
1170
1180
|
if (typeof o.queryParam != "object" || o.queryParam === null)
|
|
1171
1181
|
throw new Error("queryParam must be an object");
|
|
@@ -1188,29 +1198,29 @@ function me(o) {
|
|
|
1188
1198
|
}
|
|
1189
1199
|
}
|
|
1190
1200
|
}
|
|
1191
|
-
function
|
|
1201
|
+
function pe(o) {
|
|
1192
1202
|
if (o.contextResolver !== void 0 && typeof o.contextResolver != "function")
|
|
1193
1203
|
throw new Error(
|
|
1194
1204
|
"contextResolver must be a function: (request, baseContext, parsedQueryConfig?) => unknown"
|
|
1195
1205
|
);
|
|
1196
1206
|
}
|
|
1197
|
-
function
|
|
1207
|
+
function ve(o) {
|
|
1198
1208
|
if (o.merge !== void 0 && o.merge !== "shallow" && o.merge !== "deep")
|
|
1199
1209
|
throw new Error(
|
|
1200
1210
|
`Invalid merge strategy: "${o.merge}". Must be "shallow" or "deep"`
|
|
1201
1211
|
);
|
|
1202
1212
|
}
|
|
1203
|
-
const
|
|
1213
|
+
const we = v.object({
|
|
1204
1214
|
mode: v.enum(["DYNAMIC", "STATIC"]).optional(),
|
|
1205
1215
|
toolsets: v.union([v.array(v.string()), v.literal("ALL")]).optional()
|
|
1206
1216
|
}).strict();
|
|
1207
|
-
async function
|
|
1217
|
+
async function De(o) {
|
|
1208
1218
|
if (o.startup)
|
|
1209
1219
|
try {
|
|
1210
|
-
|
|
1211
|
-
} catch (
|
|
1212
|
-
if (
|
|
1213
|
-
const u =
|
|
1220
|
+
we.parse(o.startup);
|
|
1221
|
+
} catch (a) {
|
|
1222
|
+
if (a instanceof v.ZodError) {
|
|
1223
|
+
const u = a.format();
|
|
1214
1224
|
throw new Error(
|
|
1215
1225
|
`Invalid startup configuration:
|
|
1216
1226
|
${JSON.stringify(u, null, 2)}
|
|
@@ -1218,29 +1228,29 @@ ${JSON.stringify(u, null, 2)}
|
|
|
1218
1228
|
Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
1219
1229
|
);
|
|
1220
1230
|
}
|
|
1221
|
-
throw
|
|
1231
|
+
throw a;
|
|
1222
1232
|
}
|
|
1223
1233
|
const e = o.startup?.mode ?? "DYNAMIC";
|
|
1224
1234
|
let t;
|
|
1225
|
-
if (o.sessionContext && (K(o.sessionContext), t = new
|
|
1235
|
+
if (o.sessionContext && (K(o.sessionContext), t = new fe(o.sessionContext), e === "STATIC" && o.sessionContext.enabled !== !1 && console.warn(
|
|
1226
1236
|
"sessionContext has limited effect in STATIC mode: all clients share the same server instance with base context. Use DYNAMIC mode for per-session context isolation."
|
|
1227
1237
|
)), typeof o.createServer != "function")
|
|
1228
1238
|
throw new Error("createMcpServer: `createServer` (factory) is required");
|
|
1229
|
-
const s = o.createServer(), r = (
|
|
1239
|
+
const s = o.createServer(), r = (a) => typeof a?.server?.notification == "function", i = (a) => typeof a?.notifyToolsListChanged == "function", n = async (a) => {
|
|
1230
1240
|
try {
|
|
1231
|
-
if (r(
|
|
1232
|
-
await
|
|
1241
|
+
if (r(a)) {
|
|
1242
|
+
await a.server.notification({
|
|
1233
1243
|
method: "notifications/tools/list_changed"
|
|
1234
1244
|
});
|
|
1235
1245
|
return;
|
|
1236
1246
|
}
|
|
1237
|
-
i(
|
|
1247
|
+
i(a) && await a.notifyToolsListChanged();
|
|
1238
1248
|
} catch (u) {
|
|
1239
1249
|
if ((u instanceof Error ? u.message : String(u)) === "Not connected")
|
|
1240
1250
|
return;
|
|
1241
1251
|
console.warn("Failed to send tools list changed notification:", u);
|
|
1242
1252
|
}
|
|
1243
|
-
},
|
|
1253
|
+
}, l = new C({
|
|
1244
1254
|
server: s,
|
|
1245
1255
|
catalog: o.catalog,
|
|
1246
1256
|
moduleLoaders: o.moduleLoaders,
|
|
@@ -1250,14 +1260,14 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
1250
1260
|
startup: o.startup,
|
|
1251
1261
|
registerMetaTools: o.registerMetaTools !== void 0 ? o.registerMetaTools : e === "DYNAMIC"
|
|
1252
1262
|
});
|
|
1253
|
-
e === "STATIC" && await
|
|
1254
|
-
const c = new
|
|
1255
|
-
|
|
1256
|
-
(
|
|
1257
|
-
const u =
|
|
1263
|
+
e === "STATIC" && await l.ensureReady();
|
|
1264
|
+
const c = new he(
|
|
1265
|
+
l.getManager(),
|
|
1266
|
+
(a) => {
|
|
1267
|
+
const u = a ?? o.context;
|
|
1258
1268
|
if (e === "STATIC")
|
|
1259
|
-
return { server: s, orchestrator:
|
|
1260
|
-
const h = o.createServer(), g = new
|
|
1269
|
+
return { server: s, orchestrator: l };
|
|
1270
|
+
const h = o.createServer(), g = new C({
|
|
1261
1271
|
server: h,
|
|
1262
1272
|
catalog: o.catalog,
|
|
1263
1273
|
moduleLoaders: o.moduleLoaders,
|
|
@@ -1284,16 +1294,16 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
1284
1294
|
}
|
|
1285
1295
|
};
|
|
1286
1296
|
}
|
|
1287
|
-
function
|
|
1288
|
-
|
|
1297
|
+
function Te(o) {
|
|
1298
|
+
be(o), Se(o), xe(o), Ae(o);
|
|
1289
1299
|
}
|
|
1290
|
-
function
|
|
1300
|
+
function be(o) {
|
|
1291
1301
|
if (!o || typeof o != "object")
|
|
1292
1302
|
throw new Error(
|
|
1293
1303
|
"Permission configuration is required for createPermissionBasedMcpServer"
|
|
1294
1304
|
);
|
|
1295
1305
|
}
|
|
1296
|
-
function
|
|
1306
|
+
function Se(o) {
|
|
1297
1307
|
if (!o.source)
|
|
1298
1308
|
throw new Error('Permission source must be either "headers" or "config"');
|
|
1299
1309
|
if (o.source !== "headers" && o.source !== "config")
|
|
@@ -1301,19 +1311,19 @@ function Te(o) {
|
|
|
1301
1311
|
`Invalid permission source: "${o.source}". Must be either "headers" or "config"`
|
|
1302
1312
|
);
|
|
1303
1313
|
}
|
|
1304
|
-
function
|
|
1314
|
+
function xe(o) {
|
|
1305
1315
|
if (o.source === "config" && !o.staticMap && !o.resolver)
|
|
1306
1316
|
throw new Error(
|
|
1307
1317
|
"Config-based permissions require at least one of: staticMap or resolver function"
|
|
1308
1318
|
);
|
|
1309
1319
|
}
|
|
1310
|
-
function
|
|
1320
|
+
function Ae(o) {
|
|
1311
1321
|
if (o.staticMap !== void 0) {
|
|
1312
1322
|
if (typeof o.staticMap != "object" || o.staticMap === null)
|
|
1313
1323
|
throw new Error(
|
|
1314
1324
|
"staticMap must be an object mapping client IDs to toolset arrays"
|
|
1315
1325
|
);
|
|
1316
|
-
|
|
1326
|
+
Ce(o.staticMap);
|
|
1317
1327
|
}
|
|
1318
1328
|
if (o.resolver !== void 0 && typeof o.resolver != "function")
|
|
1319
1329
|
throw new Error(
|
|
@@ -1324,21 +1334,21 @@ function xe(o) {
|
|
|
1324
1334
|
if (o.headerName !== void 0 && (typeof o.headerName != "string" || o.headerName.length === 0))
|
|
1325
1335
|
throw new Error("headerName must be a non-empty string");
|
|
1326
1336
|
}
|
|
1327
|
-
function
|
|
1337
|
+
function Ce(o) {
|
|
1328
1338
|
for (const [e, t] of Object.entries(o))
|
|
1329
1339
|
if (!Array.isArray(t))
|
|
1330
1340
|
throw new Error(
|
|
1331
1341
|
`staticMap value for client "${e}" must be an array of toolset names`
|
|
1332
1342
|
);
|
|
1333
1343
|
}
|
|
1334
|
-
var p, H,
|
|
1335
|
-
class
|
|
1344
|
+
var p, H, B, Y, U, W;
|
|
1345
|
+
class Ee {
|
|
1336
1346
|
/**
|
|
1337
1347
|
* Creates a new PermissionResolver instance.
|
|
1338
1348
|
* @param config - The permission configuration defining how permissions are resolved
|
|
1339
1349
|
*/
|
|
1340
1350
|
constructor(e) {
|
|
1341
|
-
|
|
1351
|
+
A(this, p);
|
|
1342
1352
|
d(this, "cache", /* @__PURE__ */ new Map());
|
|
1343
1353
|
d(this, "normalizedHeaderName");
|
|
1344
1354
|
this.config = e, this.normalizedHeaderName = (e.headerName || "mcp-toolset-permissions").toLowerCase();
|
|
@@ -1361,7 +1371,7 @@ class Ae {
|
|
|
1361
1371
|
return this.cache.get(e);
|
|
1362
1372
|
let s;
|
|
1363
1373
|
try {
|
|
1364
|
-
this.config.source === "headers" ? s = m(this, p, H).call(this, t) : s = m(this, p,
|
|
1374
|
+
this.config.source === "headers" ? s = m(this, p, H).call(this, t) : s = m(this, p, Y).call(this, e), Array.isArray(s) || (console.warn(
|
|
1365
1375
|
`Permission resolution returned non-array for client ${e}, using empty permissions`
|
|
1366
1376
|
), s = []), s = s.filter(
|
|
1367
1377
|
(r) => typeof r == "string" && r.trim().length > 0
|
|
@@ -1402,7 +1412,7 @@ p = new WeakSet(), /**
|
|
|
1402
1412
|
H = function(e) {
|
|
1403
1413
|
if (!e)
|
|
1404
1414
|
return [];
|
|
1405
|
-
const t = m(this, p,
|
|
1415
|
+
const t = m(this, p, B).call(this, e, this.normalizedHeaderName);
|
|
1406
1416
|
if (!t)
|
|
1407
1417
|
return [];
|
|
1408
1418
|
try {
|
|
@@ -1421,7 +1431,7 @@ H = function(e) {
|
|
|
1421
1431
|
* @returns The header value if found, undefined otherwise
|
|
1422
1432
|
* @private
|
|
1423
1433
|
*/
|
|
1424
|
-
|
|
1434
|
+
B = function(e, t) {
|
|
1425
1435
|
if (e[t] !== void 0)
|
|
1426
1436
|
return e[t];
|
|
1427
1437
|
for (const [s, r] of Object.entries(e))
|
|
@@ -1435,14 +1445,14 @@ F = function(e, t) {
|
|
|
1435
1445
|
* @returns Array of toolset names from configuration
|
|
1436
1446
|
* @private
|
|
1437
1447
|
*/
|
|
1438
|
-
|
|
1448
|
+
Y = function(e) {
|
|
1439
1449
|
if (this.config.resolver) {
|
|
1440
|
-
const t = m(this, p,
|
|
1450
|
+
const t = m(this, p, U).call(this, e);
|
|
1441
1451
|
if (t !== null)
|
|
1442
1452
|
return t;
|
|
1443
1453
|
}
|
|
1444
1454
|
if (this.config.staticMap) {
|
|
1445
|
-
const t = m(this, p,
|
|
1455
|
+
const t = m(this, p, W).call(this, e);
|
|
1446
1456
|
if (t !== null)
|
|
1447
1457
|
return t;
|
|
1448
1458
|
}
|
|
@@ -1454,7 +1464,7 @@ _ = function(e) {
|
|
|
1454
1464
|
* @returns Array of toolset names if successful, null if resolver fails or returns invalid data
|
|
1455
1465
|
* @private
|
|
1456
1466
|
*/
|
|
1457
|
-
|
|
1467
|
+
U = function(e) {
|
|
1458
1468
|
try {
|
|
1459
1469
|
const t = this.config.resolver(e);
|
|
1460
1470
|
return Array.isArray(t) ? t : (console.warn(
|
|
@@ -1473,23 +1483,23 @@ B = function(e) {
|
|
|
1473
1483
|
* @returns Array of toolset names if found, null if client not in map
|
|
1474
1484
|
* @private
|
|
1475
1485
|
*/
|
|
1476
|
-
|
|
1486
|
+
W = function(e) {
|
|
1477
1487
|
const t = this.config.staticMap[e];
|
|
1478
1488
|
return t !== void 0 ? Array.isArray(t) ? t : [] : null;
|
|
1479
1489
|
};
|
|
1480
|
-
function
|
|
1490
|
+
function Pe(o, e) {
|
|
1481
1491
|
return async (t) => {
|
|
1482
1492
|
const s = e.resolvePermissions(
|
|
1483
1493
|
t.clientId,
|
|
1484
1494
|
t.headers
|
|
1485
|
-
), r = o(s), i = r.orchestrator.getManager(), n = [],
|
|
1495
|
+
), r = o(s), i = r.orchestrator.getManager(), n = [], l = [];
|
|
1486
1496
|
if (s.length > 0) {
|
|
1487
1497
|
const c = await i.enableToolsets(s);
|
|
1488
|
-
for (const
|
|
1489
|
-
|
|
1490
|
-
`Failed to enable toolset '${
|
|
1498
|
+
for (const a of c.results)
|
|
1499
|
+
a.success ? n.push(a.name) : (l.push(a.name), console.warn(
|
|
1500
|
+
`Failed to enable toolset '${a.name}' for client '${t.clientId}': ${a.message}`
|
|
1491
1501
|
));
|
|
1492
|
-
if (n.length === 0 &&
|
|
1502
|
+
if (n.length === 0 && l.length > 0)
|
|
1493
1503
|
throw new Error(
|
|
1494
1504
|
`All requested toolsets failed to enable for client '${t.clientId}'. Requested: [${s.join(", ")}]. Check that toolset names in permissions match the catalog.`
|
|
1495
1505
|
);
|
|
@@ -1498,12 +1508,12 @@ function Ce(o, e) {
|
|
|
1498
1508
|
server: r.server,
|
|
1499
1509
|
orchestrator: r.orchestrator,
|
|
1500
1510
|
allowedToolsets: n,
|
|
1501
|
-
failedToolsets:
|
|
1511
|
+
failedToolsets: l
|
|
1502
1512
|
};
|
|
1503
1513
|
};
|
|
1504
1514
|
}
|
|
1505
|
-
var f,
|
|
1506
|
-
class
|
|
1515
|
+
var f, J, Q, G, Z, X, ee, te, se, M, oe;
|
|
1516
|
+
class Me {
|
|
1507
1517
|
/**
|
|
1508
1518
|
* Creates a new PermissionAwareFastifyTransport instance.
|
|
1509
1519
|
* @param defaultManager - Default tool manager for status endpoints
|
|
@@ -1512,16 +1522,16 @@ class Ee {
|
|
|
1512
1522
|
* @param configSchema - Optional JSON schema for configuration discovery
|
|
1513
1523
|
*/
|
|
1514
1524
|
constructor(e, t, s = {}, r) {
|
|
1515
|
-
|
|
1525
|
+
A(this, f);
|
|
1516
1526
|
d(this, "options");
|
|
1517
1527
|
d(this, "defaultManager");
|
|
1518
1528
|
d(this, "createPermissionAwareBundle");
|
|
1519
1529
|
d(this, "app", null);
|
|
1520
1530
|
d(this, "configSchema");
|
|
1521
1531
|
// Per-client server bundles and per-client session transports
|
|
1522
|
-
d(this, "clientCache", new
|
|
1532
|
+
d(this, "clientCache", new F({
|
|
1523
1533
|
onEvict: (e, t) => {
|
|
1524
|
-
m(this, f,
|
|
1534
|
+
m(this, f, J).call(this, t);
|
|
1525
1535
|
}
|
|
1526
1536
|
}));
|
|
1527
1537
|
this.defaultManager = e, this.createPermissionAwareBundle = t, this.options = {
|
|
@@ -1541,11 +1551,11 @@ class Ee {
|
|
|
1541
1551
|
async start() {
|
|
1542
1552
|
if (this.app) return;
|
|
1543
1553
|
const e = this.options.app ?? N({ logger: this.options.logger });
|
|
1544
|
-
this.options.cors && await e.register(
|
|
1545
|
-
const t = m(this, f,
|
|
1546
|
-
m(this, f,
|
|
1554
|
+
this.options.cors && await e.register(D, { origin: !0 });
|
|
1555
|
+
const t = m(this, f, Q).call(this, this.options.basePath);
|
|
1556
|
+
m(this, f, G).call(this, e, t), m(this, f, Z).call(this, e, t), m(this, f, X).call(this, e, t), m(this, f, ee).call(this, e, t), m(this, f, te).call(this, e, t), m(this, f, se).call(this, e, t), this.options.customEndpoints && this.options.customEndpoints.length > 0 && V(e, t, this.options.customEndpoints, {
|
|
1547
1557
|
contextExtractor: async (s) => {
|
|
1548
|
-
const r = m(this, f,
|
|
1558
|
+
const r = m(this, f, M).call(this, s);
|
|
1549
1559
|
try {
|
|
1550
1560
|
const i = await this.createPermissionAwareBundle(r);
|
|
1551
1561
|
return {
|
|
@@ -1577,7 +1587,7 @@ f = new WeakSet(), /**
|
|
|
1577
1587
|
* @param bundle - The client bundle to clean up
|
|
1578
1588
|
* @private
|
|
1579
1589
|
*/
|
|
1580
|
-
|
|
1590
|
+
J = function(e) {
|
|
1581
1591
|
for (const [t, s] of e.sessions.entries())
|
|
1582
1592
|
try {
|
|
1583
1593
|
typeof s.close == "function" && s.close().catch((r) => {
|
|
@@ -1593,7 +1603,7 @@ U = function(e) {
|
|
|
1593
1603
|
* @returns Normalized base path without trailing slash
|
|
1594
1604
|
* @private
|
|
1595
1605
|
*/
|
|
1596
|
-
|
|
1606
|
+
Q = function(e) {
|
|
1597
1607
|
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
1598
1608
|
}, /**
|
|
1599
1609
|
* Registers the health check endpoint.
|
|
@@ -1601,7 +1611,7 @@ W = function(e) {
|
|
|
1601
1611
|
* @param base - Base path for routes
|
|
1602
1612
|
* @private
|
|
1603
1613
|
*/
|
|
1604
|
-
|
|
1614
|
+
G = function(e, t) {
|
|
1605
1615
|
e.get(`${t}/healthz`, async () => ({ ok: !0 }));
|
|
1606
1616
|
}, /**
|
|
1607
1617
|
* Registers the tools status endpoint.
|
|
@@ -1609,7 +1619,7 @@ J = function(e, t) {
|
|
|
1609
1619
|
* @param base - Base path for routes
|
|
1610
1620
|
* @private
|
|
1611
1621
|
*/
|
|
1612
|
-
|
|
1622
|
+
Z = function(e, t) {
|
|
1613
1623
|
e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
|
|
1614
1624
|
}, /**
|
|
1615
1625
|
* Registers the MCP configuration discovery endpoint.
|
|
@@ -1617,7 +1627,7 @@ Q = function(e, t) {
|
|
|
1617
1627
|
* @param base - Base path for routes
|
|
1618
1628
|
* @private
|
|
1619
1629
|
*/
|
|
1620
|
-
|
|
1630
|
+
X = function(e, t) {
|
|
1621
1631
|
e.get(`${t}/.well-known/mcp-config`, async (s, r) => (r.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
1622
1632
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1623
1633
|
title: "MCP Session Configuration",
|
|
@@ -1635,46 +1645,46 @@ G = function(e, t) {
|
|
|
1635
1645
|
* @param base - Base path for routes
|
|
1636
1646
|
* @private
|
|
1637
1647
|
*/
|
|
1638
|
-
|
|
1648
|
+
ee = function(e, t) {
|
|
1639
1649
|
e.post(
|
|
1640
1650
|
`${t}/mcp`,
|
|
1641
1651
|
async (s, r) => {
|
|
1642
|
-
const i = m(this, f,
|
|
1643
|
-
let
|
|
1644
|
-
if (!
|
|
1652
|
+
const i = m(this, f, M).call(this, s), n = !i.clientId.startsWith("anon-");
|
|
1653
|
+
let l = n ? this.clientCache.get(i.clientId) : null;
|
|
1654
|
+
if (!l)
|
|
1645
1655
|
try {
|
|
1646
1656
|
const u = await this.createPermissionAwareBundle(i);
|
|
1647
1657
|
u.failedToolsets.length > 0 && console.warn(
|
|
1648
1658
|
`Client ${i.clientId} had ${u.failedToolsets.length} toolsets fail to enable: [${u.failedToolsets.join(", ")}]. Successfully enabled: [${u.allowedToolsets.join(", ")}]`
|
|
1649
1659
|
);
|
|
1650
1660
|
const h = u.sessions;
|
|
1651
|
-
|
|
1661
|
+
l = {
|
|
1652
1662
|
server: u.server,
|
|
1653
1663
|
orchestrator: u.orchestrator,
|
|
1654
1664
|
allowedToolsets: u.allowedToolsets,
|
|
1655
1665
|
failedToolsets: u.failedToolsets,
|
|
1656
1666
|
sessions: h instanceof Map ? h : /* @__PURE__ */ new Map()
|
|
1657
|
-
}, n && this.clientCache.set(i.clientId,
|
|
1667
|
+
}, n && this.clientCache.set(i.clientId, l);
|
|
1658
1668
|
} catch (u) {
|
|
1659
1669
|
return console.error(
|
|
1660
1670
|
`Failed to create permission-aware bundle for client ${i.clientId}:`,
|
|
1661
1671
|
u
|
|
1662
|
-
), r.code(403), m(this, f,
|
|
1672
|
+
), r.code(403), m(this, f, oe).call(this, "Access denied");
|
|
1663
1673
|
}
|
|
1664
1674
|
const c = s.headers["mcp-session-id"];
|
|
1665
|
-
let
|
|
1666
|
-
if (c &&
|
|
1667
|
-
|
|
1668
|
-
else if (!c &&
|
|
1669
|
-
const u =
|
|
1670
|
-
|
|
1675
|
+
let a;
|
|
1676
|
+
if (c && l.sessions.get(c))
|
|
1677
|
+
a = l.sessions.get(c);
|
|
1678
|
+
else if (!c && _(s.body)) {
|
|
1679
|
+
const u = S();
|
|
1680
|
+
a = new z({
|
|
1671
1681
|
sessionIdGenerator: () => u,
|
|
1672
1682
|
onsessioninitialized: (h) => {
|
|
1673
|
-
|
|
1683
|
+
l.sessions.set(h, a);
|
|
1674
1684
|
}
|
|
1675
1685
|
});
|
|
1676
1686
|
try {
|
|
1677
|
-
await
|
|
1687
|
+
await l.server.connect(a);
|
|
1678
1688
|
} catch {
|
|
1679
1689
|
return r.code(500), {
|
|
1680
1690
|
jsonrpc: "2.0",
|
|
@@ -1682,8 +1692,8 @@ Z = function(e, t) {
|
|
|
1682
1692
|
id: null
|
|
1683
1693
|
};
|
|
1684
1694
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1695
|
+
a.onclose = () => {
|
|
1696
|
+
a?.sessionId && l.sessions.delete(a.sessionId);
|
|
1687
1697
|
};
|
|
1688
1698
|
} else
|
|
1689
1699
|
return r.code(400), {
|
|
@@ -1691,7 +1701,7 @@ Z = function(e, t) {
|
|
|
1691
1701
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
1692
1702
|
id: null
|
|
1693
1703
|
};
|
|
1694
|
-
return await
|
|
1704
|
+
return await a.handleRequest(
|
|
1695
1705
|
s.raw,
|
|
1696
1706
|
r.raw,
|
|
1697
1707
|
s.body
|
|
@@ -1704,19 +1714,19 @@ Z = function(e, t) {
|
|
|
1704
1714
|
* @param base - Base path for routes
|
|
1705
1715
|
* @private
|
|
1706
1716
|
*/
|
|
1707
|
-
|
|
1717
|
+
te = function(e, t) {
|
|
1708
1718
|
e.get(`${t}/mcp`, async (s, r) => {
|
|
1709
1719
|
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
|
|
1710
1720
|
if (!n)
|
|
1711
1721
|
return r.code(400), "Missing mcp-client-id";
|
|
1712
|
-
const
|
|
1713
|
-
if (!
|
|
1722
|
+
const l = this.clientCache.get(n);
|
|
1723
|
+
if (!l)
|
|
1714
1724
|
return r.code(400), "Invalid or expired client";
|
|
1715
1725
|
const c = s.headers["mcp-session-id"];
|
|
1716
1726
|
if (!c)
|
|
1717
1727
|
return r.code(400), "Missing mcp-session-id";
|
|
1718
|
-
const
|
|
1719
|
-
return
|
|
1728
|
+
const a = l.sessions.get(c);
|
|
1729
|
+
return a ? (await a.handleRequest(s.raw, r.raw), r) : (r.code(400), "Invalid or expired session ID");
|
|
1720
1730
|
});
|
|
1721
1731
|
}, /**
|
|
1722
1732
|
* Registers the DELETE /mcp endpoint for session termination.
|
|
@@ -1724,12 +1734,12 @@ X = function(e, t) {
|
|
|
1724
1734
|
* @param base - Base path for routes
|
|
1725
1735
|
* @private
|
|
1726
1736
|
*/
|
|
1727
|
-
|
|
1737
|
+
se = function(e, t) {
|
|
1728
1738
|
e.delete(
|
|
1729
1739
|
`${t}/mcp`,
|
|
1730
1740
|
async (s, r) => {
|
|
1731
|
-
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "",
|
|
1732
|
-
if (!n || !
|
|
1741
|
+
const i = s.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "", l = s.headers["mcp-session-id"];
|
|
1742
|
+
if (!n || !l)
|
|
1733
1743
|
return r.code(400), {
|
|
1734
1744
|
jsonrpc: "2.0",
|
|
1735
1745
|
error: {
|
|
@@ -1738,21 +1748,21 @@ ee = function(e, t) {
|
|
|
1738
1748
|
},
|
|
1739
1749
|
id: null
|
|
1740
1750
|
};
|
|
1741
|
-
const c = this.clientCache.get(n),
|
|
1742
|
-
if (!c || !
|
|
1751
|
+
const c = this.clientCache.get(n), a = c?.sessions.get(l);
|
|
1752
|
+
if (!c || !a)
|
|
1743
1753
|
return r.code(404), {
|
|
1744
1754
|
jsonrpc: "2.0",
|
|
1745
1755
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
1746
1756
|
id: null
|
|
1747
1757
|
};
|
|
1748
1758
|
try {
|
|
1749
|
-
if (typeof
|
|
1759
|
+
if (typeof a.close == "function")
|
|
1750
1760
|
try {
|
|
1751
|
-
await
|
|
1761
|
+
await a.close();
|
|
1752
1762
|
} catch {
|
|
1753
1763
|
}
|
|
1754
1764
|
} finally {
|
|
1755
|
-
|
|
1765
|
+
a?.sessionId ? c.sessions.delete(a.sessionId) : c.sessions.delete(l);
|
|
1756
1766
|
}
|
|
1757
1767
|
return r.code(204).send(), r;
|
|
1758
1768
|
}
|
|
@@ -1764,8 +1774,8 @@ ee = function(e, t) {
|
|
|
1764
1774
|
* @returns Client request context with ID and headers
|
|
1765
1775
|
* @private
|
|
1766
1776
|
*/
|
|
1767
|
-
|
|
1768
|
-
const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${
|
|
1777
|
+
M = function(e) {
|
|
1778
|
+
const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${S()}`, r = {};
|
|
1769
1779
|
for (const [i, n] of Object.entries(e.headers))
|
|
1770
1780
|
typeof n == "string" && (r[i] = n);
|
|
1771
1781
|
return { clientId: s, headers: r };
|
|
@@ -1777,7 +1787,7 @@ P = function(e) {
|
|
|
1777
1787
|
* @returns JSON-RPC error response object
|
|
1778
1788
|
* @private
|
|
1779
1789
|
*/
|
|
1780
|
-
|
|
1790
|
+
oe = function(e = "Access denied", t = -32e3) {
|
|
1781
1791
|
return {
|
|
1782
1792
|
jsonrpc: "2.0",
|
|
1783
1793
|
error: {
|
|
@@ -1787,7 +1797,7 @@ te = function(e = "Access denied", t = -32e3) {
|
|
|
1787
1797
|
id: null
|
|
1788
1798
|
};
|
|
1789
1799
|
};
|
|
1790
|
-
function
|
|
1800
|
+
function Ie(o) {
|
|
1791
1801
|
if (!o) return;
|
|
1792
1802
|
const e = {
|
|
1793
1803
|
namespaceToolsWithSetKey: o.namespaceToolsWithSetKey
|
|
@@ -1802,12 +1812,12 @@ function Pe(o) {
|
|
|
1802
1812
|
"Permission-based servers: exposurePolicy.onLimitExceeded is ignored. No toolset limits are enforced."
|
|
1803
1813
|
), e;
|
|
1804
1814
|
}
|
|
1805
|
-
async function
|
|
1815
|
+
async function ze(o) {
|
|
1806
1816
|
if (!o.permissions)
|
|
1807
1817
|
throw new Error(
|
|
1808
1818
|
"Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
|
|
1809
1819
|
);
|
|
1810
|
-
if (
|
|
1820
|
+
if (Te(o.permissions), o.sessionContext && (K(o.sessionContext), console.warn(
|
|
1811
1821
|
"Session context support for permission-based servers is limited. The base context will be used for module loaders."
|
|
1812
1822
|
)), o.startup)
|
|
1813
1823
|
throw new Error(
|
|
@@ -1817,9 +1827,9 @@ async function Oe(o) {
|
|
|
1817
1827
|
throw new Error(
|
|
1818
1828
|
"createPermissionBasedMcpServer: `createServer` (factory) is required"
|
|
1819
1829
|
);
|
|
1820
|
-
const e =
|
|
1830
|
+
const e = Ie(
|
|
1821
1831
|
o.exposurePolicy
|
|
1822
|
-
), t = new
|
|
1832
|
+
), t = new Ee(o.permissions), s = o.createServer(), r = new C({
|
|
1823
1833
|
server: s,
|
|
1824
1834
|
catalog: o.catalog,
|
|
1825
1835
|
moduleLoaders: o.moduleLoaders,
|
|
@@ -1829,9 +1839,9 @@ async function Oe(o) {
|
|
|
1829
1839
|
// No notifications in STATIC mode
|
|
1830
1840
|
startup: { mode: "STATIC", toolsets: [] },
|
|
1831
1841
|
registerMetaTools: !1
|
|
1832
|
-
}), i =
|
|
1833
|
-
(
|
|
1834
|
-
const c = o.createServer(),
|
|
1842
|
+
}), i = Pe(
|
|
1843
|
+
(l) => {
|
|
1844
|
+
const c = o.createServer(), a = new C({
|
|
1835
1845
|
server: c,
|
|
1836
1846
|
catalog: o.catalog,
|
|
1837
1847
|
moduleLoaders: o.moduleLoaders,
|
|
@@ -1844,10 +1854,10 @@ async function Oe(o) {
|
|
|
1844
1854
|
registerMetaTools: !1
|
|
1845
1855
|
// No meta-tools - toolsets are fixed per client
|
|
1846
1856
|
});
|
|
1847
|
-
return { server: c, orchestrator:
|
|
1857
|
+
return { server: c, orchestrator: a };
|
|
1848
1858
|
},
|
|
1849
1859
|
t
|
|
1850
|
-
), n = new
|
|
1860
|
+
), n = new Me(
|
|
1851
1861
|
r.getManager(),
|
|
1852
1862
|
i,
|
|
1853
1863
|
o.http,
|
|
@@ -1867,17 +1877,17 @@ async function Oe(o) {
|
|
|
1867
1877
|
}
|
|
1868
1878
|
};
|
|
1869
1879
|
}
|
|
1870
|
-
function
|
|
1880
|
+
function _e(o) {
|
|
1871
1881
|
return o;
|
|
1872
1882
|
}
|
|
1873
|
-
function
|
|
1883
|
+
function qe(o) {
|
|
1874
1884
|
return o;
|
|
1875
1885
|
}
|
|
1876
1886
|
export {
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1887
|
+
fe as SessionContextResolver,
|
|
1888
|
+
De as createMcpServer,
|
|
1889
|
+
ze as createPermissionBasedMcpServer,
|
|
1890
|
+
_e as defineEndpoint,
|
|
1891
|
+
qe as definePermissionAwareEndpoint
|
|
1882
1892
|
};
|
|
1883
1893
|
//# sourceMappingURL=index.js.map
|