toolception 0.5.2 → 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 +448 -397
- package/dist/index.js.map +1 -1
- package/dist/meta/registerMetaTools.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
var
|
|
1
|
+
var te = Object.defineProperty;
|
|
2
|
+
var L = (r) => {
|
|
2
3
|
throw TypeError(r);
|
|
3
4
|
};
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import j from "
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
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
|
+
import { z as y } from "zod";
|
|
10
|
+
import j from "fastify";
|
|
11
|
+
import k from "@fastify/cors";
|
|
12
|
+
import { randomUUID as T } from "node:crypto";
|
|
13
|
+
import { StreamableHTTPServerTransport as D } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
14
|
+
import { isInitializeRequest as O } from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
+
const R = {
|
|
14
16
|
dynamic: [
|
|
15
17
|
"dynamic-tool-discovery",
|
|
16
18
|
"dynamicToolDiscovery",
|
|
@@ -18,20 +20,21 @@ const L = {
|
|
|
18
20
|
],
|
|
19
21
|
toolsets: ["tool-sets", "toolSets", "FMP_TOOL_SETS"]
|
|
20
22
|
};
|
|
21
|
-
class
|
|
23
|
+
class re {
|
|
22
24
|
constructor(e = {}) {
|
|
25
|
+
d(this, "keys");
|
|
23
26
|
this.keys = {
|
|
24
|
-
dynamic: e.keys?.dynamic ??
|
|
25
|
-
toolsets: e.keys?.toolsets ??
|
|
27
|
+
dynamic: e.keys?.dynamic ?? R.dynamic,
|
|
28
|
+
toolsets: e.keys?.toolsets ?? R.toolsets
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
|
-
resolveMode(e,
|
|
29
|
-
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;
|
|
30
33
|
}
|
|
31
|
-
parseCommaSeparatedToolSets(e,
|
|
34
|
+
parseCommaSeparatedToolSets(e, t) {
|
|
32
35
|
if (!e || typeof e != "string") return [];
|
|
33
|
-
const
|
|
34
|
-
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)
|
|
35
38
|
o.has(n) ? i.push(n) : console.warn(
|
|
36
39
|
`Invalid toolset '${n}' ignored. Available: ${Array.from(
|
|
37
40
|
o
|
|
@@ -39,32 +42,32 @@ class se {
|
|
|
39
42
|
);
|
|
40
43
|
return i;
|
|
41
44
|
}
|
|
42
|
-
getModulesForToolSets(e,
|
|
43
|
-
const
|
|
45
|
+
getModulesForToolSets(e, t) {
|
|
46
|
+
const s = /* @__PURE__ */ new Set();
|
|
44
47
|
for (const o of e) {
|
|
45
|
-
const i =
|
|
46
|
-
i && (i.modules || []).forEach((n) =>
|
|
48
|
+
const i = t[o];
|
|
49
|
+
i && (i.modules || []).forEach((n) => s.add(n));
|
|
47
50
|
}
|
|
48
|
-
return Array.from(
|
|
51
|
+
return Array.from(s);
|
|
49
52
|
}
|
|
50
|
-
validateToolsetName(e,
|
|
53
|
+
validateToolsetName(e, t) {
|
|
51
54
|
if (!e || typeof e != "string")
|
|
52
55
|
return {
|
|
53
56
|
isValid: !1,
|
|
54
57
|
error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${Object.keys(
|
|
55
|
-
|
|
58
|
+
t
|
|
56
59
|
).join(", ")}`
|
|
57
60
|
};
|
|
58
|
-
const
|
|
59
|
-
return
|
|
61
|
+
const s = e.trim();
|
|
62
|
+
return s.length === 0 ? {
|
|
60
63
|
isValid: !1,
|
|
61
64
|
error: `Empty toolset name provided. Available toolsets: ${Object.keys(
|
|
62
|
-
|
|
65
|
+
t
|
|
63
66
|
).join(", ")}`
|
|
64
|
-
} : s
|
|
67
|
+
} : t[s] ? { isValid: !0, sanitized: s } : {
|
|
65
68
|
isValid: !1,
|
|
66
|
-
error: `Toolset '${
|
|
67
|
-
|
|
69
|
+
error: `Toolset '${s}' not found. Available toolsets: ${Object.keys(
|
|
70
|
+
t
|
|
68
71
|
).join(", ")}`
|
|
69
72
|
};
|
|
70
73
|
}
|
|
@@ -75,42 +78,44 @@ class se {
|
|
|
75
78
|
* @param catalog - The toolset catalog to validate against
|
|
76
79
|
* @returns Validation result with modules array if valid
|
|
77
80
|
*/
|
|
78
|
-
validateToolsetModules(e,
|
|
81
|
+
validateToolsetModules(e, t) {
|
|
79
82
|
try {
|
|
80
83
|
for (const o of e)
|
|
81
|
-
if (!
|
|
84
|
+
if (!t[o])
|
|
82
85
|
return {
|
|
83
86
|
isValid: !1,
|
|
84
87
|
error: `Toolset '${o}' not found in catalog`
|
|
85
88
|
};
|
|
86
|
-
return { isValid: !0, modules: this.getModulesForToolSets(e,
|
|
87
|
-
} catch (
|
|
89
|
+
return { isValid: !0, modules: this.getModulesForToolSets(e, t) };
|
|
90
|
+
} catch (s) {
|
|
88
91
|
return {
|
|
89
92
|
isValid: !1,
|
|
90
|
-
error: `Error resolving modules for ${e.join(", ")}: ${
|
|
93
|
+
error: `Error resolving modules for ${e.join(", ")}: ${s instanceof Error ? s.message : "Unknown error"}`
|
|
91
94
|
};
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
isDynamicEnabled(e) {
|
|
95
98
|
if (!e) return !1;
|
|
96
|
-
for (const
|
|
97
|
-
const
|
|
98
|
-
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")
|
|
99
102
|
return !0;
|
|
100
103
|
}
|
|
101
104
|
return !1;
|
|
102
105
|
}
|
|
103
106
|
getToolsetsString(e) {
|
|
104
107
|
if (e)
|
|
105
|
-
for (const
|
|
106
|
-
const
|
|
107
|
-
if (typeof
|
|
108
|
-
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;
|
|
109
112
|
}
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
|
-
class
|
|
115
|
+
class ie {
|
|
113
116
|
constructor(e) {
|
|
117
|
+
d(this, "catalog");
|
|
118
|
+
d(this, "moduleLoaders");
|
|
114
119
|
this.catalog = e.catalog, this.moduleLoaders = e.moduleLoaders ?? {};
|
|
115
120
|
}
|
|
116
121
|
getAvailableToolsets() {
|
|
@@ -127,30 +132,30 @@ class te {
|
|
|
127
132
|
", "
|
|
128
133
|
)}`
|
|
129
134
|
};
|
|
130
|
-
const
|
|
131
|
-
return
|
|
135
|
+
const t = e.trim();
|
|
136
|
+
return t.length === 0 ? {
|
|
132
137
|
isValid: !1,
|
|
133
138
|
error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(
|
|
134
139
|
", "
|
|
135
140
|
)}`
|
|
136
|
-
} : this.catalog[
|
|
141
|
+
} : this.catalog[t] ? { isValid: !0, sanitized: t } : {
|
|
137
142
|
isValid: !1,
|
|
138
|
-
error: `Toolset '${
|
|
143
|
+
error: `Toolset '${t}' not found. Available toolsets: ${this.getAvailableToolsets().join(
|
|
139
144
|
", "
|
|
140
145
|
)}`
|
|
141
146
|
};
|
|
142
147
|
}
|
|
143
|
-
async resolveToolsForToolsets(e,
|
|
144
|
-
const
|
|
148
|
+
async resolveToolsForToolsets(e, t) {
|
|
149
|
+
const s = [];
|
|
145
150
|
for (const o of e) {
|
|
146
151
|
const i = this.catalog[o];
|
|
147
|
-
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))
|
|
148
153
|
for (const n of i.modules) {
|
|
149
154
|
const l = this.moduleLoaders[n];
|
|
150
155
|
if (l)
|
|
151
156
|
try {
|
|
152
|
-
const a = await l(
|
|
153
|
-
Array.isArray(a) && a.length > 0 &&
|
|
157
|
+
const a = await l(t);
|
|
158
|
+
Array.isArray(a) && a.length > 0 && s.push(...a);
|
|
154
159
|
} catch (a) {
|
|
155
160
|
console.warn(
|
|
156
161
|
`Module loader '${n}' failed for toolset '${o}':`,
|
|
@@ -159,48 +164,54 @@ class te {
|
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
166
|
}
|
|
162
|
-
return
|
|
167
|
+
return s;
|
|
163
168
|
}
|
|
164
169
|
}
|
|
165
|
-
class
|
|
166
|
-
constructor(
|
|
167
|
-
super(
|
|
170
|
+
class N extends Error {
|
|
171
|
+
constructor(t, s, o, i) {
|
|
172
|
+
super(t);
|
|
173
|
+
d(this, "code");
|
|
174
|
+
d(this, "details");
|
|
175
|
+
this.name = "ToolingError", this.code = s, this.details = o;
|
|
168
176
|
}
|
|
169
177
|
}
|
|
170
|
-
class
|
|
178
|
+
class z {
|
|
171
179
|
constructor(e = {}) {
|
|
172
|
-
|
|
180
|
+
d(this, "options");
|
|
181
|
+
d(this, "names", /* @__PURE__ */ new Set());
|
|
182
|
+
d(this, "toolsetToNames", /* @__PURE__ */ new Map());
|
|
183
|
+
this.options = {
|
|
173
184
|
namespaceWithToolset: e.namespaceWithToolset ?? !0
|
|
174
185
|
};
|
|
175
186
|
}
|
|
176
|
-
getSafeName(e,
|
|
177
|
-
return !this.options.namespaceWithToolset ||
|
|
187
|
+
getSafeName(e, t) {
|
|
188
|
+
return !this.options.namespaceWithToolset || t.startsWith(`${e}.`) ? t : `${e}.${t}`;
|
|
178
189
|
}
|
|
179
190
|
has(e) {
|
|
180
191
|
return this.names.has(e);
|
|
181
192
|
}
|
|
182
193
|
add(e) {
|
|
183
194
|
if (this.names.has(e))
|
|
184
|
-
throw new
|
|
195
|
+
throw new N(
|
|
185
196
|
`Tool name collision: '${e}' already registered`,
|
|
186
197
|
"E_TOOL_NAME_CONFLICT"
|
|
187
198
|
);
|
|
188
199
|
this.names.add(e);
|
|
189
200
|
}
|
|
190
|
-
addForToolset(e,
|
|
191
|
-
this.add(
|
|
192
|
-
const
|
|
193
|
-
|
|
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);
|
|
194
205
|
}
|
|
195
|
-
mapAndValidate(e,
|
|
196
|
-
return
|
|
197
|
-
const o = this.getSafeName(e,
|
|
206
|
+
mapAndValidate(e, t) {
|
|
207
|
+
return t.map((s) => {
|
|
208
|
+
const o = this.getSafeName(e, s.name);
|
|
198
209
|
if (this.has(o))
|
|
199
|
-
throw new
|
|
210
|
+
throw new N(
|
|
200
211
|
`Tool name collision for '${o}'`,
|
|
201
212
|
"E_TOOL_NAME_CONFLICT"
|
|
202
213
|
);
|
|
203
|
-
return { ...
|
|
214
|
+
return { ...s, name: o };
|
|
204
215
|
});
|
|
205
216
|
}
|
|
206
217
|
list() {
|
|
@@ -208,14 +219,21 @@ class O {
|
|
|
208
219
|
}
|
|
209
220
|
listByToolset() {
|
|
210
221
|
const e = {};
|
|
211
|
-
for (const [
|
|
212
|
-
e[
|
|
222
|
+
for (const [t, s] of this.toolsetToNames.entries())
|
|
223
|
+
e[t] = Array.from(s);
|
|
213
224
|
return e;
|
|
214
225
|
}
|
|
215
226
|
}
|
|
216
|
-
class
|
|
227
|
+
class ne {
|
|
217
228
|
constructor(e) {
|
|
218
|
-
this
|
|
229
|
+
d(this, "server");
|
|
230
|
+
d(this, "resolver");
|
|
231
|
+
d(this, "context");
|
|
232
|
+
d(this, "onToolsListChanged");
|
|
233
|
+
d(this, "exposurePolicy");
|
|
234
|
+
d(this, "toolRegistry");
|
|
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 z({ namespaceWithToolset: !0 });
|
|
219
237
|
}
|
|
220
238
|
/**
|
|
221
239
|
* Sends a tool list change notification if configured.
|
|
@@ -250,14 +268,14 @@ class oe {
|
|
|
250
268
|
* @param skipNotification - If true, skips the tool list change notification (for batch operations)
|
|
251
269
|
* @returns Result object with success status and message
|
|
252
270
|
*/
|
|
253
|
-
async enableToolset(e,
|
|
254
|
-
const
|
|
255
|
-
if (!
|
|
271
|
+
async enableToolset(e, t = !1) {
|
|
272
|
+
const s = this.resolver.validateToolsetName(e);
|
|
273
|
+
if (!s.isValid || !s.sanitized)
|
|
256
274
|
return {
|
|
257
275
|
success: !1,
|
|
258
|
-
message:
|
|
276
|
+
message: s.error || "Unknown validation error"
|
|
259
277
|
};
|
|
260
|
-
const o =
|
|
278
|
+
const o = s.sanitized;
|
|
261
279
|
if (this.activeToolsets.has(o))
|
|
262
280
|
return {
|
|
263
281
|
success: !1,
|
|
@@ -280,7 +298,7 @@ class oe {
|
|
|
280
298
|
for (const c of a)
|
|
281
299
|
this.registerSingleTool(c, o), n.push(c.name);
|
|
282
300
|
}
|
|
283
|
-
return this.activeToolsets.add(o),
|
|
301
|
+
return this.activeToolsets.add(o), t || await this.notifyToolsChanged(), {
|
|
284
302
|
success: !0,
|
|
285
303
|
message: `Toolset '${o}' enabled successfully. Registered ${l?.length ?? 0} tools.`
|
|
286
304
|
};
|
|
@@ -320,13 +338,13 @@ class oe {
|
|
|
320
338
|
* @param toolsetKey - The toolset key for tracking
|
|
321
339
|
* @private
|
|
322
340
|
*/
|
|
323
|
-
registerSingleTool(e,
|
|
341
|
+
registerSingleTool(e, t) {
|
|
324
342
|
this.server.tool(
|
|
325
343
|
e.name,
|
|
326
344
|
e.description,
|
|
327
345
|
e.inputSchema,
|
|
328
|
-
async (
|
|
329
|
-
), this.toolRegistry.addForToolset(
|
|
346
|
+
async (s) => await e.handler(s)
|
|
347
|
+
), this.toolRegistry.addForToolset(t, e.name);
|
|
330
348
|
}
|
|
331
349
|
/**
|
|
332
350
|
* Disables a toolset by name.
|
|
@@ -335,21 +353,21 @@ class oe {
|
|
|
335
353
|
* @returns Result object with success status and message
|
|
336
354
|
*/
|
|
337
355
|
async disableToolset(e) {
|
|
338
|
-
const
|
|
339
|
-
if (!
|
|
356
|
+
const t = this.resolver.validateToolsetName(e);
|
|
357
|
+
if (!t.isValid || !t.sanitized) {
|
|
340
358
|
const o = Array.from(this.activeToolsets).join(", ") || "none";
|
|
341
359
|
return {
|
|
342
360
|
success: !1,
|
|
343
|
-
message: `${
|
|
361
|
+
message: `${t.error || "Unknown validation error"} Active toolsets: ${o}`
|
|
344
362
|
};
|
|
345
363
|
}
|
|
346
|
-
const
|
|
347
|
-
return this.activeToolsets.has(
|
|
364
|
+
const s = t.sanitized;
|
|
365
|
+
return this.activeToolsets.has(s) ? (this.activeToolsets.delete(s), await this.notifyToolsChanged(), {
|
|
348
366
|
success: !0,
|
|
349
|
-
message: `Toolset '${
|
|
367
|
+
message: `Toolset '${s}' disabled successfully. Individual tools remain registered due to MCP limitations.`
|
|
350
368
|
}) : {
|
|
351
369
|
success: !1,
|
|
352
|
-
message: `Toolset '${
|
|
370
|
+
message: `Toolset '${s}' is not currently active. Active toolsets: ${Array.from(this.activeToolsets).join(", ") || "none"}`
|
|
353
371
|
};
|
|
354
372
|
}
|
|
355
373
|
getStatus() {
|
|
@@ -370,21 +388,21 @@ class oe {
|
|
|
370
388
|
* @returns Result object with overall success status and individual results
|
|
371
389
|
*/
|
|
372
390
|
async enableToolsets(e) {
|
|
373
|
-
const
|
|
391
|
+
const t = [];
|
|
374
392
|
for (const n of e)
|
|
375
393
|
try {
|
|
376
394
|
const l = await this.enableToolset(n, !0);
|
|
377
|
-
|
|
395
|
+
t.push({ name: n, ...l });
|
|
378
396
|
} catch (l) {
|
|
379
|
-
|
|
397
|
+
t.push({
|
|
380
398
|
name: n,
|
|
381
399
|
success: !1,
|
|
382
400
|
message: l instanceof Error ? l.message : "Unknown error",
|
|
383
401
|
code: "E_INTERNAL"
|
|
384
402
|
});
|
|
385
403
|
}
|
|
386
|
-
const
|
|
387
|
-
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 };
|
|
388
406
|
}
|
|
389
407
|
/**
|
|
390
408
|
* Enables all available toolsets in a batch operation.
|
|
@@ -395,11 +413,12 @@ class oe {
|
|
|
395
413
|
return this.enableToolsets(e);
|
|
396
414
|
}
|
|
397
415
|
}
|
|
398
|
-
function
|
|
399
|
-
(
|
|
416
|
+
function ae(r, e, t) {
|
|
417
|
+
(t?.mode ?? "DYNAMIC") === "DYNAMIC" && (r.tool(
|
|
400
418
|
"enable_toolset",
|
|
401
419
|
"Enable a toolset by name",
|
|
402
|
-
{ name:
|
|
420
|
+
{ name: y.string().describe("Toolset name") },
|
|
421
|
+
{ destructiveHint: !0, idempotentHint: !0 },
|
|
403
422
|
async (o) => {
|
|
404
423
|
const i = await e.enableToolset(o.name);
|
|
405
424
|
return {
|
|
@@ -409,7 +428,8 @@ function re(r, e, s) {
|
|
|
409
428
|
), r.tool(
|
|
410
429
|
"disable_toolset",
|
|
411
430
|
"Disable a toolset by name (state only)",
|
|
412
|
-
{ name:
|
|
431
|
+
{ name: y.string().describe("Toolset name") },
|
|
432
|
+
{ destructiveHint: !0, idempotentHint: !0 },
|
|
413
433
|
async (o) => {
|
|
414
434
|
const i = await e.disableToolset(o.name);
|
|
415
435
|
return {
|
|
@@ -420,6 +440,7 @@ function re(r, e, s) {
|
|
|
420
440
|
"list_toolsets",
|
|
421
441
|
"List available toolsets with active status and definitions",
|
|
422
442
|
{},
|
|
443
|
+
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
423
444
|
async () => {
|
|
424
445
|
const o = e.getAvailableToolsets(), i = e.getStatus().toolsetToTools, n = o.map((l) => {
|
|
425
446
|
const a = e.getToolsetDefinition(l);
|
|
@@ -444,7 +465,8 @@ function re(r, e, s) {
|
|
|
444
465
|
), r.tool(
|
|
445
466
|
"describe_toolset",
|
|
446
467
|
"Describe a toolset with definition, active status and tools",
|
|
447
|
-
{ name:
|
|
468
|
+
{ name: y.string().describe("Toolset name") },
|
|
469
|
+
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
448
470
|
async (o) => {
|
|
449
471
|
const i = e.getToolsetDefinition(o.name), n = e.getStatus().toolsetToTools;
|
|
450
472
|
if (!i)
|
|
@@ -475,6 +497,7 @@ function re(r, e, s) {
|
|
|
475
497
|
"list_tools",
|
|
476
498
|
"List currently registered tool names (best effort)",
|
|
477
499
|
{},
|
|
500
|
+
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
478
501
|
async () => {
|
|
479
502
|
const o = e.getStatus(), i = {
|
|
480
503
|
tools: o.tools,
|
|
@@ -486,26 +509,32 @@ function re(r, e, s) {
|
|
|
486
509
|
}
|
|
487
510
|
);
|
|
488
511
|
}
|
|
489
|
-
class
|
|
512
|
+
class A {
|
|
490
513
|
constructor(e) {
|
|
491
|
-
this
|
|
492
|
-
|
|
493
|
-
this
|
|
514
|
+
d(this, "mode");
|
|
515
|
+
d(this, "resolver");
|
|
516
|
+
d(this, "manager");
|
|
517
|
+
d(this, "toolsetValidator");
|
|
518
|
+
d(this, "initPromise");
|
|
519
|
+
d(this, "initError", null);
|
|
520
|
+
this.toolsetValidator = new re();
|
|
521
|
+
const t = e.startup ?? {}, s = this.resolveStartupConfig(t, e.catalog);
|
|
522
|
+
this.mode = s.mode, this.resolver = new ie({
|
|
494
523
|
catalog: e.catalog,
|
|
495
524
|
moduleLoaders: e.moduleLoaders
|
|
496
525
|
});
|
|
497
|
-
const o = new
|
|
526
|
+
const o = new z({
|
|
498
527
|
namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
|
|
499
528
|
});
|
|
500
|
-
this.manager = new
|
|
529
|
+
this.manager = new ne({
|
|
501
530
|
server: e.server,
|
|
502
531
|
resolver: this.resolver,
|
|
503
532
|
context: e.context,
|
|
504
533
|
onToolsListChanged: e.notifyToolsListChanged,
|
|
505
534
|
exposurePolicy: e.exposurePolicy,
|
|
506
535
|
toolRegistry: o
|
|
507
|
-
}), e.registerMetaTools !== !1 &&
|
|
508
|
-
const i =
|
|
536
|
+
}), e.registerMetaTools !== !1 && ae(e.server, this.manager, { mode: this.mode });
|
|
537
|
+
const i = s.toolsets;
|
|
509
538
|
this.initPromise = this.initializeToolsets(i);
|
|
510
539
|
}
|
|
511
540
|
/**
|
|
@@ -518,8 +547,8 @@ class b {
|
|
|
518
547
|
async initializeToolsets(e) {
|
|
519
548
|
try {
|
|
520
549
|
e === "ALL" ? await this.manager.enableToolsets(this.resolver.getAvailableToolsets()) : Array.isArray(e) && e.length > 0 && await this.manager.enableToolsets(e);
|
|
521
|
-
} catch (
|
|
522
|
-
this.initError =
|
|
550
|
+
} catch (t) {
|
|
551
|
+
this.initError = t instanceof Error ? t : new Error(String(t)), console.error("Failed to initialize toolsets:", this.initError);
|
|
523
552
|
}
|
|
524
553
|
}
|
|
525
554
|
/**
|
|
@@ -539,19 +568,19 @@ class b {
|
|
|
539
568
|
async isReady() {
|
|
540
569
|
return await this.initPromise, this.initError === null;
|
|
541
570
|
}
|
|
542
|
-
resolveStartupConfig(e,
|
|
571
|
+
resolveStartupConfig(e, t) {
|
|
543
572
|
if (e.mode) {
|
|
544
573
|
if (e.mode === "DYNAMIC" && e.toolsets)
|
|
545
574
|
return console.warn("startup.toolsets provided but ignored in DYNAMIC mode"), { mode: "DYNAMIC" };
|
|
546
575
|
if (e.mode === "STATIC") {
|
|
547
576
|
if (e.toolsets === "ALL")
|
|
548
577
|
return { mode: "STATIC", toolsets: "ALL" };
|
|
549
|
-
const
|
|
550
|
-
for (const i of
|
|
551
|
-
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);
|
|
552
581
|
n && l ? o.push(l) : a && console.warn(a);
|
|
553
582
|
}
|
|
554
|
-
if (
|
|
583
|
+
if (s.length > 0 && o.length === 0)
|
|
555
584
|
throw new Error(
|
|
556
585
|
"STATIC mode requires valid toolsets or 'ALL'; none were valid"
|
|
557
586
|
);
|
|
@@ -561,16 +590,16 @@ class b {
|
|
|
561
590
|
}
|
|
562
591
|
if (e.toolsets === "ALL") return { mode: "STATIC", toolsets: "ALL" };
|
|
563
592
|
if (Array.isArray(e.toolsets) && e.toolsets.length > 0) {
|
|
564
|
-
const
|
|
593
|
+
const s = [];
|
|
565
594
|
for (const o of e.toolsets) {
|
|
566
|
-
const { isValid: i, sanitized: n, error: l } = this.toolsetValidator.validateToolsetName(o,
|
|
567
|
-
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);
|
|
568
597
|
}
|
|
569
|
-
if (
|
|
598
|
+
if (s.length === 0)
|
|
570
599
|
throw new Error(
|
|
571
600
|
"STATIC mode requires valid toolsets or 'ALL'; none were valid"
|
|
572
601
|
);
|
|
573
|
-
return { mode: "STATIC", toolsets:
|
|
602
|
+
return { mode: "STATIC", toolsets: s };
|
|
574
603
|
}
|
|
575
604
|
return { mode: "DYNAMIC" };
|
|
576
605
|
}
|
|
@@ -581,13 +610,19 @@ class b {
|
|
|
581
610
|
return this.manager;
|
|
582
611
|
}
|
|
583
612
|
}
|
|
584
|
-
var
|
|
585
|
-
class
|
|
613
|
+
var w, E;
|
|
614
|
+
class V {
|
|
586
615
|
constructor(e = {}) {
|
|
587
|
-
|
|
588
|
-
this
|
|
589
|
-
|
|
590
|
-
|
|
616
|
+
b(this, w);
|
|
617
|
+
d(this, "storage", /* @__PURE__ */ new Map());
|
|
618
|
+
d(this, "maxSize");
|
|
619
|
+
d(this, "ttlMs");
|
|
620
|
+
d(this, "onEvict");
|
|
621
|
+
// Use ReturnType<typeof setInterval> for cross-env typings without NodeJS namespace
|
|
622
|
+
d(this, "pruneInterval");
|
|
623
|
+
this.maxSize = e.maxSize ?? 1e3, this.ttlMs = e.ttlMs ?? 1e3 * 60 * 60, this.onEvict = e.onEvict;
|
|
624
|
+
const t = e.pruneIntervalMs ?? 1e3 * 60 * 10;
|
|
625
|
+
this.pruneInterval = setInterval(() => this.pruneExpired(), t);
|
|
591
626
|
}
|
|
592
627
|
getEntryCount() {
|
|
593
628
|
return this.storage.size;
|
|
@@ -599,13 +634,13 @@ class z {
|
|
|
599
634
|
return this.ttlMs;
|
|
600
635
|
}
|
|
601
636
|
get(e) {
|
|
602
|
-
const
|
|
603
|
-
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;
|
|
604
639
|
}
|
|
605
|
-
set(e,
|
|
640
|
+
set(e, t) {
|
|
606
641
|
this.storage.size >= this.maxSize && this.evictLeastRecentlyUsed();
|
|
607
|
-
const
|
|
608
|
-
this.storage.set(e,
|
|
642
|
+
const s = { resource: t, lastAccessed: Date.now() };
|
|
643
|
+
this.storage.set(e, s);
|
|
609
644
|
}
|
|
610
645
|
/**
|
|
611
646
|
* Removes an entry from the cache.
|
|
@@ -613,8 +648,8 @@ class z {
|
|
|
613
648
|
* @param key - The key to remove
|
|
614
649
|
*/
|
|
615
650
|
delete(e) {
|
|
616
|
-
const
|
|
617
|
-
|
|
651
|
+
const t = this.storage.get(e);
|
|
652
|
+
t && (this.storage.delete(e), m(this, w, E).call(this, e, t.resource));
|
|
618
653
|
}
|
|
619
654
|
/**
|
|
620
655
|
* Stops the background pruning interval and optionally clears all entries.
|
|
@@ -630,8 +665,8 @@ class z {
|
|
|
630
665
|
clear() {
|
|
631
666
|
const e = Array.from(this.storage.entries());
|
|
632
667
|
this.storage.clear();
|
|
633
|
-
for (const [
|
|
634
|
-
|
|
668
|
+
for (const [t, s] of e)
|
|
669
|
+
m(this, w, E).call(this, t, s.resource);
|
|
635
670
|
}
|
|
636
671
|
/**
|
|
637
672
|
* Evicts the least recently used entry from the cache.
|
|
@@ -646,33 +681,33 @@ class z {
|
|
|
646
681
|
* @private
|
|
647
682
|
*/
|
|
648
683
|
pruneExpired() {
|
|
649
|
-
const e = Date.now(),
|
|
650
|
-
for (const [
|
|
651
|
-
e - o.lastAccessed > this.ttlMs &&
|
|
652
|
-
for (const
|
|
653
|
-
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);
|
|
654
689
|
}
|
|
655
690
|
}
|
|
656
|
-
|
|
691
|
+
w = new WeakSet(), /**
|
|
657
692
|
* Safely calls the evict callback, catching and logging any errors.
|
|
658
693
|
* @param key - The key being evicted
|
|
659
694
|
* @param resource - The resource being evicted
|
|
660
695
|
* @private
|
|
661
696
|
*/
|
|
662
|
-
|
|
697
|
+
E = function(e, t) {
|
|
663
698
|
if (this.onEvict)
|
|
664
699
|
try {
|
|
665
|
-
const
|
|
666
|
-
|
|
700
|
+
const s = this.onEvict(e, t);
|
|
701
|
+
s instanceof Promise && s.catch((o) => {
|
|
667
702
|
console.warn(`Error in cache eviction callback for key '${e}':`, o);
|
|
668
703
|
});
|
|
669
|
-
} catch (
|
|
670
|
-
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);
|
|
671
706
|
}
|
|
672
707
|
};
|
|
673
|
-
function
|
|
708
|
+
function F(r, e, t, s) {
|
|
674
709
|
const o = ["/mcp", "/healthz", "/tools", "/.well-known/mcp-config"];
|
|
675
|
-
for (const i of
|
|
710
|
+
for (const i of t) {
|
|
676
711
|
const n = `${e}${i.path}`;
|
|
677
712
|
if (o.some(
|
|
678
713
|
(c) => n.startsWith(`${e}${c}`)
|
|
@@ -683,48 +718,48 @@ function V(r, e, s, t) {
|
|
|
683
718
|
continue;
|
|
684
719
|
}
|
|
685
720
|
const a = i.method.toLowerCase();
|
|
686
|
-
r[a](n, async (c,
|
|
721
|
+
r[a](n, async (c, u) => {
|
|
687
722
|
try {
|
|
688
|
-
const
|
|
689
|
-
let
|
|
723
|
+
const f = c.headers["mcp-client-id"]?.trim(), v = f && f.length > 0 ? f : `anon-${T()}`;
|
|
724
|
+
let x;
|
|
690
725
|
if (i.bodySchema) {
|
|
691
|
-
const
|
|
692
|
-
if (!
|
|
693
|
-
return
|
|
694
|
-
|
|
726
|
+
const g = i.bodySchema.safeParse(c.body);
|
|
727
|
+
if (!g.success)
|
|
728
|
+
return S(u, "body", g.error);
|
|
729
|
+
x = g.data;
|
|
695
730
|
}
|
|
696
731
|
let I = {};
|
|
697
732
|
if (i.querySchema) {
|
|
698
|
-
const
|
|
699
|
-
if (!
|
|
700
|
-
return
|
|
701
|
-
I =
|
|
733
|
+
const g = i.querySchema.safeParse(c.query);
|
|
734
|
+
if (!g.success)
|
|
735
|
+
return S(u, "query", g.error);
|
|
736
|
+
I = g.data;
|
|
702
737
|
}
|
|
703
|
-
let
|
|
738
|
+
let M = {};
|
|
704
739
|
if (i.paramsSchema) {
|
|
705
|
-
const
|
|
706
|
-
if (!
|
|
707
|
-
return
|
|
708
|
-
|
|
740
|
+
const g = i.paramsSchema.safeParse(c.params);
|
|
741
|
+
if (!g.success)
|
|
742
|
+
return S(u, "params", g.error);
|
|
743
|
+
M = g.data;
|
|
709
744
|
}
|
|
710
|
-
const
|
|
711
|
-
body:
|
|
745
|
+
const P = {
|
|
746
|
+
body: x,
|
|
712
747
|
query: I,
|
|
713
|
-
params:
|
|
748
|
+
params: M,
|
|
714
749
|
headers: c.headers,
|
|
715
|
-
clientId:
|
|
750
|
+
clientId: v
|
|
716
751
|
};
|
|
717
|
-
if (
|
|
718
|
-
const
|
|
719
|
-
Object.assign(
|
|
752
|
+
if (s?.contextExtractor) {
|
|
753
|
+
const g = await s.contextExtractor(c);
|
|
754
|
+
Object.assign(P, g);
|
|
720
755
|
}
|
|
721
|
-
const $ = await i.handler(
|
|
756
|
+
const $ = await i.handler(P);
|
|
722
757
|
if (i.responseSchema) {
|
|
723
|
-
const
|
|
724
|
-
return
|
|
758
|
+
const g = i.responseSchema.safeParse($);
|
|
759
|
+
return g.success ? g.data : (console.error(
|
|
725
760
|
`Response validation failed for ${i.method} ${i.path}:`,
|
|
726
|
-
|
|
727
|
-
),
|
|
761
|
+
g.error
|
|
762
|
+
), u.code(500), {
|
|
728
763
|
error: {
|
|
729
764
|
code: "RESPONSE_VALIDATION_ERROR",
|
|
730
765
|
message: "Internal server error: invalid response format"
|
|
@@ -732,51 +767,58 @@ function V(r, e, s, t) {
|
|
|
732
767
|
});
|
|
733
768
|
}
|
|
734
769
|
return $;
|
|
735
|
-
} catch (
|
|
770
|
+
} catch (f) {
|
|
736
771
|
return console.error(
|
|
737
772
|
`Error in custom endpoint ${i.method} ${i.path}:`,
|
|
738
|
-
|
|
739
|
-
),
|
|
773
|
+
f
|
|
774
|
+
), u.code(500), {
|
|
740
775
|
error: {
|
|
741
776
|
code: "INTERNAL_ERROR",
|
|
742
|
-
message:
|
|
777
|
+
message: f instanceof Error ? f.message : "Internal server error"
|
|
743
778
|
}
|
|
744
779
|
};
|
|
745
780
|
}
|
|
746
781
|
});
|
|
747
782
|
}
|
|
748
783
|
}
|
|
749
|
-
function
|
|
784
|
+
function S(r, e, t) {
|
|
750
785
|
return r.code(400), {
|
|
751
786
|
error: {
|
|
752
787
|
code: "VALIDATION_ERROR",
|
|
753
788
|
message: `Validation failed for ${e}`,
|
|
754
|
-
details:
|
|
789
|
+
details: t.errors
|
|
755
790
|
}
|
|
756
791
|
};
|
|
757
792
|
}
|
|
758
|
-
class
|
|
759
|
-
constructor(e,
|
|
760
|
-
this
|
|
761
|
-
|
|
762
|
-
|
|
793
|
+
class le {
|
|
794
|
+
constructor(e, t, s = {}, o) {
|
|
795
|
+
d(this, "options");
|
|
796
|
+
d(this, "defaultManager");
|
|
797
|
+
d(this, "createBundle");
|
|
798
|
+
d(this, "app", null);
|
|
799
|
+
d(this, "configSchema");
|
|
800
|
+
// Per-client server bundles and per-client session transports
|
|
801
|
+
d(this, "clientCache", new V({
|
|
802
|
+
onEvict: (e, t) => {
|
|
803
|
+
this.cleanupBundle(t);
|
|
763
804
|
}
|
|
764
|
-
})
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
805
|
+
}));
|
|
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
|
|
772
814
|
}, this.configSchema = o;
|
|
773
815
|
}
|
|
774
816
|
async start() {
|
|
775
817
|
if (this.app) return;
|
|
776
|
-
const e = this.options.app ??
|
|
777
|
-
this.options.cors && await e.register(
|
|
778
|
-
const
|
|
779
|
-
e.get(`${
|
|
818
|
+
const e = this.options.app ?? j({ logger: this.options.logger });
|
|
819
|
+
this.options.cors && await e.register(k, { origin: !0 });
|
|
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 ?? {
|
|
780
822
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
781
823
|
title: "MCP Session Configuration",
|
|
782
824
|
description: "Schema for the /mcp endpoint configuration",
|
|
@@ -786,32 +828,32 @@ class ie {
|
|
|
786
828
|
"x-mcp-version": "1.0",
|
|
787
829
|
"x-query-style": "dot+bracket"
|
|
788
830
|
})), e.post(
|
|
789
|
-
`${
|
|
790
|
-
async (
|
|
791
|
-
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-");
|
|
792
834
|
let a = l ? this.clientCache.get(n) : null;
|
|
793
835
|
if (!a) {
|
|
794
|
-
const
|
|
836
|
+
const f = this.createBundle(), v = f.sessions;
|
|
795
837
|
a = {
|
|
796
|
-
server:
|
|
797
|
-
orchestrator:
|
|
798
|
-
sessions:
|
|
838
|
+
server: f.server,
|
|
839
|
+
orchestrator: f.orchestrator,
|
|
840
|
+
sessions: v instanceof Map ? v : /* @__PURE__ */ new Map()
|
|
799
841
|
}, l && this.clientCache.set(n, a);
|
|
800
842
|
}
|
|
801
|
-
const c =
|
|
802
|
-
let
|
|
843
|
+
const c = s.headers["mcp-session-id"];
|
|
844
|
+
let u;
|
|
803
845
|
if (c && a.sessions.get(c))
|
|
804
|
-
|
|
805
|
-
else if (!c &&
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
sessionIdGenerator: () =>
|
|
809
|
-
onsessioninitialized: (
|
|
810
|
-
a.sessions.set(
|
|
846
|
+
u = a.sessions.get(c);
|
|
847
|
+
else if (!c && O(s.body)) {
|
|
848
|
+
const f = T();
|
|
849
|
+
u = new D({
|
|
850
|
+
sessionIdGenerator: () => f,
|
|
851
|
+
onsessioninitialized: (v) => {
|
|
852
|
+
a.sessions.set(v, u);
|
|
811
853
|
}
|
|
812
854
|
});
|
|
813
855
|
try {
|
|
814
|
-
await a.server.connect(
|
|
856
|
+
await a.server.connect(u);
|
|
815
857
|
} catch {
|
|
816
858
|
return o.code(500), {
|
|
817
859
|
jsonrpc: "2.0",
|
|
@@ -819,8 +861,8 @@ class ie {
|
|
|
819
861
|
id: null
|
|
820
862
|
};
|
|
821
863
|
}
|
|
822
|
-
|
|
823
|
-
|
|
864
|
+
u.onclose = () => {
|
|
865
|
+
u?.sessionId && a.sessions.delete(u.sessionId);
|
|
824
866
|
};
|
|
825
867
|
} else
|
|
826
868
|
return o.code(400), {
|
|
@@ -828,28 +870,28 @@ class ie {
|
|
|
828
870
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
829
871
|
id: null
|
|
830
872
|
};
|
|
831
|
-
return await
|
|
832
|
-
|
|
873
|
+
return await u.handleRequest(
|
|
874
|
+
s.raw,
|
|
833
875
|
o.raw,
|
|
834
|
-
|
|
876
|
+
s.body
|
|
835
877
|
), o;
|
|
836
878
|
}
|
|
837
|
-
), e.get(`${
|
|
838
|
-
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 : "";
|
|
839
881
|
if (!n)
|
|
840
882
|
return o.code(400), "Missing mcp-client-id";
|
|
841
883
|
const l = this.clientCache.get(n);
|
|
842
884
|
if (!l)
|
|
843
885
|
return o.code(400), "Invalid or expired client";
|
|
844
|
-
const a =
|
|
886
|
+
const a = s.headers["mcp-session-id"];
|
|
845
887
|
if (!a)
|
|
846
888
|
return o.code(400), "Missing mcp-session-id";
|
|
847
889
|
const c = l.sessions.get(a);
|
|
848
|
-
return c ? (await c.handleRequest(
|
|
890
|
+
return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
|
|
849
891
|
}), e.delete(
|
|
850
|
-
`${
|
|
851
|
-
async (
|
|
852
|
-
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"];
|
|
853
895
|
if (!n || !l)
|
|
854
896
|
return o.code(400), {
|
|
855
897
|
jsonrpc: "2.0",
|
|
@@ -877,7 +919,7 @@ class ie {
|
|
|
877
919
|
}
|
|
878
920
|
return o.code(204).send(), o;
|
|
879
921
|
}
|
|
880
|
-
), this.options.customEndpoints && this.options.customEndpoints.length > 0 &&
|
|
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;
|
|
881
923
|
}
|
|
882
924
|
/**
|
|
883
925
|
* Stops the Fastify server and cleans up all resources.
|
|
@@ -893,27 +935,27 @@ class ie {
|
|
|
893
935
|
* @private
|
|
894
936
|
*/
|
|
895
937
|
cleanupBundle(e) {
|
|
896
|
-
for (const [
|
|
938
|
+
for (const [t, s] of e.sessions.entries())
|
|
897
939
|
try {
|
|
898
|
-
typeof
|
|
899
|
-
console.warn(`Error closing session ${
|
|
940
|
+
typeof s.close == "function" && s.close().catch((o) => {
|
|
941
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
900
942
|
});
|
|
901
943
|
} catch (o) {
|
|
902
|
-
console.warn(`Error closing session ${
|
|
944
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
903
945
|
}
|
|
904
946
|
e.sessions.clear();
|
|
905
947
|
}
|
|
906
948
|
}
|
|
907
|
-
const
|
|
908
|
-
mode:
|
|
909
|
-
toolsets:
|
|
949
|
+
const ce = y.object({
|
|
950
|
+
mode: y.enum(["DYNAMIC", "STATIC"]).optional(),
|
|
951
|
+
toolsets: y.union([y.array(y.string()), y.literal("ALL")]).optional()
|
|
910
952
|
}).strict();
|
|
911
|
-
async function
|
|
953
|
+
async function Ie(r) {
|
|
912
954
|
if (r.startup)
|
|
913
955
|
try {
|
|
914
|
-
|
|
956
|
+
ce.parse(r.startup);
|
|
915
957
|
} catch (a) {
|
|
916
|
-
if (a instanceof
|
|
958
|
+
if (a instanceof y.ZodError) {
|
|
917
959
|
const c = a.format();
|
|
918
960
|
throw new Error(
|
|
919
961
|
`Invalid startup configuration:
|
|
@@ -927,9 +969,9 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
927
969
|
const e = r.startup?.mode ?? "DYNAMIC";
|
|
928
970
|
if (typeof r.createServer != "function")
|
|
929
971
|
throw new Error("createMcpServer: `createServer` (factory) is required");
|
|
930
|
-
const
|
|
972
|
+
const t = r.createServer(), s = (a) => typeof a?.server?.notification == "function", o = (a) => typeof a?.notifyToolsListChanged == "function", i = async (a) => {
|
|
931
973
|
try {
|
|
932
|
-
if (
|
|
974
|
+
if (s(a)) {
|
|
933
975
|
await a.server.notification({
|
|
934
976
|
method: "notifications/tools/list_changed"
|
|
935
977
|
});
|
|
@@ -941,23 +983,23 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
941
983
|
return;
|
|
942
984
|
console.warn("Failed to send tools list changed notification:", c);
|
|
943
985
|
}
|
|
944
|
-
}, n = new
|
|
945
|
-
server:
|
|
986
|
+
}, n = new A({
|
|
987
|
+
server: t,
|
|
946
988
|
catalog: r.catalog,
|
|
947
989
|
moduleLoaders: r.moduleLoaders,
|
|
948
990
|
exposurePolicy: r.exposurePolicy,
|
|
949
991
|
context: r.context,
|
|
950
|
-
notifyToolsListChanged: async () => i(
|
|
992
|
+
notifyToolsListChanged: async () => i(t),
|
|
951
993
|
startup: r.startup,
|
|
952
994
|
registerMetaTools: r.registerMetaTools !== void 0 ? r.registerMetaTools : e === "DYNAMIC"
|
|
953
995
|
});
|
|
954
996
|
e === "STATIC" && await n.ensureReady();
|
|
955
|
-
const l = new
|
|
997
|
+
const l = new le(
|
|
956
998
|
n.getManager(),
|
|
957
999
|
() => {
|
|
958
1000
|
if (e === "STATIC")
|
|
959
|
-
return { server:
|
|
960
|
-
const a = r.createServer(), c = new
|
|
1001
|
+
return { server: t, orchestrator: n };
|
|
1002
|
+
const a = r.createServer(), c = new A({
|
|
961
1003
|
server: a,
|
|
962
1004
|
catalog: r.catalog,
|
|
963
1005
|
moduleLoaders: r.moduleLoaders,
|
|
@@ -973,7 +1015,7 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
973
1015
|
r.configSchema
|
|
974
1016
|
);
|
|
975
1017
|
return {
|
|
976
|
-
server:
|
|
1018
|
+
server: t,
|
|
977
1019
|
start: async () => {
|
|
978
1020
|
await l.start();
|
|
979
1021
|
},
|
|
@@ -982,16 +1024,16 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
982
1024
|
}
|
|
983
1025
|
};
|
|
984
1026
|
}
|
|
985
|
-
function
|
|
986
|
-
|
|
1027
|
+
function de(r) {
|
|
1028
|
+
ue(r), he(r), fe(r), me(r);
|
|
987
1029
|
}
|
|
988
|
-
function
|
|
1030
|
+
function ue(r) {
|
|
989
1031
|
if (!r || typeof r != "object")
|
|
990
1032
|
throw new Error(
|
|
991
1033
|
"Permission configuration is required for createPermissionBasedMcpServer"
|
|
992
1034
|
);
|
|
993
1035
|
}
|
|
994
|
-
function
|
|
1036
|
+
function he(r) {
|
|
995
1037
|
if (!r.source)
|
|
996
1038
|
throw new Error('Permission source must be either "headers" or "config"');
|
|
997
1039
|
if (r.source !== "headers" && r.source !== "config")
|
|
@@ -999,19 +1041,19 @@ function ce(r) {
|
|
|
999
1041
|
`Invalid permission source: "${r.source}". Must be either "headers" or "config"`
|
|
1000
1042
|
);
|
|
1001
1043
|
}
|
|
1002
|
-
function
|
|
1044
|
+
function fe(r) {
|
|
1003
1045
|
if (r.source === "config" && !r.staticMap && !r.resolver)
|
|
1004
1046
|
throw new Error(
|
|
1005
1047
|
"Config-based permissions require at least one of: staticMap or resolver function"
|
|
1006
1048
|
);
|
|
1007
1049
|
}
|
|
1008
|
-
function
|
|
1050
|
+
function me(r) {
|
|
1009
1051
|
if (r.staticMap !== void 0) {
|
|
1010
1052
|
if (typeof r.staticMap != "object" || r.staticMap === null)
|
|
1011
1053
|
throw new Error(
|
|
1012
1054
|
"staticMap must be an object mapping client IDs to toolset arrays"
|
|
1013
1055
|
);
|
|
1014
|
-
|
|
1056
|
+
ge(r.staticMap);
|
|
1015
1057
|
}
|
|
1016
1058
|
if (r.resolver !== void 0 && typeof r.resolver != "function")
|
|
1017
1059
|
throw new Error(
|
|
@@ -1022,22 +1064,24 @@ function he(r) {
|
|
|
1022
1064
|
if (r.headerName !== void 0 && (typeof r.headerName != "string" || r.headerName.length === 0))
|
|
1023
1065
|
throw new Error("headerName must be a non-empty string");
|
|
1024
1066
|
}
|
|
1025
|
-
function
|
|
1026
|
-
for (const [e,
|
|
1027
|
-
if (!Array.isArray(
|
|
1067
|
+
function ge(r) {
|
|
1068
|
+
for (const [e, t] of Object.entries(r))
|
|
1069
|
+
if (!Array.isArray(t))
|
|
1028
1070
|
throw new Error(
|
|
1029
1071
|
`staticMap value for client "${e}" must be an array of toolset names`
|
|
1030
1072
|
);
|
|
1031
1073
|
}
|
|
1032
|
-
var
|
|
1033
|
-
class
|
|
1074
|
+
var p, _, H, B, Y, W;
|
|
1075
|
+
class pe {
|
|
1034
1076
|
/**
|
|
1035
1077
|
* Creates a new PermissionResolver instance.
|
|
1036
1078
|
* @param config - The permission configuration defining how permissions are resolved
|
|
1037
1079
|
*/
|
|
1038
1080
|
constructor(e) {
|
|
1039
|
-
|
|
1040
|
-
this
|
|
1081
|
+
b(this, p);
|
|
1082
|
+
d(this, "cache", /* @__PURE__ */ new Map());
|
|
1083
|
+
d(this, "normalizedHeaderName");
|
|
1084
|
+
this.config = e, this.normalizedHeaderName = (e.headerName || "mcp-toolset-permissions").toLowerCase();
|
|
1041
1085
|
}
|
|
1042
1086
|
/**
|
|
1043
1087
|
* Resolves permissions for a client based on the configured source.
|
|
@@ -1052,23 +1096,23 @@ class fe {
|
|
|
1052
1096
|
* @param headers - Optional request headers (required for header-based permissions)
|
|
1053
1097
|
* @returns Array of toolset names the client is allowed to access
|
|
1054
1098
|
*/
|
|
1055
|
-
resolvePermissions(e,
|
|
1099
|
+
resolvePermissions(e, t) {
|
|
1056
1100
|
if (this.cache.has(e))
|
|
1057
1101
|
return this.cache.get(e);
|
|
1058
|
-
let
|
|
1102
|
+
let s;
|
|
1059
1103
|
try {
|
|
1060
|
-
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(
|
|
1061
1105
|
`Permission resolution returned non-array for client ${e}, using empty permissions`
|
|
1062
|
-
),
|
|
1106
|
+
), s = []), s = s.filter(
|
|
1063
1107
|
(o) => typeof o == "string" && o.trim().length > 0
|
|
1064
1108
|
);
|
|
1065
1109
|
} catch (o) {
|
|
1066
1110
|
console.error(
|
|
1067
1111
|
`Unexpected error resolving permissions for client ${e}:`,
|
|
1068
1112
|
o
|
|
1069
|
-
),
|
|
1113
|
+
), s = [];
|
|
1070
1114
|
}
|
|
1071
|
-
return this.cache.set(e,
|
|
1115
|
+
return this.cache.set(e, s), s;
|
|
1072
1116
|
}
|
|
1073
1117
|
/**
|
|
1074
1118
|
* Invalidates cached permissions for a specific client.
|
|
@@ -1086,7 +1130,7 @@ class fe {
|
|
|
1086
1130
|
this.cache.clear();
|
|
1087
1131
|
}
|
|
1088
1132
|
}
|
|
1089
|
-
|
|
1133
|
+
p = new WeakSet(), /**
|
|
1090
1134
|
* Parses permissions from request headers.
|
|
1091
1135
|
* Extracts comma-separated toolset names from the configured header.
|
|
1092
1136
|
* Handles malformed headers gracefully by returning empty permissions.
|
|
@@ -1095,18 +1139,18 @@ g = new WeakSet(), /**
|
|
|
1095
1139
|
* @returns Array of toolset names from headers, or empty array if header is missing/malformed
|
|
1096
1140
|
* @private
|
|
1097
1141
|
*/
|
|
1098
|
-
|
|
1142
|
+
_ = function(e) {
|
|
1099
1143
|
if (!e)
|
|
1100
1144
|
return [];
|
|
1101
|
-
const
|
|
1102
|
-
if (!
|
|
1145
|
+
const t = m(this, p, H).call(this, e, this.normalizedHeaderName);
|
|
1146
|
+
if (!t)
|
|
1103
1147
|
return [];
|
|
1104
1148
|
try {
|
|
1105
|
-
return
|
|
1106
|
-
} catch (
|
|
1149
|
+
return t.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1150
|
+
} catch (s) {
|
|
1107
1151
|
return console.warn(
|
|
1108
1152
|
`Failed to parse permission header '${this.normalizedHeaderName}':`,
|
|
1109
|
-
|
|
1153
|
+
s
|
|
1110
1154
|
), [];
|
|
1111
1155
|
}
|
|
1112
1156
|
}, /**
|
|
@@ -1117,11 +1161,11 @@ F = function(e) {
|
|
|
1117
1161
|
* @returns The header value if found, undefined otherwise
|
|
1118
1162
|
* @private
|
|
1119
1163
|
*/
|
|
1120
|
-
|
|
1121
|
-
if (e[
|
|
1122
|
-
return e[
|
|
1123
|
-
for (const [
|
|
1124
|
-
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)
|
|
1125
1169
|
return o;
|
|
1126
1170
|
}, /**
|
|
1127
1171
|
* Resolves permissions from server-side configuration.
|
|
@@ -1133,14 +1177,14 @@ _ = function(e, s) {
|
|
|
1133
1177
|
*/
|
|
1134
1178
|
B = function(e) {
|
|
1135
1179
|
if (this.config.resolver) {
|
|
1136
|
-
const
|
|
1137
|
-
if (
|
|
1138
|
-
return
|
|
1180
|
+
const t = m(this, p, Y).call(this, e);
|
|
1181
|
+
if (t !== null)
|
|
1182
|
+
return t;
|
|
1139
1183
|
}
|
|
1140
1184
|
if (this.config.staticMap) {
|
|
1141
|
-
const
|
|
1142
|
-
if (
|
|
1143
|
-
return
|
|
1185
|
+
const t = m(this, p, W).call(this, e);
|
|
1186
|
+
if (t !== null)
|
|
1187
|
+
return t;
|
|
1144
1188
|
}
|
|
1145
1189
|
return this.config.defaultPermissions || [];
|
|
1146
1190
|
}, /**
|
|
@@ -1150,16 +1194,16 @@ B = function(e) {
|
|
|
1150
1194
|
* @returns Array of toolset names if successful, null if resolver fails or returns invalid data
|
|
1151
1195
|
* @private
|
|
1152
1196
|
*/
|
|
1153
|
-
|
|
1197
|
+
Y = function(e) {
|
|
1154
1198
|
try {
|
|
1155
|
-
const
|
|
1156
|
-
return Array.isArray(
|
|
1199
|
+
const t = this.config.resolver(e);
|
|
1200
|
+
return Array.isArray(t) ? t : (console.warn(
|
|
1157
1201
|
`Permission resolver returned non-array for client ${e}, using fallback`
|
|
1158
1202
|
), null);
|
|
1159
|
-
} catch (
|
|
1160
|
-
const
|
|
1203
|
+
} catch (t) {
|
|
1204
|
+
const s = t instanceof Error ? t.message : String(t);
|
|
1161
1205
|
return console.warn(
|
|
1162
|
-
`Permission resolver declined client ${e} (${
|
|
1206
|
+
`Permission resolver declined client ${e} (${s}), trying fallback`
|
|
1163
1207
|
), null;
|
|
1164
1208
|
}
|
|
1165
1209
|
}, /**
|
|
@@ -1169,25 +1213,25 @@ H = function(e) {
|
|
|
1169
1213
|
* @returns Array of toolset names if found, null if client not in map
|
|
1170
1214
|
* @private
|
|
1171
1215
|
*/
|
|
1172
|
-
|
|
1173
|
-
const
|
|
1174
|
-
return
|
|
1216
|
+
W = function(e) {
|
|
1217
|
+
const t = this.config.staticMap[e];
|
|
1218
|
+
return t !== void 0 ? Array.isArray(t) ? t : [] : null;
|
|
1175
1219
|
};
|
|
1176
|
-
function
|
|
1177
|
-
return async (
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
), o = r(
|
|
1182
|
-
if (
|
|
1183
|
-
const a = await i.enableToolsets(
|
|
1220
|
+
function ye(r, e) {
|
|
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);
|
|
1184
1228
|
for (const c of a.results)
|
|
1185
1229
|
c.success ? n.push(c.name) : (l.push(c.name), console.warn(
|
|
1186
|
-
`Failed to enable toolset '${c.name}' for client '${
|
|
1230
|
+
`Failed to enable toolset '${c.name}' for client '${t.clientId}': ${c.message}`
|
|
1187
1231
|
));
|
|
1188
1232
|
if (n.length === 0 && l.length > 0)
|
|
1189
1233
|
throw new Error(
|
|
1190
|
-
`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.`
|
|
1191
1235
|
);
|
|
1192
1236
|
}
|
|
1193
1237
|
return {
|
|
@@ -1198,8 +1242,8 @@ function me(r, e) {
|
|
|
1198
1242
|
};
|
|
1199
1243
|
};
|
|
1200
1244
|
}
|
|
1201
|
-
var h,
|
|
1202
|
-
class
|
|
1245
|
+
var h, U, q, J, G, K, Z, Q, X, C, ee;
|
|
1246
|
+
class ve {
|
|
1203
1247
|
/**
|
|
1204
1248
|
* Creates a new PermissionAwareFastifyTransport instance.
|
|
1205
1249
|
* @param defaultManager - Default tool manager for status endpoints
|
|
@@ -1207,20 +1251,27 @@ class ge {
|
|
|
1207
1251
|
* @param options - Transport configuration options
|
|
1208
1252
|
* @param configSchema - Optional JSON schema for configuration discovery
|
|
1209
1253
|
*/
|
|
1210
|
-
constructor(e,
|
|
1211
|
-
|
|
1212
|
-
this
|
|
1213
|
-
|
|
1214
|
-
|
|
1254
|
+
constructor(e, t, s = {}, o) {
|
|
1255
|
+
b(this, h);
|
|
1256
|
+
d(this, "options");
|
|
1257
|
+
d(this, "defaultManager");
|
|
1258
|
+
d(this, "createPermissionAwareBundle");
|
|
1259
|
+
d(this, "app", null);
|
|
1260
|
+
d(this, "configSchema");
|
|
1261
|
+
// Per-client server bundles and per-client session transports
|
|
1262
|
+
d(this, "clientCache", new V({
|
|
1263
|
+
onEvict: (e, t) => {
|
|
1264
|
+
m(this, h, U).call(this, t);
|
|
1215
1265
|
}
|
|
1216
|
-
})
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1266
|
+
}));
|
|
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
|
|
1224
1275
|
}, this.configSchema = o;
|
|
1225
1276
|
}
|
|
1226
1277
|
/**
|
|
@@ -1229,12 +1280,12 @@ class ge {
|
|
|
1229
1280
|
*/
|
|
1230
1281
|
async start() {
|
|
1231
1282
|
if (this.app) return;
|
|
1232
|
-
const e = this.options.app ??
|
|
1233
|
-
this.options.cors && await e.register(
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
contextExtractor: async (
|
|
1237
|
-
const o =
|
|
1283
|
+
const e = this.options.app ?? j({ logger: this.options.logger });
|
|
1284
|
+
this.options.cors && await e.register(k, { origin: !0 });
|
|
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);
|
|
1238
1289
|
try {
|
|
1239
1290
|
const i = await this.createPermissionAwareBundle(o);
|
|
1240
1291
|
return {
|
|
@@ -1266,14 +1317,14 @@ h = new WeakSet(), /**
|
|
|
1266
1317
|
* @param bundle - The client bundle to clean up
|
|
1267
1318
|
* @private
|
|
1268
1319
|
*/
|
|
1269
|
-
|
|
1270
|
-
for (const [
|
|
1320
|
+
U = function(e) {
|
|
1321
|
+
for (const [t, s] of e.sessions.entries())
|
|
1271
1322
|
try {
|
|
1272
|
-
typeof
|
|
1273
|
-
console.warn(`Error closing session ${
|
|
1323
|
+
typeof s.close == "function" && s.close().catch((o) => {
|
|
1324
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
1274
1325
|
});
|
|
1275
1326
|
} catch (o) {
|
|
1276
|
-
console.warn(`Error closing session ${
|
|
1327
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
1277
1328
|
}
|
|
1278
1329
|
e.sessions.clear();
|
|
1279
1330
|
}, /**
|
|
@@ -1282,7 +1333,7 @@ W = function(e) {
|
|
|
1282
1333
|
* @returns Normalized base path without trailing slash
|
|
1283
1334
|
* @private
|
|
1284
1335
|
*/
|
|
1285
|
-
|
|
1336
|
+
q = function(e) {
|
|
1286
1337
|
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
1287
1338
|
}, /**
|
|
1288
1339
|
* Registers the health check endpoint.
|
|
@@ -1290,24 +1341,24 @@ U = function(e) {
|
|
|
1290
1341
|
* @param base - Base path for routes
|
|
1291
1342
|
* @private
|
|
1292
1343
|
*/
|
|
1293
|
-
|
|
1294
|
-
e.get(`${
|
|
1344
|
+
J = function(e, t) {
|
|
1345
|
+
e.get(`${t}/healthz`, async () => ({ ok: !0 }));
|
|
1295
1346
|
}, /**
|
|
1296
1347
|
* Registers the tools status endpoint.
|
|
1297
1348
|
* @param app - Fastify instance
|
|
1298
1349
|
* @param base - Base path for routes
|
|
1299
1350
|
* @private
|
|
1300
1351
|
*/
|
|
1301
|
-
|
|
1302
|
-
e.get(`${
|
|
1352
|
+
G = function(e, t) {
|
|
1353
|
+
e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
|
|
1303
1354
|
}, /**
|
|
1304
1355
|
* Registers the MCP configuration discovery endpoint.
|
|
1305
1356
|
* @param app - Fastify instance
|
|
1306
1357
|
* @param base - Base path for routes
|
|
1307
1358
|
* @private
|
|
1308
1359
|
*/
|
|
1309
|
-
|
|
1310
|
-
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 ?? {
|
|
1311
1362
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1312
1363
|
title: "MCP Session Configuration",
|
|
1313
1364
|
description: "Schema for the /mcp endpoint configuration",
|
|
@@ -1324,42 +1375,42 @@ G = function(e, s) {
|
|
|
1324
1375
|
* @param base - Base path for routes
|
|
1325
1376
|
* @private
|
|
1326
1377
|
*/
|
|
1327
|
-
|
|
1378
|
+
Z = function(e, t) {
|
|
1328
1379
|
e.post(
|
|
1329
|
-
`${
|
|
1330
|
-
async (
|
|
1331
|
-
const i =
|
|
1380
|
+
`${t}/mcp`,
|
|
1381
|
+
async (s, o) => {
|
|
1382
|
+
const i = m(this, h, C).call(this, s), n = !i.clientId.startsWith("anon-");
|
|
1332
1383
|
let l = n ? this.clientCache.get(i.clientId) : null;
|
|
1333
1384
|
if (!l)
|
|
1334
1385
|
try {
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
`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(", ")}]`
|
|
1338
1389
|
);
|
|
1339
|
-
const
|
|
1390
|
+
const f = u.sessions;
|
|
1340
1391
|
l = {
|
|
1341
|
-
server:
|
|
1342
|
-
orchestrator:
|
|
1343
|
-
allowedToolsets:
|
|
1344
|
-
failedToolsets:
|
|
1345
|
-
sessions:
|
|
1392
|
+
server: u.server,
|
|
1393
|
+
orchestrator: u.orchestrator,
|
|
1394
|
+
allowedToolsets: u.allowedToolsets,
|
|
1395
|
+
failedToolsets: u.failedToolsets,
|
|
1396
|
+
sessions: f instanceof Map ? f : /* @__PURE__ */ new Map()
|
|
1346
1397
|
}, n && this.clientCache.set(i.clientId, l);
|
|
1347
|
-
} catch (
|
|
1398
|
+
} catch (u) {
|
|
1348
1399
|
return console.error(
|
|
1349
1400
|
`Failed to create permission-aware bundle for client ${i.clientId}:`,
|
|
1350
|
-
|
|
1351
|
-
), o.code(403),
|
|
1401
|
+
u
|
|
1402
|
+
), o.code(403), m(this, h, ee).call(this, "Access denied");
|
|
1352
1403
|
}
|
|
1353
|
-
const a =
|
|
1404
|
+
const a = s.headers["mcp-session-id"];
|
|
1354
1405
|
let c;
|
|
1355
1406
|
if (a && l.sessions.get(a))
|
|
1356
1407
|
c = l.sessions.get(a);
|
|
1357
|
-
else if (!a &&
|
|
1358
|
-
const
|
|
1359
|
-
c = new
|
|
1360
|
-
sessionIdGenerator: () =>
|
|
1361
|
-
onsessioninitialized: (
|
|
1362
|
-
l.sessions.set(
|
|
1408
|
+
else if (!a && O(s.body)) {
|
|
1409
|
+
const u = T();
|
|
1410
|
+
c = new D({
|
|
1411
|
+
sessionIdGenerator: () => u,
|
|
1412
|
+
onsessioninitialized: (f) => {
|
|
1413
|
+
l.sessions.set(f, c);
|
|
1363
1414
|
}
|
|
1364
1415
|
});
|
|
1365
1416
|
try {
|
|
@@ -1381,9 +1432,9 @@ K = function(e, s) {
|
|
|
1381
1432
|
id: null
|
|
1382
1433
|
};
|
|
1383
1434
|
return await c.handleRequest(
|
|
1384
|
-
|
|
1435
|
+
s.raw,
|
|
1385
1436
|
o.raw,
|
|
1386
|
-
|
|
1437
|
+
s.body
|
|
1387
1438
|
), o;
|
|
1388
1439
|
}
|
|
1389
1440
|
);
|
|
@@ -1393,19 +1444,19 @@ K = function(e, s) {
|
|
|
1393
1444
|
* @param base - Base path for routes
|
|
1394
1445
|
* @private
|
|
1395
1446
|
*/
|
|
1396
|
-
|
|
1397
|
-
e.get(`${
|
|
1398
|
-
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 : "";
|
|
1399
1450
|
if (!n)
|
|
1400
1451
|
return o.code(400), "Missing mcp-client-id";
|
|
1401
1452
|
const l = this.clientCache.get(n);
|
|
1402
1453
|
if (!l)
|
|
1403
1454
|
return o.code(400), "Invalid or expired client";
|
|
1404
|
-
const a =
|
|
1455
|
+
const a = s.headers["mcp-session-id"];
|
|
1405
1456
|
if (!a)
|
|
1406
1457
|
return o.code(400), "Missing mcp-session-id";
|
|
1407
1458
|
const c = l.sessions.get(a);
|
|
1408
|
-
return c ? (await c.handleRequest(
|
|
1459
|
+
return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
|
|
1409
1460
|
});
|
|
1410
1461
|
}, /**
|
|
1411
1462
|
* Registers the DELETE /mcp endpoint for session termination.
|
|
@@ -1413,11 +1464,11 @@ Z = function(e, s) {
|
|
|
1413
1464
|
* @param base - Base path for routes
|
|
1414
1465
|
* @private
|
|
1415
1466
|
*/
|
|
1416
|
-
|
|
1467
|
+
X = function(e, t) {
|
|
1417
1468
|
e.delete(
|
|
1418
|
-
`${
|
|
1419
|
-
async (
|
|
1420
|
-
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"];
|
|
1421
1472
|
if (!n || !l)
|
|
1422
1473
|
return o.code(400), {
|
|
1423
1474
|
jsonrpc: "2.0",
|
|
@@ -1453,11 +1504,11 @@ Q = function(e, s) {
|
|
|
1453
1504
|
* @returns Client request context with ID and headers
|
|
1454
1505
|
* @private
|
|
1455
1506
|
*/
|
|
1456
|
-
|
|
1457
|
-
const
|
|
1507
|
+
C = function(e) {
|
|
1508
|
+
const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${T()}`, o = {};
|
|
1458
1509
|
for (const [i, n] of Object.entries(e.headers))
|
|
1459
1510
|
typeof n == "string" && (o[i] = n);
|
|
1460
|
-
return { clientId:
|
|
1511
|
+
return { clientId: s, headers: o };
|
|
1461
1512
|
}, /**
|
|
1462
1513
|
* Creates a safe error response that doesn't expose unauthorized toolset information.
|
|
1463
1514
|
* Used for permission-related errors to prevent information leakage.
|
|
@@ -1466,17 +1517,17 @@ E = function(e) {
|
|
|
1466
1517
|
* @returns JSON-RPC error response object
|
|
1467
1518
|
* @private
|
|
1468
1519
|
*/
|
|
1469
|
-
|
|
1520
|
+
ee = function(e = "Access denied", t = -32e3) {
|
|
1470
1521
|
return {
|
|
1471
1522
|
jsonrpc: "2.0",
|
|
1472
1523
|
error: {
|
|
1473
|
-
code:
|
|
1524
|
+
code: t,
|
|
1474
1525
|
message: e
|
|
1475
1526
|
},
|
|
1476
1527
|
id: null
|
|
1477
1528
|
};
|
|
1478
1529
|
};
|
|
1479
|
-
function
|
|
1530
|
+
function Te(r) {
|
|
1480
1531
|
if (!r) return;
|
|
1481
1532
|
const e = {
|
|
1482
1533
|
namespaceToolsWithSetKey: r.namespaceToolsWithSetKey
|
|
@@ -1491,12 +1542,12 @@ function pe(r) {
|
|
|
1491
1542
|
"Permission-based servers: exposurePolicy.onLimitExceeded is ignored. No toolset limits are enforced."
|
|
1492
1543
|
), e;
|
|
1493
1544
|
}
|
|
1494
|
-
async function
|
|
1545
|
+
async function Me(r) {
|
|
1495
1546
|
if (!r.permissions)
|
|
1496
1547
|
throw new Error(
|
|
1497
1548
|
"Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
|
|
1498
1549
|
);
|
|
1499
|
-
if (
|
|
1550
|
+
if (de(r.permissions), r.startup)
|
|
1500
1551
|
throw new Error(
|
|
1501
1552
|
"Permission-based servers determine toolsets from client permissions. The 'startup' option is not allowed. Remove it from your configuration."
|
|
1502
1553
|
);
|
|
@@ -1504,10 +1555,10 @@ async function Ce(r) {
|
|
|
1504
1555
|
throw new Error(
|
|
1505
1556
|
"createPermissionBasedMcpServer: `createServer` (factory) is required"
|
|
1506
1557
|
);
|
|
1507
|
-
const e =
|
|
1558
|
+
const e = Te(
|
|
1508
1559
|
r.exposurePolicy
|
|
1509
|
-
),
|
|
1510
|
-
server:
|
|
1560
|
+
), t = new pe(r.permissions), s = r.createServer(), o = new A({
|
|
1561
|
+
server: s,
|
|
1511
1562
|
catalog: r.catalog,
|
|
1512
1563
|
moduleLoaders: r.moduleLoaders,
|
|
1513
1564
|
exposurePolicy: e,
|
|
@@ -1516,9 +1567,9 @@ async function Ce(r) {
|
|
|
1516
1567
|
// No notifications in STATIC mode
|
|
1517
1568
|
startup: { mode: "STATIC", toolsets: [] },
|
|
1518
1569
|
registerMetaTools: !1
|
|
1519
|
-
}), i =
|
|
1570
|
+
}), i = ye(
|
|
1520
1571
|
(l) => {
|
|
1521
|
-
const a = r.createServer(), c = new
|
|
1572
|
+
const a = r.createServer(), c = new A({
|
|
1522
1573
|
server: a,
|
|
1523
1574
|
catalog: r.catalog,
|
|
1524
1575
|
moduleLoaders: r.moduleLoaders,
|
|
@@ -1533,15 +1584,15 @@ async function Ce(r) {
|
|
|
1533
1584
|
});
|
|
1534
1585
|
return { server: a, orchestrator: c };
|
|
1535
1586
|
},
|
|
1536
|
-
|
|
1537
|
-
), n = new
|
|
1587
|
+
t
|
|
1588
|
+
), n = new ve(
|
|
1538
1589
|
o.getManager(),
|
|
1539
1590
|
i,
|
|
1540
1591
|
r.http,
|
|
1541
1592
|
r.configSchema
|
|
1542
1593
|
);
|
|
1543
1594
|
return {
|
|
1544
|
-
server:
|
|
1595
|
+
server: s,
|
|
1545
1596
|
start: async () => {
|
|
1546
1597
|
await n.start();
|
|
1547
1598
|
},
|
|
@@ -1549,21 +1600,21 @@ async function Ce(r) {
|
|
|
1549
1600
|
try {
|
|
1550
1601
|
await n.stop();
|
|
1551
1602
|
} finally {
|
|
1552
|
-
|
|
1603
|
+
t.clearCache();
|
|
1553
1604
|
}
|
|
1554
1605
|
}
|
|
1555
1606
|
};
|
|
1556
1607
|
}
|
|
1557
|
-
function
|
|
1608
|
+
function Pe(r) {
|
|
1558
1609
|
return r;
|
|
1559
1610
|
}
|
|
1560
|
-
function
|
|
1611
|
+
function $e(r) {
|
|
1561
1612
|
return r;
|
|
1562
1613
|
}
|
|
1563
1614
|
export {
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1615
|
+
Ie as createMcpServer,
|
|
1616
|
+
Me as createPermissionBasedMcpServer,
|
|
1617
|
+
Pe as defineEndpoint,
|
|
1618
|
+
$e as definePermissionAwareEndpoint
|
|
1568
1619
|
};
|
|
1569
1620
|
//# sourceMappingURL=index.js.map
|