toolception 0.6.1 → 0.6.2
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/README.md +1 -1
- package/dist/core/DynamicToolManager.d.ts +33 -22
- package/dist/core/DynamicToolManager.d.ts.map +1 -1
- package/dist/core/ServerOrchestrator.d.ts +33 -25
- package/dist/core/ServerOrchestrator.d.ts.map +1 -1
- package/dist/core/ToolRegistry.d.ts +5 -3
- package/dist/core/ToolRegistry.d.ts.map +1 -1
- package/dist/core/core.types.d.ts +29 -0
- package/dist/core/core.types.d.ts.map +1 -0
- package/dist/http/FastifyTransport.d.ts +49 -41
- package/dist/http/FastifyTransport.d.ts.map +1 -1
- package/dist/http/{customEndpoints.d.ts → http.types.d.ts} +33 -86
- package/dist/http/http.types.d.ts.map +1 -0
- package/dist/http/http.utils.d.ts +121 -0
- package/dist/http/http.utils.d.ts.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1087 -850
- package/dist/index.js.map +1 -1
- package/dist/mode/ModeResolver.d.ts +11 -10
- package/dist/mode/ModeResolver.d.ts.map +1 -1
- package/dist/mode/ModuleResolver.d.ts +18 -4
- package/dist/mode/ModuleResolver.d.ts.map +1 -1
- package/dist/mode/mode.types.d.ts +15 -0
- package/dist/mode/mode.types.d.ts.map +1 -0
- package/dist/permissions/PermissionAwareFastifyTransport.d.ts +15 -39
- package/dist/permissions/PermissionAwareFastifyTransport.d.ts.map +1 -1
- package/dist/permissions/PermissionResolver.d.ts +9 -24
- package/dist/permissions/PermissionResolver.d.ts.map +1 -1
- package/dist/permissions/{createPermissionAwareBundle.d.ts → permissions.types.d.ts} +17 -18
- package/dist/permissions/permissions.types.d.ts.map +1 -0
- package/dist/permissions/permissions.utils.d.ts +31 -0
- package/dist/permissions/permissions.utils.d.ts.map +1 -0
- package/dist/server/createMcpServer.d.ts +3 -46
- package/dist/server/createMcpServer.d.ts.map +1 -1
- package/dist/server/createPermissionBasedMcpServer.d.ts +2 -64
- package/dist/server/createPermissionBasedMcpServer.d.ts.map +1 -1
- package/dist/server/server.types.d.ts +71 -0
- package/dist/server/server.types.d.ts.map +1 -0
- package/dist/server/server.utils.d.ts +45 -0
- package/dist/server/server.utils.d.ts.map +1 -0
- package/dist/session/ClientResourceCache.d.ts +8 -27
- package/dist/session/ClientResourceCache.d.ts.map +1 -1
- package/dist/session/SessionContextResolver.d.ts +21 -72
- package/dist/session/SessionContextResolver.d.ts.map +1 -1
- package/dist/session/session.types.d.ts +29 -0
- package/dist/session/session.types.d.ts.map +1 -0
- package/dist/session/{validateSessionContextConfig.d.ts → session.utils.d.ts} +1 -2
- package/dist/session/session.utils.d.ts.map +1 -0
- package/dist/types/index.d.ts +0 -24
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/http/customEndpoints.d.ts.map +0 -1
- package/dist/http/endpointRegistration.d.ts +0 -35
- package/dist/http/endpointRegistration.d.ts.map +0 -1
- package/dist/permissions/createPermissionAwareBundle.d.ts.map +0 -1
- package/dist/permissions/validatePermissionConfig.d.ts +0 -9
- package/dist/permissions/validatePermissionConfig.d.ts.map +0 -1
- package/dist/session/validateSessionContextConfig.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,70 +1,71 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
throw TypeError(
|
|
1
|
+
var ge = Object.defineProperty;
|
|
2
|
+
var H = (r) => {
|
|
3
|
+
throw TypeError(r);
|
|
4
4
|
};
|
|
5
|
-
var
|
|
6
|
-
var d = (
|
|
7
|
-
var
|
|
8
|
-
var m = (
|
|
9
|
-
import { z as
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import { randomUUID as S, createHash as
|
|
13
|
-
import { StreamableHTTPServerTransport as
|
|
14
|
-
import { isInitializeRequest as
|
|
15
|
-
const
|
|
5
|
+
var ye = (r, e, t) => e in r ? ge(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
6
|
+
var d = (r, e, t) => ye(r, typeof e != "symbol" ? e + "" : e, t), ve = (r, e, t) => e.has(r) || H("Cannot " + t);
|
|
7
|
+
var x = (r, e, t) => e.has(r) ? H("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) => (ve(r, e, "access private method"), t);
|
|
9
|
+
import { z as g } from "zod";
|
|
10
|
+
import U from "fastify";
|
|
11
|
+
import J from "@fastify/cors";
|
|
12
|
+
import { randomUUID as S, createHash as be } from "node:crypto";
|
|
13
|
+
import { StreamableHTTPServerTransport as Q } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
14
|
+
import { isInitializeRequest as G } from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
+
const V = {
|
|
16
16
|
dynamic: [
|
|
17
17
|
"dynamic-tool-discovery",
|
|
18
18
|
"dynamicToolDiscovery",
|
|
19
19
|
"DYNAMIC_TOOL_DISCOVERY"
|
|
20
20
|
],
|
|
21
21
|
toolsets: ["tool-sets", "toolSets", "FMP_TOOL_SETS"]
|
|
22
|
-
};
|
|
23
|
-
class
|
|
22
|
+
}, W = ["_meta"];
|
|
23
|
+
class L {
|
|
24
24
|
constructor(e = {}) {
|
|
25
25
|
d(this, "keys");
|
|
26
26
|
this.keys = {
|
|
27
|
-
dynamic: e.keys?.dynamic ??
|
|
28
|
-
toolsets: e.keys?.toolsets ??
|
|
27
|
+
dynamic: e.keys?.dynamic ?? V.dynamic,
|
|
28
|
+
toolsets: e.keys?.toolsets ?? V.toolsets
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
static builder() {
|
|
32
|
+
const e = {}, t = {
|
|
33
|
+
keys(s) {
|
|
34
|
+
return e.keys = s, t;
|
|
35
|
+
},
|
|
36
|
+
build() {
|
|
37
|
+
return new L(e);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return t;
|
|
41
|
+
}
|
|
31
42
|
resolveMode(e, t) {
|
|
32
43
|
return this.isDynamicEnabled(t) ? "DYNAMIC" : this.getToolsetsString(t) ? "STATIC" : this.isDynamicEnabled(e) ? "DYNAMIC" : this.getToolsetsString(e) ? "STATIC" : null;
|
|
33
44
|
}
|
|
34
45
|
parseCommaSeparatedToolSets(e, t) {
|
|
35
46
|
if (!e || typeof e != "string") return [];
|
|
36
|
-
const s = e.split(",").map((
|
|
37
|
-
for (const
|
|
38
|
-
|
|
39
|
-
`Invalid toolset '${
|
|
40
|
-
|
|
47
|
+
const s = e.split(",").map((i) => i.trim()).filter((i) => i.length > 0), o = new Set(Object.keys(t)), n = [];
|
|
48
|
+
for (const i of s)
|
|
49
|
+
o.has(i) ? n.push(i) : console.warn(
|
|
50
|
+
`Invalid toolset '${i}' ignored. Available: ${Array.from(
|
|
51
|
+
o
|
|
41
52
|
).join(", ")}`
|
|
42
53
|
);
|
|
43
|
-
return
|
|
54
|
+
return n;
|
|
44
55
|
}
|
|
45
56
|
getModulesForToolSets(e, t) {
|
|
46
57
|
const s = /* @__PURE__ */ new Set();
|
|
47
|
-
for (const
|
|
48
|
-
const
|
|
49
|
-
|
|
58
|
+
for (const o of e) {
|
|
59
|
+
const n = t[o];
|
|
60
|
+
n && (n.modules || []).forEach((i) => s.add(i));
|
|
50
61
|
}
|
|
51
62
|
return Array.from(s);
|
|
52
63
|
}
|
|
53
64
|
validateToolsetName(e, t) {
|
|
54
65
|
if (!e || typeof e != "string")
|
|
55
|
-
return
|
|
56
|
-
isValid: !1,
|
|
57
|
-
error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${Object.keys(
|
|
58
|
-
t
|
|
59
|
-
).join(", ")}`
|
|
60
|
-
};
|
|
66
|
+
return this.createInvalidNameError(e, t);
|
|
61
67
|
const s = e.trim();
|
|
62
|
-
return s.length === 0 ? {
|
|
63
|
-
isValid: !1,
|
|
64
|
-
error: `Empty toolset name provided. Available toolsets: ${Object.keys(
|
|
65
|
-
t
|
|
66
|
-
).join(", ")}`
|
|
67
|
-
} : t[s] ? { isValid: !0, sanitized: s } : {
|
|
68
|
+
return s.length === 0 ? this.createInvalidNameError(s, t) : t[s] ? { isValid: !0, sanitized: s } : {
|
|
68
69
|
isValid: !1,
|
|
69
70
|
error: `Toolset '${s}' not found. Available toolsets: ${Object.keys(
|
|
70
71
|
t
|
|
@@ -72,19 +73,32 @@ class le {
|
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
76
|
+
* @param name - The invalid name value
|
|
77
|
+
* @param catalog - The toolset catalog for listing available options
|
|
78
|
+
* @returns Validation result with descriptive error message
|
|
79
|
+
*/
|
|
80
|
+
createInvalidNameError(e, t) {
|
|
81
|
+
const s = Object.keys(t).join(", ");
|
|
82
|
+
return !e || typeof e != "string" ? {
|
|
83
|
+
isValid: !1,
|
|
84
|
+
error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${s}`
|
|
85
|
+
} : {
|
|
86
|
+
isValid: !1,
|
|
87
|
+
error: `Empty toolset name provided. Available toolsets: ${s}`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
77
91
|
* @param toolsetNames - Array of toolset names to validate
|
|
78
92
|
* @param catalog - The toolset catalog to validate against
|
|
79
93
|
* @returns Validation result with modules array if valid
|
|
80
94
|
*/
|
|
81
95
|
validateToolsetModules(e, t) {
|
|
82
96
|
try {
|
|
83
|
-
for (const
|
|
84
|
-
if (!t[
|
|
97
|
+
for (const o of e)
|
|
98
|
+
if (!t[o])
|
|
85
99
|
return {
|
|
86
100
|
isValid: !1,
|
|
87
|
-
error: `Toolset '${
|
|
101
|
+
error: `Toolset '${o}' not found in catalog`
|
|
88
102
|
};
|
|
89
103
|
return { isValid: !0, modules: this.getModulesForToolSets(e, t) };
|
|
90
104
|
} catch (s) {
|
|
@@ -112,18 +126,31 @@ class le {
|
|
|
112
126
|
}
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
|
-
|
|
116
|
-
class ce {
|
|
129
|
+
class j {
|
|
117
130
|
constructor(e) {
|
|
118
131
|
d(this, "catalog");
|
|
119
132
|
d(this, "moduleLoaders");
|
|
120
|
-
for (const t of
|
|
133
|
+
for (const t of W)
|
|
121
134
|
if (t in e.catalog)
|
|
122
135
|
throw new Error(
|
|
123
136
|
`Toolset key '${t}' is reserved for internal use and cannot be used in the catalog`
|
|
124
137
|
);
|
|
125
138
|
this.catalog = e.catalog, this.moduleLoaders = e.moduleLoaders ?? {};
|
|
126
139
|
}
|
|
140
|
+
static builder() {
|
|
141
|
+
const e = {}, t = {
|
|
142
|
+
catalog(s) {
|
|
143
|
+
return e.catalog = s, t;
|
|
144
|
+
},
|
|
145
|
+
moduleLoaders(s) {
|
|
146
|
+
return e.moduleLoaders = s, t;
|
|
147
|
+
},
|
|
148
|
+
build() {
|
|
149
|
+
return new j(e);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return t;
|
|
153
|
+
}
|
|
127
154
|
getAvailableToolsets() {
|
|
128
155
|
return Object.keys(this.catalog);
|
|
129
156
|
}
|
|
@@ -144,7 +171,7 @@ class ce {
|
|
|
144
171
|
error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(
|
|
145
172
|
", "
|
|
146
173
|
)}`
|
|
147
|
-
} :
|
|
174
|
+
} : W.includes(t) ? {
|
|
148
175
|
isValid: !1,
|
|
149
176
|
error: `Toolset key '${t}' is reserved for internal use`
|
|
150
177
|
} : this.catalog[t] ? { isValid: !0, sanitized: t } : {
|
|
@@ -156,35 +183,51 @@ class ce {
|
|
|
156
183
|
}
|
|
157
184
|
async resolveToolsForToolsets(e, t) {
|
|
158
185
|
const s = [];
|
|
159
|
-
for (const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
for (const n of i.modules) {
|
|
163
|
-
const l = this.moduleLoaders[n];
|
|
164
|
-
if (l)
|
|
165
|
-
try {
|
|
166
|
-
const c = await l(t);
|
|
167
|
-
Array.isArray(c) && c.length > 0 && s.push(...c);
|
|
168
|
-
} catch (c) {
|
|
169
|
-
console.warn(
|
|
170
|
-
`Module loader '${n}' failed for toolset '${r}':`,
|
|
171
|
-
c
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
186
|
+
for (const o of e) {
|
|
187
|
+
const n = this.catalog[o];
|
|
188
|
+
n && (this.collectDirectTools(n, s), await this.loadModuleTools(n, o, t, s));
|
|
175
189
|
}
|
|
176
190
|
return s;
|
|
177
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* @param def - The toolset definition
|
|
194
|
+
* @param collected - Mutable array to append direct tools to
|
|
195
|
+
*/
|
|
196
|
+
collectDirectTools(e, t) {
|
|
197
|
+
Array.isArray(e.tools) && e.tools.length > 0 && t.push(...e.tools);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* @param def - The toolset definition containing module keys
|
|
201
|
+
* @param toolsetName - The toolset name for error messages
|
|
202
|
+
* @param context - Optional context passed to module loaders
|
|
203
|
+
* @param collected - Mutable array to append loaded tools to
|
|
204
|
+
*/
|
|
205
|
+
async loadModuleTools(e, t, s, o) {
|
|
206
|
+
if (!(!Array.isArray(e.modules) || e.modules.length === 0))
|
|
207
|
+
for (const n of e.modules) {
|
|
208
|
+
const i = this.moduleLoaders[n];
|
|
209
|
+
if (i)
|
|
210
|
+
try {
|
|
211
|
+
const a = await i(s);
|
|
212
|
+
Array.isArray(a) && a.length > 0 && o.push(...a);
|
|
213
|
+
} catch (a) {
|
|
214
|
+
console.warn(
|
|
215
|
+
`Module loader '${n}' failed for toolset '${t}':`,
|
|
216
|
+
a
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
178
221
|
}
|
|
179
|
-
class
|
|
180
|
-
constructor(t, s,
|
|
222
|
+
class Y extends Error {
|
|
223
|
+
constructor(t, s, o, n) {
|
|
181
224
|
super(t);
|
|
182
225
|
d(this, "code");
|
|
183
226
|
d(this, "details");
|
|
184
|
-
this.name = "ToolingError", this.code = s, this.details =
|
|
227
|
+
this.name = "ToolingError", this.code = s, this.details = o;
|
|
185
228
|
}
|
|
186
229
|
}
|
|
187
|
-
class
|
|
230
|
+
class C {
|
|
188
231
|
constructor(e = {}) {
|
|
189
232
|
d(this, "options");
|
|
190
233
|
d(this, "names", /* @__PURE__ */ new Set());
|
|
@@ -193,6 +236,17 @@ class q {
|
|
|
193
236
|
namespaceWithToolset: e.namespaceWithToolset ?? !0
|
|
194
237
|
};
|
|
195
238
|
}
|
|
239
|
+
static builder() {
|
|
240
|
+
const e = {}, t = {
|
|
241
|
+
namespaceWithToolset(s) {
|
|
242
|
+
return e.namespaceWithToolset = s, t;
|
|
243
|
+
},
|
|
244
|
+
build() {
|
|
245
|
+
return new C(e);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
return t;
|
|
249
|
+
}
|
|
196
250
|
getSafeName(e, t) {
|
|
197
251
|
return !this.options.namespaceWithToolset || t.startsWith(`${e}.`) ? t : `${e}.${t}`;
|
|
198
252
|
}
|
|
@@ -201,7 +255,7 @@ class q {
|
|
|
201
255
|
}
|
|
202
256
|
add(e) {
|
|
203
257
|
if (this.names.has(e))
|
|
204
|
-
throw new
|
|
258
|
+
throw new Y(
|
|
205
259
|
`Tool name collision: '${e}' already registered`,
|
|
206
260
|
"E_TOOL_NAME_CONFLICT"
|
|
207
261
|
);
|
|
@@ -214,13 +268,13 @@ class q {
|
|
|
214
268
|
}
|
|
215
269
|
mapAndValidate(e, t) {
|
|
216
270
|
return t.map((s) => {
|
|
217
|
-
const
|
|
218
|
-
if (this.has(
|
|
219
|
-
throw new
|
|
220
|
-
`Tool name collision for '${
|
|
271
|
+
const o = this.getSafeName(e, s.name);
|
|
272
|
+
if (this.has(o))
|
|
273
|
+
throw new Y(
|
|
274
|
+
`Tool name collision for '${o}'`,
|
|
221
275
|
"E_TOOL_NAME_CONFLICT"
|
|
222
276
|
);
|
|
223
|
-
return { ...s, name:
|
|
277
|
+
return { ...s, name: o };
|
|
224
278
|
});
|
|
225
279
|
}
|
|
226
280
|
list() {
|
|
@@ -233,7 +287,7 @@ class q {
|
|
|
233
287
|
return e;
|
|
234
288
|
}
|
|
235
289
|
}
|
|
236
|
-
class
|
|
290
|
+
class N {
|
|
237
291
|
constructor(e) {
|
|
238
292
|
d(this, "server");
|
|
239
293
|
d(this, "resolver");
|
|
@@ -242,13 +296,36 @@ class de {
|
|
|
242
296
|
d(this, "exposurePolicy");
|
|
243
297
|
d(this, "toolRegistry");
|
|
244
298
|
d(this, "activeToolsets", /* @__PURE__ */ new Set());
|
|
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 ??
|
|
299
|
+
this.server = e.server, this.resolver = e.resolver, this.context = e.context, this.onToolsListChanged = e.onToolsListChanged, this.exposurePolicy = e.exposurePolicy, this.toolRegistry = e.toolRegistry ?? C.builder().namespaceWithToolset(!0).build();
|
|
300
|
+
}
|
|
301
|
+
static builder() {
|
|
302
|
+
const e = {}, t = {
|
|
303
|
+
server(s) {
|
|
304
|
+
return e.server = s, t;
|
|
305
|
+
},
|
|
306
|
+
resolver(s) {
|
|
307
|
+
return e.resolver = s, t;
|
|
308
|
+
},
|
|
309
|
+
context(s) {
|
|
310
|
+
return e.context = s, t;
|
|
311
|
+
},
|
|
312
|
+
onToolsListChanged(s) {
|
|
313
|
+
return e.onToolsListChanged = s, t;
|
|
314
|
+
},
|
|
315
|
+
exposurePolicy(s) {
|
|
316
|
+
return e.exposurePolicy = s, t;
|
|
317
|
+
},
|
|
318
|
+
toolRegistry(s) {
|
|
319
|
+
return e.toolRegistry = s, t;
|
|
320
|
+
},
|
|
321
|
+
build() {
|
|
322
|
+
return new N(e);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
return t;
|
|
246
326
|
}
|
|
247
327
|
/**
|
|
248
|
-
* Sends a tool list change notification if configured.
|
|
249
|
-
* Logs warnings on failure instead of throwing.
|
|
250
328
|
* @returns Promise that resolves when notification is sent (or skipped)
|
|
251
|
-
* @private
|
|
252
329
|
*/
|
|
253
330
|
async notifyToolsChanged() {
|
|
254
331
|
if (this.onToolsListChanged)
|
|
@@ -272,59 +349,81 @@ class de {
|
|
|
272
349
|
}
|
|
273
350
|
/**
|
|
274
351
|
* Enables a single toolset by name.
|
|
275
|
-
* Validates the toolset, checks exposure policies, resolves tools, and registers them.
|
|
276
352
|
* @param toolsetName - The name of the toolset to enable
|
|
277
|
-
* @param skipNotification - If true, skips the tool list change notification
|
|
353
|
+
* @param skipNotification - If true, skips the tool list change notification
|
|
278
354
|
* @returns Result object with success status and message
|
|
279
355
|
*/
|
|
280
356
|
async enableToolset(e, t = !1) {
|
|
281
|
-
const s = this.
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const r = s.sanitized;
|
|
288
|
-
if (this.activeToolsets.has(r))
|
|
289
|
-
return {
|
|
290
|
-
success: !1,
|
|
291
|
-
message: `Toolset '${r}' is already enabled.`
|
|
292
|
-
};
|
|
293
|
-
const i = this.checkExposurePolicy(r);
|
|
294
|
-
if (!i.allowed)
|
|
295
|
-
return { success: !1, message: i.message };
|
|
296
|
-
const n = [];
|
|
357
|
+
const s = this.validateToolsetForEnable(e);
|
|
358
|
+
if ("message" in s) return s;
|
|
359
|
+
const { sanitized: o } = s, n = this.checkExposurePolicy(o);
|
|
360
|
+
if (!n.allowed)
|
|
361
|
+
return { success: !1, message: n.message };
|
|
362
|
+
const i = [];
|
|
297
363
|
try {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)
|
|
302
|
-
if (l && l.length > 0) {
|
|
303
|
-
const c = this.toolRegistry.mapAndValidate(
|
|
304
|
-
r,
|
|
305
|
-
l
|
|
306
|
-
);
|
|
307
|
-
for (const a of c)
|
|
308
|
-
this.registerSingleTool(a, r), n.push(a.name);
|
|
309
|
-
}
|
|
310
|
-
return this.activeToolsets.add(r), t || await this.notifyToolsChanged(), {
|
|
311
|
-
success: !0,
|
|
312
|
-
message: `Toolset '${r}' enabled successfully. Registered ${l?.length ?? 0} tools.`
|
|
313
|
-
};
|
|
314
|
-
} catch (l) {
|
|
315
|
-
return n.length > 0 && console.warn(
|
|
316
|
-
`Partial failure enabling toolset '${r}'. ${n.length} tools were registered but toolset activation failed. Tools remain registered due to MCP limitations: ${n.join(", ")}`
|
|
317
|
-
), {
|
|
364
|
+
const a = await this.resolveAndRegisterTools(o, i);
|
|
365
|
+
return this.activeToolsets.add(o), t || await this.notifyToolsChanged(), this.buildEnableResult(o, a);
|
|
366
|
+
} catch (a) {
|
|
367
|
+
return this.handlePartialFailure(o, i), {
|
|
318
368
|
success: !1,
|
|
319
|
-
message: `Failed to enable toolset '${
|
|
369
|
+
message: `Failed to enable toolset '${o}': ${a instanceof Error ? a.message : "Unknown error"}`
|
|
320
370
|
};
|
|
321
371
|
}
|
|
322
372
|
}
|
|
323
373
|
/**
|
|
324
|
-
*
|
|
374
|
+
* @param toolsetName - The raw toolset name to validate
|
|
375
|
+
* @returns Error result if invalid, or `{ sanitized }` to continue
|
|
376
|
+
*/
|
|
377
|
+
validateToolsetForEnable(e) {
|
|
378
|
+
const t = this.resolver.validateToolsetName(e);
|
|
379
|
+
return !t.isValid || !t.sanitized ? {
|
|
380
|
+
success: !1,
|
|
381
|
+
message: t.error || "Unknown validation error"
|
|
382
|
+
} : this.activeToolsets.has(t.sanitized) ? {
|
|
383
|
+
success: !1,
|
|
384
|
+
message: `Toolset '${t.sanitized}' is already enabled.`
|
|
385
|
+
} : { sanitized: t.sanitized };
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* @param sanitized - The validated toolset name
|
|
389
|
+
* @param registeredTools - Mutable array tracking registered tool names for rollback
|
|
390
|
+
* @returns The number of tools resolved
|
|
391
|
+
*/
|
|
392
|
+
async resolveAndRegisterTools(e, t) {
|
|
393
|
+
const s = await this.resolver.resolveToolsForToolsets(
|
|
394
|
+
[e],
|
|
395
|
+
this.context
|
|
396
|
+
);
|
|
397
|
+
if (s && s.length > 0) {
|
|
398
|
+
const o = this.toolRegistry.mapAndValidate(e, s);
|
|
399
|
+
for (const n of o)
|
|
400
|
+
this.registerSingleTool(n, e), t.push(n.name);
|
|
401
|
+
}
|
|
402
|
+
return s?.length ?? 0;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* @param sanitized - The toolset name
|
|
406
|
+
* @param toolCount - Number of tools registered
|
|
407
|
+
* @returns Success result object
|
|
408
|
+
*/
|
|
409
|
+
buildEnableResult(e, t) {
|
|
410
|
+
return {
|
|
411
|
+
success: !0,
|
|
412
|
+
message: `Toolset '${e}' enabled successfully. Registered ${t} tools.`
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* @param sanitized - The toolset name that partially failed
|
|
417
|
+
* @param registeredTools - Tools that were registered before the failure
|
|
418
|
+
*/
|
|
419
|
+
handlePartialFailure(e, t) {
|
|
420
|
+
t.length > 0 && console.warn(
|
|
421
|
+
`Partial failure enabling toolset '${e}'. ${t.length} tools were registered but toolset activation failed. Tools remain registered due to MCP limitations: ${t.join(", ")}`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
325
425
|
* @param toolsetName - The sanitized toolset name to check
|
|
326
426
|
* @returns Object indicating if allowed and reason message if not
|
|
327
|
-
* @private
|
|
328
427
|
*/
|
|
329
428
|
checkExposurePolicy(e) {
|
|
330
429
|
return this.exposurePolicy?.allowlist && !this.exposurePolicy.allowlist.includes(e) ? {
|
|
@@ -342,10 +441,8 @@ class de {
|
|
|
342
441
|
}) : { allowed: !0, message: "" };
|
|
343
442
|
}
|
|
344
443
|
/**
|
|
345
|
-
* Registers a single tool with the MCP server.
|
|
346
444
|
* @param tool - The tool definition to register
|
|
347
445
|
* @param toolsetKey - The toolset key for tracking
|
|
348
|
-
* @private
|
|
349
446
|
*/
|
|
350
447
|
registerSingleTool(e, t) {
|
|
351
448
|
e.annotations && Object.keys(e.annotations).length > 0 && e.annotations ? this.server.tool(
|
|
@@ -353,27 +450,25 @@ class de {
|
|
|
353
450
|
e.description,
|
|
354
451
|
e.inputSchema,
|
|
355
452
|
e.annotations,
|
|
356
|
-
async (
|
|
453
|
+
async (o) => await e.handler(o)
|
|
357
454
|
) : this.server.tool(
|
|
358
455
|
e.name,
|
|
359
456
|
e.description,
|
|
360
457
|
e.inputSchema,
|
|
361
|
-
async (
|
|
458
|
+
async (o) => await e.handler(o)
|
|
362
459
|
), this.toolRegistry.addForToolset(t, e.name);
|
|
363
460
|
}
|
|
364
461
|
/**
|
|
365
|
-
* Disables a toolset by name.
|
|
366
|
-
* Note: Due to MCP limitations, tools remain registered but the toolset is marked inactive.
|
|
367
462
|
* @param toolsetName - The name of the toolset to disable
|
|
368
463
|
* @returns Result object with success status and message
|
|
369
464
|
*/
|
|
370
465
|
async disableToolset(e) {
|
|
371
466
|
const t = this.resolver.validateToolsetName(e);
|
|
372
467
|
if (!t.isValid || !t.sanitized) {
|
|
373
|
-
const
|
|
468
|
+
const o = Array.from(this.activeToolsets).join(", ") || "none";
|
|
374
469
|
return {
|
|
375
470
|
success: !1,
|
|
376
|
-
message: `${t.error || "Unknown validation error"} Active toolsets: ${
|
|
471
|
+
message: `${t.error || "Unknown validation error"} Active toolsets: ${o}`
|
|
377
472
|
};
|
|
378
473
|
}
|
|
379
474
|
const s = t.sanitized;
|
|
@@ -397,30 +492,27 @@ class de {
|
|
|
397
492
|
};
|
|
398
493
|
}
|
|
399
494
|
/**
|
|
400
|
-
* Enables multiple toolsets in a batch operation.
|
|
401
|
-
* Sends a single notification after all toolsets are processed.
|
|
402
495
|
* @param toolsetNames - Array of toolset names to enable
|
|
403
496
|
* @returns Result object with overall success status and individual results
|
|
404
497
|
*/
|
|
405
498
|
async enableToolsets(e) {
|
|
406
499
|
const t = [];
|
|
407
|
-
for (const
|
|
500
|
+
for (const i of e)
|
|
408
501
|
try {
|
|
409
|
-
const
|
|
410
|
-
t.push({ name:
|
|
411
|
-
} catch (
|
|
502
|
+
const a = await this.enableToolset(i, !0);
|
|
503
|
+
t.push({ name: i, ...a });
|
|
504
|
+
} catch (a) {
|
|
412
505
|
t.push({
|
|
413
|
-
name:
|
|
506
|
+
name: i,
|
|
414
507
|
success: !1,
|
|
415
|
-
message:
|
|
508
|
+
message: a instanceof Error ? a.message : "Unknown error",
|
|
416
509
|
code: "E_INTERNAL"
|
|
417
510
|
});
|
|
418
511
|
}
|
|
419
|
-
const s = t.every((
|
|
420
|
-
return
|
|
512
|
+
const s = t.every((i) => i.success), o = t.some((i) => i.success), n = s ? "All toolsets enabled" : o ? "Some toolsets failed to enable" : "All toolsets failed to enable";
|
|
513
|
+
return o && await this.notifyToolsChanged(), { success: s, results: t, message: n };
|
|
421
514
|
}
|
|
422
515
|
/**
|
|
423
|
-
* Enables all available toolsets in a batch operation.
|
|
424
516
|
* @returns Result object with overall success status and individual results
|
|
425
517
|
*/
|
|
426
518
|
async enableAllToolsets() {
|
|
@@ -428,104 +520,104 @@ class de {
|
|
|
428
520
|
return this.enableToolsets(e);
|
|
429
521
|
}
|
|
430
522
|
}
|
|
431
|
-
const
|
|
432
|
-
function
|
|
433
|
-
(s?.mode ?? "DYNAMIC") === "DYNAMIC" && (t.addForToolset(
|
|
523
|
+
const T = "_meta";
|
|
524
|
+
function Te(r, e, t, s) {
|
|
525
|
+
(s?.mode ?? "DYNAMIC") === "DYNAMIC" && (t.addForToolset(T, "enable_toolset"), r.tool(
|
|
434
526
|
"enable_toolset",
|
|
435
527
|
"Enable a toolset by name",
|
|
436
|
-
{ name:
|
|
528
|
+
{ name: g.string().describe("Toolset name") },
|
|
437
529
|
{ destructiveHint: !0, idempotentHint: !0 },
|
|
438
|
-
async (
|
|
439
|
-
const
|
|
530
|
+
async (n) => {
|
|
531
|
+
const i = await e.enableToolset(n.name);
|
|
440
532
|
return {
|
|
441
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
533
|
+
content: [{ type: "text", text: JSON.stringify(i) }]
|
|
442
534
|
};
|
|
443
535
|
}
|
|
444
|
-
), t.addForToolset(
|
|
536
|
+
), t.addForToolset(T, "disable_toolset"), r.tool(
|
|
445
537
|
"disable_toolset",
|
|
446
538
|
"Disable a toolset by name (state only)",
|
|
447
|
-
{ name:
|
|
539
|
+
{ name: g.string().describe("Toolset name") },
|
|
448
540
|
{ destructiveHint: !0, idempotentHint: !0 },
|
|
449
|
-
async (
|
|
450
|
-
const
|
|
541
|
+
async (n) => {
|
|
542
|
+
const i = await e.disableToolset(n.name);
|
|
451
543
|
return {
|
|
452
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
544
|
+
content: [{ type: "text", text: JSON.stringify(i) }]
|
|
453
545
|
};
|
|
454
546
|
}
|
|
455
|
-
), t.addForToolset(
|
|
547
|
+
), t.addForToolset(T, "list_toolsets"), r.tool(
|
|
456
548
|
"list_toolsets",
|
|
457
549
|
"List available toolsets with active status and definitions",
|
|
458
550
|
{},
|
|
459
551
|
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
460
552
|
async () => {
|
|
461
|
-
const
|
|
462
|
-
const
|
|
553
|
+
const n = e.getAvailableToolsets(), i = e.getStatus().toolsetToTools, a = n.map((l) => {
|
|
554
|
+
const c = e.getToolsetDefinition(l);
|
|
463
555
|
return {
|
|
464
|
-
key:
|
|
465
|
-
active: e.isActive(
|
|
466
|
-
definition:
|
|
467
|
-
name:
|
|
468
|
-
description:
|
|
469
|
-
modules:
|
|
470
|
-
decisionCriteria:
|
|
556
|
+
key: l,
|
|
557
|
+
active: e.isActive(l),
|
|
558
|
+
definition: c ? {
|
|
559
|
+
name: c.name,
|
|
560
|
+
description: c.description,
|
|
561
|
+
modules: c.modules ?? [],
|
|
562
|
+
decisionCriteria: c.decisionCriteria ?? void 0
|
|
471
563
|
} : null,
|
|
472
|
-
tools:
|
|
564
|
+
tools: i[l] ?? []
|
|
473
565
|
};
|
|
474
566
|
});
|
|
475
567
|
return {
|
|
476
568
|
content: [
|
|
477
|
-
{ type: "text", text: JSON.stringify({ toolsets:
|
|
569
|
+
{ type: "text", text: JSON.stringify({ toolsets: a }) }
|
|
478
570
|
]
|
|
479
571
|
};
|
|
480
572
|
}
|
|
481
|
-
), t.addForToolset(
|
|
573
|
+
), t.addForToolset(T, "describe_toolset"), r.tool(
|
|
482
574
|
"describe_toolset",
|
|
483
575
|
"Describe a toolset with definition, active status and tools",
|
|
484
|
-
{ name:
|
|
576
|
+
{ name: g.string().describe("Toolset name") },
|
|
485
577
|
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
486
|
-
async (
|
|
487
|
-
const
|
|
488
|
-
if (!
|
|
578
|
+
async (n) => {
|
|
579
|
+
const i = e.getToolsetDefinition(n.name), a = e.getStatus().toolsetToTools;
|
|
580
|
+
if (!i)
|
|
489
581
|
return {
|
|
490
582
|
content: [
|
|
491
583
|
{
|
|
492
584
|
type: "text",
|
|
493
|
-
text: JSON.stringify({ error: `Unknown toolset '${
|
|
585
|
+
text: JSON.stringify({ error: `Unknown toolset '${n.name}'` })
|
|
494
586
|
}
|
|
495
587
|
]
|
|
496
588
|
};
|
|
497
|
-
const
|
|
498
|
-
key:
|
|
499
|
-
active: e.isActive(
|
|
589
|
+
const l = {
|
|
590
|
+
key: n.name,
|
|
591
|
+
active: e.isActive(n.name),
|
|
500
592
|
definition: {
|
|
501
|
-
name:
|
|
502
|
-
description:
|
|
503
|
-
modules:
|
|
504
|
-
decisionCriteria:
|
|
593
|
+
name: i.name,
|
|
594
|
+
description: i.description,
|
|
595
|
+
modules: i.modules ?? [],
|
|
596
|
+
decisionCriteria: i.decisionCriteria ?? void 0
|
|
505
597
|
},
|
|
506
|
-
tools:
|
|
598
|
+
tools: a[n.name] ?? []
|
|
507
599
|
};
|
|
508
600
|
return {
|
|
509
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
601
|
+
content: [{ type: "text", text: JSON.stringify(l) }]
|
|
510
602
|
};
|
|
511
603
|
}
|
|
512
|
-
)), t.addForToolset(
|
|
604
|
+
)), t.addForToolset(T, "list_tools"), r.tool(
|
|
513
605
|
"list_tools",
|
|
514
606
|
"List currently registered tool names (best effort)",
|
|
515
607
|
{},
|
|
516
608
|
{ readOnlyHint: !0, idempotentHint: !0 },
|
|
517
609
|
async () => {
|
|
518
|
-
const
|
|
519
|
-
tools:
|
|
520
|
-
toolsetToTools:
|
|
610
|
+
const n = e.getStatus(), i = {
|
|
611
|
+
tools: n.tools,
|
|
612
|
+
toolsetToTools: n.toolsetToTools
|
|
521
613
|
};
|
|
522
614
|
return {
|
|
523
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
615
|
+
content: [{ type: "text", text: JSON.stringify(i) }]
|
|
524
616
|
};
|
|
525
617
|
}
|
|
526
618
|
);
|
|
527
619
|
}
|
|
528
|
-
class
|
|
620
|
+
class A {
|
|
529
621
|
constructor(e) {
|
|
530
622
|
d(this, "mode");
|
|
531
623
|
d(this, "resolver");
|
|
@@ -533,32 +625,51 @@ class C {
|
|
|
533
625
|
d(this, "toolsetValidator");
|
|
534
626
|
d(this, "initPromise");
|
|
535
627
|
d(this, "initError", null);
|
|
536
|
-
this.toolsetValidator =
|
|
628
|
+
this.toolsetValidator = L.builder().build();
|
|
537
629
|
const t = e.startup ?? {}, s = this.resolveStartupConfig(t, e.catalog);
|
|
538
|
-
this.mode = s.mode, this.resolver =
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
|
|
544
|
-
});
|
|
545
|
-
this.manager = new de({
|
|
546
|
-
server: e.server,
|
|
547
|
-
resolver: this.resolver,
|
|
548
|
-
context: e.context,
|
|
549
|
-
onToolsListChanged: e.notifyToolsListChanged,
|
|
550
|
-
exposurePolicy: e.exposurePolicy,
|
|
551
|
-
toolRegistry: r
|
|
552
|
-
}), e.registerMetaTools !== !1 && ue(e.server, this.manager, r, { mode: this.mode });
|
|
630
|
+
this.mode = s.mode, this.resolver = j.builder().catalog(e.catalog).moduleLoaders(e.moduleLoaders ?? {}).build();
|
|
631
|
+
const o = C.builder().namespaceWithToolset(
|
|
632
|
+
e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
|
|
633
|
+
).build(), n = N.builder().server(e.server).resolver(this.resolver).context(e.context).toolRegistry(o);
|
|
634
|
+
e.notifyToolsListChanged && n.onToolsListChanged(e.notifyToolsListChanged), e.exposurePolicy && n.exposurePolicy(e.exposurePolicy), this.manager = n.build(), e.registerMetaTools !== !1 && Te(e.server, this.manager, o, { mode: this.mode });
|
|
553
635
|
const i = s.toolsets;
|
|
554
636
|
this.initPromise = this.initializeToolsets(i);
|
|
555
637
|
}
|
|
638
|
+
static builder() {
|
|
639
|
+
const e = {}, t = {
|
|
640
|
+
server(s) {
|
|
641
|
+
return e.server = s, t;
|
|
642
|
+
},
|
|
643
|
+
catalog(s) {
|
|
644
|
+
return e.catalog = s, t;
|
|
645
|
+
},
|
|
646
|
+
moduleLoaders(s) {
|
|
647
|
+
return e.moduleLoaders = s, t;
|
|
648
|
+
},
|
|
649
|
+
exposurePolicy(s) {
|
|
650
|
+
return e.exposurePolicy = s, t;
|
|
651
|
+
},
|
|
652
|
+
context(s) {
|
|
653
|
+
return e.context = s, t;
|
|
654
|
+
},
|
|
655
|
+
notifyToolsListChanged(s) {
|
|
656
|
+
return e.notifyToolsListChanged = s, t;
|
|
657
|
+
},
|
|
658
|
+
startup(s) {
|
|
659
|
+
return e.startup = s, t;
|
|
660
|
+
},
|
|
661
|
+
registerMetaTools(s) {
|
|
662
|
+
return e.registerMetaTools = s, t;
|
|
663
|
+
},
|
|
664
|
+
build() {
|
|
665
|
+
return new A(e);
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
return t;
|
|
669
|
+
}
|
|
556
670
|
/**
|
|
557
|
-
* Initializes toolsets asynchronously during construction.
|
|
558
|
-
* Stores any errors for later retrieval via ensureReady().
|
|
559
671
|
* @param initial - The toolsets to initialize or "ALL"
|
|
560
672
|
* @returns Promise that resolves when initialization is complete
|
|
561
|
-
* @private
|
|
562
673
|
*/
|
|
563
674
|
async initializeToolsets(e) {
|
|
564
675
|
try {
|
|
@@ -567,50 +678,46 @@ class C {
|
|
|
567
678
|
this.initError = t instanceof Error ? t : new Error(String(t)), console.error("Failed to initialize toolsets:", this.initError);
|
|
568
679
|
}
|
|
569
680
|
}
|
|
570
|
-
/**
|
|
571
|
-
* Waits for the orchestrator to be fully initialized.
|
|
572
|
-
* Call this before using the orchestrator to ensure all toolsets are loaded.
|
|
573
|
-
* @throws {Error} If initialization failed
|
|
574
|
-
*/
|
|
575
681
|
async ensureReady() {
|
|
576
682
|
if (await this.initPromise, this.initError)
|
|
577
683
|
throw this.initError;
|
|
578
684
|
}
|
|
579
|
-
/**
|
|
580
|
-
* Checks if the orchestrator has finished initialization.
|
|
581
|
-
* Does not throw on error - use ensureReady() for that.
|
|
582
|
-
* @returns Promise that resolves to true if ready, false if initialization failed
|
|
583
|
-
*/
|
|
584
685
|
async isReady() {
|
|
585
686
|
return await this.initPromise, this.initError === null;
|
|
586
687
|
}
|
|
587
688
|
resolveStartupConfig(e, t) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
689
|
+
return e.mode ? this.resolveExplicitMode(e.mode, e.toolsets, t) : this.inferModeFromToolsets(e, t);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* @param mode - The explicit mode
|
|
693
|
+
* @param toolsets - Optional toolsets from startup config
|
|
694
|
+
* @param catalog - The toolset catalog to validate against
|
|
695
|
+
* @returns Resolved mode and toolsets
|
|
696
|
+
*/
|
|
697
|
+
resolveExplicitMode(e, t, s) {
|
|
698
|
+
if (e === "DYNAMIC" && t)
|
|
699
|
+
return console.warn("startup.toolsets provided but ignored in DYNAMIC mode"), { mode: "DYNAMIC" };
|
|
700
|
+
if (e === "STATIC") {
|
|
701
|
+
if (t === "ALL")
|
|
702
|
+
return { mode: "STATIC", toolsets: "ALL" };
|
|
703
|
+
const o = Array.isArray(t) ? t : [], n = this.validateAndCollectToolsets(o, s);
|
|
704
|
+
if (o.length > 0 && n.length === 0)
|
|
705
|
+
throw new Error(
|
|
706
|
+
"STATIC mode requires valid toolsets or 'ALL'; none were valid"
|
|
707
|
+
);
|
|
708
|
+
return { mode: "STATIC", toolsets: n };
|
|
606
709
|
}
|
|
710
|
+
return { mode: e };
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* @param startup - Startup config without an explicit mode
|
|
714
|
+
* @param catalog - The toolset catalog to validate against
|
|
715
|
+
* @returns Inferred mode and toolsets
|
|
716
|
+
*/
|
|
717
|
+
inferModeFromToolsets(e, t) {
|
|
607
718
|
if (e.toolsets === "ALL") return { mode: "STATIC", toolsets: "ALL" };
|
|
608
719
|
if (Array.isArray(e.toolsets) && e.toolsets.length > 0) {
|
|
609
|
-
const s =
|
|
610
|
-
for (const r of e.toolsets) {
|
|
611
|
-
const { isValid: i, sanitized: n, error: l } = this.toolsetValidator.validateToolsetName(r, t);
|
|
612
|
-
i && n ? s.push(n) : l && console.warn(l);
|
|
613
|
-
}
|
|
720
|
+
const s = this.validateAndCollectToolsets(e.toolsets, t);
|
|
614
721
|
if (s.length === 0)
|
|
615
722
|
throw new Error(
|
|
616
723
|
"STATIC mode requires valid toolsets or 'ALL'; none were valid"
|
|
@@ -619,6 +726,19 @@ class C {
|
|
|
619
726
|
}
|
|
620
727
|
return { mode: "DYNAMIC" };
|
|
621
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* @param names - Array of toolset names to validate
|
|
731
|
+
* @param catalog - The toolset catalog to validate against
|
|
732
|
+
* @returns Array of valid, sanitized toolset names
|
|
733
|
+
*/
|
|
734
|
+
validateAndCollectToolsets(e, t) {
|
|
735
|
+
const s = [];
|
|
736
|
+
for (const o of e) {
|
|
737
|
+
const { isValid: n, sanitized: i, error: a } = this.toolsetValidator.validateToolsetName(o, t);
|
|
738
|
+
n && i ? s.push(i) : a && console.warn(a);
|
|
739
|
+
}
|
|
740
|
+
return s;
|
|
741
|
+
}
|
|
622
742
|
getMode() {
|
|
623
743
|
return this.mode;
|
|
624
744
|
}
|
|
@@ -626,10 +746,10 @@ class C {
|
|
|
626
746
|
return this.manager;
|
|
627
747
|
}
|
|
628
748
|
}
|
|
629
|
-
var
|
|
630
|
-
class
|
|
749
|
+
var w, M;
|
|
750
|
+
const D = class D {
|
|
631
751
|
constructor(e = {}) {
|
|
632
|
-
|
|
752
|
+
x(this, w);
|
|
633
753
|
d(this, "storage", /* @__PURE__ */ new Map());
|
|
634
754
|
d(this, "maxSize");
|
|
635
755
|
d(this, "ttlMs");
|
|
@@ -640,6 +760,26 @@ class F {
|
|
|
640
760
|
const t = e.pruneIntervalMs ?? 1e3 * 60 * 10;
|
|
641
761
|
this.pruneInterval = setInterval(() => this.pruneExpired(), t);
|
|
642
762
|
}
|
|
763
|
+
static builder() {
|
|
764
|
+
const e = {}, t = {
|
|
765
|
+
maxSize(s) {
|
|
766
|
+
return e.maxSize = s, t;
|
|
767
|
+
},
|
|
768
|
+
ttlMs(s) {
|
|
769
|
+
return e.ttlMs = s, t;
|
|
770
|
+
},
|
|
771
|
+
pruneIntervalMs(s) {
|
|
772
|
+
return e.pruneIntervalMs = s, t;
|
|
773
|
+
},
|
|
774
|
+
onEvict(s) {
|
|
775
|
+
return e.onEvict = s, t;
|
|
776
|
+
},
|
|
777
|
+
build() {
|
|
778
|
+
return new D(e);
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
return t;
|
|
782
|
+
}
|
|
643
783
|
getEntryCount() {
|
|
644
784
|
return this.storage.size;
|
|
645
785
|
}
|
|
@@ -659,122 +799,112 @@ class F {
|
|
|
659
799
|
this.storage.set(e, s);
|
|
660
800
|
}
|
|
661
801
|
/**
|
|
662
|
-
* Removes an entry from the cache.
|
|
663
|
-
* Calls the onEvict callback if configured.
|
|
664
802
|
* @param key - The key to remove
|
|
665
803
|
*/
|
|
666
804
|
delete(e) {
|
|
667
805
|
const t = this.storage.get(e);
|
|
668
|
-
t && (this.storage.delete(e), m(this,
|
|
806
|
+
t && (this.storage.delete(e), m(this, w, M).call(this, e, t.resource));
|
|
669
807
|
}
|
|
670
808
|
/**
|
|
671
|
-
* Stops the background pruning interval and optionally clears all entries.
|
|
672
809
|
* @param clearEntries - If true, also removes all entries and calls onEvict for each
|
|
673
810
|
*/
|
|
674
811
|
stop(e = !1) {
|
|
675
812
|
this.pruneInterval && (clearInterval(this.pruneInterval), this.pruneInterval = void 0), e && this.clear();
|
|
676
813
|
}
|
|
677
|
-
/**
|
|
678
|
-
* Clears all entries from the cache.
|
|
679
|
-
* Calls onEvict for each entry being removed.
|
|
680
|
-
*/
|
|
681
814
|
clear() {
|
|
682
815
|
const e = Array.from(this.storage.entries());
|
|
683
816
|
this.storage.clear();
|
|
684
817
|
for (const [t, s] of e)
|
|
685
|
-
m(this,
|
|
818
|
+
m(this, w, M).call(this, t, s.resource);
|
|
686
819
|
}
|
|
687
|
-
/**
|
|
688
|
-
* Evicts the least recently used entry from the cache.
|
|
689
|
-
* @private
|
|
690
|
-
*/
|
|
691
820
|
evictLeastRecentlyUsed() {
|
|
692
821
|
const e = this.storage.keys().next().value;
|
|
693
822
|
e && this.delete(e);
|
|
694
823
|
}
|
|
695
|
-
/**
|
|
696
|
-
* Removes all expired entries from the cache.
|
|
697
|
-
* @private
|
|
698
|
-
*/
|
|
699
824
|
pruneExpired() {
|
|
700
825
|
const e = Date.now(), t = [];
|
|
701
|
-
for (const [s,
|
|
702
|
-
e -
|
|
826
|
+
for (const [s, o] of this.storage.entries())
|
|
827
|
+
e - o.lastAccessed > this.ttlMs && t.push(s);
|
|
703
828
|
for (const s of t)
|
|
704
829
|
this.delete(s);
|
|
705
830
|
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
* Safely calls the evict callback, catching and logging any errors.
|
|
831
|
+
};
|
|
832
|
+
w = new WeakSet(), /**
|
|
709
833
|
* @param key - The key being evicted
|
|
710
834
|
* @param resource - The resource being evicted
|
|
711
|
-
* @private
|
|
712
835
|
*/
|
|
713
|
-
|
|
836
|
+
M = function(e, t) {
|
|
714
837
|
if (this.onEvict)
|
|
715
838
|
try {
|
|
716
839
|
const s = this.onEvict(e, t);
|
|
717
|
-
s instanceof Promise && s.catch((
|
|
718
|
-
console.warn(`Error in cache eviction callback for key '${e}':`,
|
|
840
|
+
s instanceof Promise && s.catch((o) => {
|
|
841
|
+
console.warn(`Error in cache eviction callback for key '${e}':`, o);
|
|
719
842
|
});
|
|
720
843
|
} catch (s) {
|
|
721
844
|
console.warn(`Error in cache eviction callback for key '${e}':`, s);
|
|
722
845
|
}
|
|
723
846
|
};
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
847
|
+
let E = D;
|
|
848
|
+
function tt(r) {
|
|
849
|
+
return r;
|
|
850
|
+
}
|
|
851
|
+
function st(r) {
|
|
852
|
+
return r;
|
|
853
|
+
}
|
|
854
|
+
function Z(r, e, t, s) {
|
|
855
|
+
const o = ["/mcp", "/healthz", "/tools", "/.well-known/mcp-config"];
|
|
856
|
+
for (const n of t) {
|
|
857
|
+
const i = `${e}${n.path}`;
|
|
858
|
+
if (o.some(
|
|
859
|
+
(c) => i.startsWith(`${e}${c}`)
|
|
730
860
|
)) {
|
|
731
861
|
console.warn(
|
|
732
|
-
`Custom endpoint ${
|
|
862
|
+
`Custom endpoint ${n.method} ${n.path} conflicts with built-in MCP endpoint. Skipping registration.`
|
|
733
863
|
);
|
|
734
864
|
continue;
|
|
735
865
|
}
|
|
736
|
-
const
|
|
737
|
-
|
|
866
|
+
const l = n.method.toLowerCase();
|
|
867
|
+
r[l](i, async (c, u) => {
|
|
738
868
|
try {
|
|
739
|
-
const
|
|
740
|
-
let
|
|
741
|
-
if (
|
|
742
|
-
const
|
|
743
|
-
if (!
|
|
744
|
-
return
|
|
745
|
-
|
|
869
|
+
const f = c.headers["mcp-client-id"]?.trim(), v = f && f.length > 0 ? f : `anon-${S()}`;
|
|
870
|
+
let b;
|
|
871
|
+
if (n.bodySchema) {
|
|
872
|
+
const p = n.bodySchema.safeParse(c.body);
|
|
873
|
+
if (!p.success)
|
|
874
|
+
return P(u, "body", p.error);
|
|
875
|
+
b = p.data;
|
|
746
876
|
}
|
|
747
|
-
let
|
|
748
|
-
if (
|
|
749
|
-
const
|
|
750
|
-
if (!
|
|
751
|
-
return
|
|
752
|
-
|
|
877
|
+
let F = {};
|
|
878
|
+
if (n.querySchema) {
|
|
879
|
+
const p = n.querySchema.safeParse(c.query);
|
|
880
|
+
if (!p.success)
|
|
881
|
+
return P(u, "query", p.error);
|
|
882
|
+
F = p.data;
|
|
753
883
|
}
|
|
754
|
-
let
|
|
755
|
-
if (
|
|
756
|
-
const
|
|
757
|
-
if (!
|
|
758
|
-
return
|
|
759
|
-
|
|
884
|
+
let _ = {};
|
|
885
|
+
if (n.paramsSchema) {
|
|
886
|
+
const p = n.paramsSchema.safeParse(c.params);
|
|
887
|
+
if (!p.success)
|
|
888
|
+
return P(u, "params", p.error);
|
|
889
|
+
_ = p.data;
|
|
760
890
|
}
|
|
761
|
-
const
|
|
762
|
-
body:
|
|
763
|
-
query:
|
|
764
|
-
params:
|
|
765
|
-
headers:
|
|
766
|
-
clientId:
|
|
891
|
+
const K = {
|
|
892
|
+
body: b,
|
|
893
|
+
query: F,
|
|
894
|
+
params: _,
|
|
895
|
+
headers: c.headers,
|
|
896
|
+
clientId: v
|
|
767
897
|
};
|
|
768
898
|
if (s?.contextExtractor) {
|
|
769
|
-
const
|
|
770
|
-
Object.assign(
|
|
899
|
+
const p = await s.contextExtractor(c);
|
|
900
|
+
Object.assign(K, p);
|
|
771
901
|
}
|
|
772
|
-
const
|
|
773
|
-
if (
|
|
774
|
-
const
|
|
775
|
-
return
|
|
776
|
-
`Response validation failed for ${
|
|
777
|
-
|
|
902
|
+
const B = await n.handler(K);
|
|
903
|
+
if (n.responseSchema) {
|
|
904
|
+
const p = n.responseSchema.safeParse(B);
|
|
905
|
+
return p.success ? p.data : (console.error(
|
|
906
|
+
`Response validation failed for ${n.method} ${n.path}:`,
|
|
907
|
+
p.error
|
|
778
908
|
), u.code(500), {
|
|
779
909
|
error: {
|
|
780
910
|
code: "RESPONSE_VALIDATION_ERROR",
|
|
@@ -782,23 +912,23 @@ function V(o, e, t, s) {
|
|
|
782
912
|
}
|
|
783
913
|
});
|
|
784
914
|
}
|
|
785
|
-
return
|
|
786
|
-
} catch (
|
|
915
|
+
return B;
|
|
916
|
+
} catch (f) {
|
|
787
917
|
return console.error(
|
|
788
|
-
`Error in custom endpoint ${
|
|
789
|
-
|
|
918
|
+
`Error in custom endpoint ${n.method} ${n.path}:`,
|
|
919
|
+
f
|
|
790
920
|
), u.code(500), {
|
|
791
921
|
error: {
|
|
792
922
|
code: "INTERNAL_ERROR",
|
|
793
|
-
message:
|
|
923
|
+
message: f instanceof Error ? f.message : "Internal server error"
|
|
794
924
|
}
|
|
795
925
|
};
|
|
796
926
|
}
|
|
797
927
|
});
|
|
798
928
|
}
|
|
799
929
|
}
|
|
800
|
-
function
|
|
801
|
-
return
|
|
930
|
+
function P(r, e, t) {
|
|
931
|
+
return r.code(400), {
|
|
802
932
|
error: {
|
|
803
933
|
code: "VALIDATION_ERROR",
|
|
804
934
|
message: `Validation failed for ${e}`,
|
|
@@ -806,8 +936,9 @@ function E(o, e, t) {
|
|
|
806
936
|
}
|
|
807
937
|
};
|
|
808
938
|
}
|
|
809
|
-
|
|
810
|
-
|
|
939
|
+
const we = g.string({ message: "Missing required mcp-client-id header" }).trim().min(1, "mcp-client-id header must not be empty");
|
|
940
|
+
class O {
|
|
941
|
+
constructor(e, t, s = {}, o, n, i) {
|
|
811
942
|
d(this, "options");
|
|
812
943
|
d(this, "defaultManager");
|
|
813
944
|
d(this, "createBundle");
|
|
@@ -816,12 +947,12 @@ class he {
|
|
|
816
947
|
d(this, "app", null);
|
|
817
948
|
d(this, "configSchema");
|
|
818
949
|
// Per-client server bundles and per-client session transports
|
|
819
|
-
d(this, "clientCache", new
|
|
950
|
+
d(this, "clientCache", new E({
|
|
820
951
|
onEvict: (e, t) => {
|
|
821
952
|
this.cleanupBundle(t);
|
|
822
953
|
}
|
|
823
954
|
}));
|
|
824
|
-
this.defaultManager = e, this.createBundle = t, this.sessionContextResolver =
|
|
955
|
+
this.defaultManager = e, this.createBundle = t, this.sessionContextResolver = n, this.baseContext = i, this.options = {
|
|
825
956
|
host: s.host ?? "0.0.0.0",
|
|
826
957
|
port: s.port ?? 3e3,
|
|
827
958
|
basePath: s.basePath ?? "/",
|
|
@@ -829,14 +960,89 @@ class he {
|
|
|
829
960
|
logger: s.logger ?? !1,
|
|
830
961
|
app: s.app,
|
|
831
962
|
customEndpoints: s.customEndpoints
|
|
832
|
-
}, this.configSchema =
|
|
963
|
+
}, this.configSchema = o;
|
|
964
|
+
}
|
|
965
|
+
static builder() {
|
|
966
|
+
let e, t;
|
|
967
|
+
const s = {};
|
|
968
|
+
let o, n, i;
|
|
969
|
+
const a = {
|
|
970
|
+
defaultManager(l) {
|
|
971
|
+
return e = l, a;
|
|
972
|
+
},
|
|
973
|
+
createBundle(l) {
|
|
974
|
+
return t = l, a;
|
|
975
|
+
},
|
|
976
|
+
host(l) {
|
|
977
|
+
return s.host = l, a;
|
|
978
|
+
},
|
|
979
|
+
port(l) {
|
|
980
|
+
return s.port = l, a;
|
|
981
|
+
},
|
|
982
|
+
basePath(l) {
|
|
983
|
+
return s.basePath = l, a;
|
|
984
|
+
},
|
|
985
|
+
cors(l) {
|
|
986
|
+
return s.cors = l, a;
|
|
987
|
+
},
|
|
988
|
+
logger(l) {
|
|
989
|
+
return s.logger = l, a;
|
|
990
|
+
},
|
|
991
|
+
app(l) {
|
|
992
|
+
return s.app = l, a;
|
|
993
|
+
},
|
|
994
|
+
customEndpoints(l) {
|
|
995
|
+
return s.customEndpoints = l, a;
|
|
996
|
+
},
|
|
997
|
+
configSchema(l) {
|
|
998
|
+
return o = l, a;
|
|
999
|
+
},
|
|
1000
|
+
sessionContextResolver(l) {
|
|
1001
|
+
return n = l, a;
|
|
1002
|
+
},
|
|
1003
|
+
baseContext(l) {
|
|
1004
|
+
return i = l, a;
|
|
1005
|
+
},
|
|
1006
|
+
build() {
|
|
1007
|
+
return new O(e, t, s, o, n, i);
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
return a;
|
|
833
1011
|
}
|
|
834
1012
|
async start() {
|
|
835
1013
|
if (this.app) return;
|
|
836
|
-
const e = this.options.app ??
|
|
837
|
-
this.options.cors && await e.register(
|
|
838
|
-
const t = this.
|
|
839
|
-
e.
|
|
1014
|
+
const e = this.options.app ?? U({ logger: this.options.logger });
|
|
1015
|
+
this.options.cors && await e.register(J, { origin: !0 });
|
|
1016
|
+
const t = this.normalizeBasePath(this.options.basePath);
|
|
1017
|
+
this.registerHealthEndpoint(e, t), this.registerToolsEndpoint(e, t), this.registerConfigDiscoveryEndpoint(e, t), this.registerMcpPostEndpoint(e, t), this.registerMcpGetEndpoint(e, t), this.registerMcpDeleteEndpoint(e, t), this.options.customEndpoints && this.options.customEndpoints.length > 0 && Z(e, t, this.options.customEndpoints), this.options.app || await e.listen({ host: this.options.host, port: this.options.port }), this.app = e;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* @param basePath - The base path to normalize
|
|
1021
|
+
* @returns Normalized base path without trailing slash
|
|
1022
|
+
*/
|
|
1023
|
+
normalizeBasePath(e) {
|
|
1024
|
+
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* @param app - Fastify instance
|
|
1028
|
+
* @param base - Base path for routes
|
|
1029
|
+
*/
|
|
1030
|
+
registerHealthEndpoint(e, t) {
|
|
1031
|
+
e.get(`${t}/healthz`, async () => ({ ok: !0 }));
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* @param app - Fastify instance
|
|
1035
|
+
* @param base - Base path for routes
|
|
1036
|
+
*/
|
|
1037
|
+
registerToolsEndpoint(e, t) {
|
|
1038
|
+
e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* @param app - Fastify instance
|
|
1042
|
+
* @param base - Base path for routes
|
|
1043
|
+
*/
|
|
1044
|
+
registerConfigDiscoveryEndpoint(e, t) {
|
|
1045
|
+
e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
840
1046
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
841
1047
|
title: "MCP Session Configuration",
|
|
842
1048
|
description: "Schema for the /mcp endpoint configuration",
|
|
@@ -845,76 +1051,106 @@ class he {
|
|
|
845
1051
|
required: [],
|
|
846
1052
|
"x-mcp-version": "1.0",
|
|
847
1053
|
"x-query-style": "dot+bracket"
|
|
848
|
-
}))
|
|
1054
|
+
}));
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* @param app - Fastify instance
|
|
1058
|
+
* @param base - Base path for routes
|
|
1059
|
+
*/
|
|
1060
|
+
registerMcpPostEndpoint(e, t) {
|
|
1061
|
+
e.post(
|
|
849
1062
|
`${t}/mcp`,
|
|
850
|
-
async (s,
|
|
851
|
-
const
|
|
1063
|
+
async (s, o) => {
|
|
1064
|
+
const n = we.safeParse(
|
|
1065
|
+
s.headers["mcp-client-id"]
|
|
1066
|
+
);
|
|
1067
|
+
if (!n.success)
|
|
1068
|
+
return o.code(400), {
|
|
1069
|
+
jsonrpc: "2.0",
|
|
1070
|
+
error: { code: -32600, message: n.error.issues[0].message },
|
|
1071
|
+
id: null
|
|
1072
|
+
};
|
|
1073
|
+
const i = n.data, { cacheKey: a, mergedContext: l } = this.resolveSessionContext(
|
|
852
1074
|
s,
|
|
853
|
-
|
|
1075
|
+
i
|
|
854
1076
|
);
|
|
855
|
-
let
|
|
856
|
-
if (!
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
server:
|
|
860
|
-
orchestrator:
|
|
1077
|
+
let c = this.clientCache.get(a);
|
|
1078
|
+
if (!c) {
|
|
1079
|
+
const v = this.createBundle(l);
|
|
1080
|
+
c = {
|
|
1081
|
+
server: v.server,
|
|
1082
|
+
orchestrator: v.orchestrator,
|
|
861
1083
|
sessions: /* @__PURE__ */ new Map()
|
|
862
|
-
},
|
|
1084
|
+
}, this.clientCache.set(a, c);
|
|
863
1085
|
}
|
|
864
|
-
const
|
|
865
|
-
let
|
|
866
|
-
if (
|
|
867
|
-
|
|
868
|
-
else if (!
|
|
869
|
-
const
|
|
870
|
-
|
|
871
|
-
sessionIdGenerator: () =>
|
|
872
|
-
onsessioninitialized: (
|
|
873
|
-
|
|
1086
|
+
const u = s.headers["mcp-session-id"];
|
|
1087
|
+
let f;
|
|
1088
|
+
if (u && c.sessions.get(u))
|
|
1089
|
+
f = c.sessions.get(u);
|
|
1090
|
+
else if (!u && G(s.body)) {
|
|
1091
|
+
const v = S();
|
|
1092
|
+
f = new Q({
|
|
1093
|
+
sessionIdGenerator: () => v,
|
|
1094
|
+
onsessioninitialized: (b) => {
|
|
1095
|
+
c.sessions.set(b, f);
|
|
874
1096
|
}
|
|
875
1097
|
});
|
|
876
1098
|
try {
|
|
877
|
-
await
|
|
1099
|
+
await c.server.connect(f);
|
|
878
1100
|
} catch {
|
|
879
|
-
return
|
|
1101
|
+
return o.code(500), {
|
|
880
1102
|
jsonrpc: "2.0",
|
|
881
1103
|
error: { code: -32603, message: "Error initializing server." },
|
|
882
1104
|
id: null
|
|
883
1105
|
};
|
|
884
1106
|
}
|
|
885
|
-
|
|
886
|
-
|
|
1107
|
+
f.onclose = () => {
|
|
1108
|
+
f?.sessionId && c.sessions.delete(f.sessionId);
|
|
887
1109
|
};
|
|
888
1110
|
} else
|
|
889
|
-
return
|
|
1111
|
+
return o.code(400), {
|
|
890
1112
|
jsonrpc: "2.0",
|
|
891
1113
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
892
1114
|
id: null
|
|
893
1115
|
};
|
|
894
|
-
return await
|
|
1116
|
+
return await f.handleRequest(
|
|
895
1117
|
s.raw,
|
|
896
|
-
|
|
1118
|
+
o.raw,
|
|
897
1119
|
s.body
|
|
898
|
-
),
|
|
1120
|
+
), o;
|
|
899
1121
|
}
|
|
900
|
-
)
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* @param app - Fastify instance
|
|
1126
|
+
* @param base - Base path for routes
|
|
1127
|
+
*/
|
|
1128
|
+
registerMcpGetEndpoint(e, t) {
|
|
1129
|
+
e.get(`${t}/mcp`, async (s, o) => {
|
|
1130
|
+
const n = s.headers["mcp-client-id"]?.trim(), i = n && n.length > 0 ? n : "";
|
|
1131
|
+
if (!i)
|
|
1132
|
+
return o.code(400), "Missing mcp-client-id";
|
|
1133
|
+
const { cacheKey: a } = this.resolveSessionContext(s, i), l = this.clientCache.get(a);
|
|
905
1134
|
if (!l)
|
|
906
|
-
return
|
|
1135
|
+
return o.code(400), "Invalid or expired client";
|
|
907
1136
|
const c = s.headers["mcp-session-id"];
|
|
908
1137
|
if (!c)
|
|
909
|
-
return
|
|
910
|
-
const
|
|
911
|
-
return
|
|
912
|
-
})
|
|
1138
|
+
return o.code(400), "Missing mcp-session-id";
|
|
1139
|
+
const u = l.sessions.get(c);
|
|
1140
|
+
return u ? (await u.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* @param app - Fastify instance
|
|
1145
|
+
* @param base - Base path for routes
|
|
1146
|
+
*/
|
|
1147
|
+
registerMcpDeleteEndpoint(e, t) {
|
|
1148
|
+
e.delete(
|
|
913
1149
|
`${t}/mcp`,
|
|
914
|
-
async (s,
|
|
915
|
-
const
|
|
916
|
-
if (!
|
|
917
|
-
return
|
|
1150
|
+
async (s, o) => {
|
|
1151
|
+
const n = s.headers["mcp-client-id"]?.trim(), i = n && n.length > 0 ? n : "", a = s.headers["mcp-session-id"];
|
|
1152
|
+
if (!i || !a)
|
|
1153
|
+
return o.code(400), {
|
|
918
1154
|
jsonrpc: "2.0",
|
|
919
1155
|
error: {
|
|
920
1156
|
code: -32600,
|
|
@@ -922,59 +1158,47 @@ class he {
|
|
|
922
1158
|
},
|
|
923
1159
|
id: null
|
|
924
1160
|
};
|
|
925
|
-
const c = this.clientCache.get(
|
|
926
|
-
if (!c || !
|
|
927
|
-
return
|
|
1161
|
+
const { cacheKey: l } = this.resolveSessionContext(s, i), c = this.clientCache.get(l), u = c?.sessions.get(a);
|
|
1162
|
+
if (!c || !u)
|
|
1163
|
+
return o.code(404), {
|
|
928
1164
|
jsonrpc: "2.0",
|
|
929
1165
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
930
1166
|
id: null
|
|
931
1167
|
};
|
|
932
1168
|
try {
|
|
933
|
-
if (typeof
|
|
1169
|
+
if (typeof u.close == "function")
|
|
934
1170
|
try {
|
|
935
|
-
await
|
|
1171
|
+
await u.close();
|
|
936
1172
|
} catch {
|
|
937
1173
|
}
|
|
938
1174
|
} finally {
|
|
939
|
-
|
|
1175
|
+
u?.sessionId ? c.sessions.delete(u.sessionId) : c.sessions.delete(a);
|
|
940
1176
|
}
|
|
941
|
-
return
|
|
1177
|
+
return o.code(204).send(), o;
|
|
942
1178
|
}
|
|
943
|
-
)
|
|
1179
|
+
);
|
|
944
1180
|
}
|
|
945
|
-
/**
|
|
946
|
-
* Stops the Fastify server and cleans up all resources.
|
|
947
|
-
* Closes all client sessions and clears the cache.
|
|
948
|
-
*/
|
|
949
1181
|
async stop() {
|
|
950
1182
|
this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
|
|
951
1183
|
}
|
|
952
1184
|
/**
|
|
953
|
-
* Cleans up resources associated with a client bundle.
|
|
954
|
-
* Closes all sessions within the bundle.
|
|
955
1185
|
* @param bundle - The client bundle to clean up
|
|
956
|
-
* @private
|
|
957
1186
|
*/
|
|
958
1187
|
cleanupBundle(e) {
|
|
959
1188
|
for (const [t, s] of e.sessions.entries())
|
|
960
1189
|
try {
|
|
961
|
-
typeof s.close == "function" && s.close().catch((
|
|
962
|
-
console.warn(`Error closing session ${t}:`,
|
|
1190
|
+
typeof s.close == "function" && s.close().catch((o) => {
|
|
1191
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
963
1192
|
});
|
|
964
|
-
} catch (
|
|
965
|
-
console.warn(`Error closing session ${t}:`,
|
|
1193
|
+
} catch (o) {
|
|
1194
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
966
1195
|
}
|
|
967
1196
|
e.sessions.clear();
|
|
968
1197
|
}
|
|
969
1198
|
/**
|
|
970
|
-
* Resolves the session context and generates a cache key for the request.
|
|
971
|
-
* If a session context resolver is configured, it extracts query parameters
|
|
972
|
-
* and merges session-specific context with the base context.
|
|
973
|
-
*
|
|
974
1199
|
* @param req - The Fastify request
|
|
975
1200
|
* @param clientId - The client identifier
|
|
976
1201
|
* @returns Object with cache key and merged context
|
|
977
|
-
* @private
|
|
978
1202
|
*/
|
|
979
1203
|
resolveSessionContext(e, t) {
|
|
980
1204
|
if (!this.sessionContextResolver)
|
|
@@ -986,45 +1210,38 @@ class he {
|
|
|
986
1210
|
clientId: t,
|
|
987
1211
|
headers: this.extractHeaders(e),
|
|
988
1212
|
query: this.extractQuery(e)
|
|
989
|
-
},
|
|
1213
|
+
}, o = this.sessionContextResolver.resolve(
|
|
990
1214
|
s,
|
|
991
1215
|
this.baseContext
|
|
992
1216
|
);
|
|
993
1217
|
return {
|
|
994
|
-
cacheKey:
|
|
995
|
-
mergedContext:
|
|
1218
|
+
cacheKey: o.cacheKeySuffix === "default" ? t : `${t}:${o.cacheKeySuffix}`,
|
|
1219
|
+
mergedContext: o.context
|
|
996
1220
|
};
|
|
997
1221
|
}
|
|
998
1222
|
/**
|
|
999
|
-
* Extracts headers from a Fastify request as a Record.
|
|
1000
|
-
* Normalizes header names to lowercase.
|
|
1001
|
-
*
|
|
1002
1223
|
* @param req - The Fastify request
|
|
1003
1224
|
* @returns Headers as a string record
|
|
1004
|
-
* @private
|
|
1005
1225
|
*/
|
|
1006
1226
|
extractHeaders(e) {
|
|
1007
1227
|
const t = {};
|
|
1008
|
-
for (const [s,
|
|
1009
|
-
typeof
|
|
1228
|
+
for (const [s, o] of Object.entries(e.headers))
|
|
1229
|
+
typeof o == "string" ? t[s.toLowerCase()] = o : Array.isArray(o) && o.length > 0 && (t[s.toLowerCase()] = o[0]);
|
|
1010
1230
|
return t;
|
|
1011
1231
|
}
|
|
1012
1232
|
/**
|
|
1013
|
-
* Extracts query parameters from a Fastify request as a Record.
|
|
1014
|
-
*
|
|
1015
1233
|
* @param req - The Fastify request
|
|
1016
1234
|
* @returns Query parameters as a string record
|
|
1017
|
-
* @private
|
|
1018
1235
|
*/
|
|
1019
1236
|
extractQuery(e) {
|
|
1020
1237
|
const t = {}, s = e.query;
|
|
1021
1238
|
if (s && typeof s == "object")
|
|
1022
|
-
for (const [
|
|
1023
|
-
typeof
|
|
1239
|
+
for (const [o, n] of Object.entries(s))
|
|
1240
|
+
typeof n == "string" && (t[o] = n);
|
|
1024
1241
|
return t;
|
|
1025
1242
|
}
|
|
1026
1243
|
}
|
|
1027
|
-
class
|
|
1244
|
+
class k {
|
|
1028
1245
|
constructor(e) {
|
|
1029
1246
|
d(this, "config");
|
|
1030
1247
|
d(this, "queryParamName");
|
|
@@ -1033,9 +1250,27 @@ class fe {
|
|
|
1033
1250
|
d(this, "mergeStrategy");
|
|
1034
1251
|
this.config = e, this.queryParamName = e.queryParam?.name ?? "config", this.encoding = e.queryParam?.encoding ?? "base64", this.allowedKeys = e.queryParam?.allowedKeys ? new Set(e.queryParam.allowedKeys) : null, this.mergeStrategy = e.merge ?? "shallow";
|
|
1035
1252
|
}
|
|
1253
|
+
static builder() {
|
|
1254
|
+
const e = {}, t = {
|
|
1255
|
+
enabled(s) {
|
|
1256
|
+
return e.enabled = s, t;
|
|
1257
|
+
},
|
|
1258
|
+
queryParam(s) {
|
|
1259
|
+
return e.queryParam = s, t;
|
|
1260
|
+
},
|
|
1261
|
+
contextResolver(s) {
|
|
1262
|
+
return e.contextResolver = s, t;
|
|
1263
|
+
},
|
|
1264
|
+
merge(s) {
|
|
1265
|
+
return e.merge = s, t;
|
|
1266
|
+
},
|
|
1267
|
+
build() {
|
|
1268
|
+
return new k(e);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
return t;
|
|
1272
|
+
}
|
|
1036
1273
|
/**
|
|
1037
|
-
* Resolves the session context for a request.
|
|
1038
|
-
*
|
|
1039
1274
|
* @param request - The request context (clientId, headers, query)
|
|
1040
1275
|
* @param baseContext - The base context from server configuration
|
|
1041
1276
|
* @returns The resolved context and cache key suffix
|
|
@@ -1047,34 +1282,48 @@ class fe {
|
|
|
1047
1282
|
cacheKeySuffix: "default"
|
|
1048
1283
|
};
|
|
1049
1284
|
const s = this.parseQueryConfig(e.query);
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1285
|
+
return this.config.contextResolver ? this.resolveWithCustomResolver(e, t, s) : this.resolveWithDefaultMerge(t, s);
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* @param request - The request context
|
|
1289
|
+
* @param baseContext - The base context from server configuration
|
|
1290
|
+
* @param parsedConfig - The parsed query parameter config
|
|
1291
|
+
* @returns The resolved context and cache key suffix
|
|
1292
|
+
*/
|
|
1293
|
+
resolveWithCustomResolver(e, t, s) {
|
|
1294
|
+
const o = this.config.contextResolver;
|
|
1295
|
+
if (!o)
|
|
1296
|
+
return { context: t, cacheKeySuffix: "default" };
|
|
1297
|
+
try {
|
|
1298
|
+
return {
|
|
1299
|
+
context: o(
|
|
1300
|
+
e,
|
|
1301
|
+
t,
|
|
1302
|
+
s
|
|
1303
|
+
),
|
|
1304
|
+
cacheKeySuffix: this.generateCacheKeySuffix(s)
|
|
1305
|
+
};
|
|
1306
|
+
} catch {
|
|
1307
|
+
return {
|
|
1308
|
+
context: t,
|
|
1309
|
+
cacheKeySuffix: "default"
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* @param baseContext - The base context from server configuration
|
|
1315
|
+
* @param parsedConfig - The parsed query parameter config
|
|
1316
|
+
* @returns The merged context and cache key suffix
|
|
1317
|
+
*/
|
|
1318
|
+
resolveWithDefaultMerge(e, t) {
|
|
1066
1319
|
return {
|
|
1067
|
-
context: this.mergeContexts(
|
|
1068
|
-
cacheKeySuffix: this.generateCacheKeySuffix(
|
|
1320
|
+
context: this.mergeContexts(e, t),
|
|
1321
|
+
cacheKeySuffix: this.generateCacheKeySuffix(t)
|
|
1069
1322
|
};
|
|
1070
1323
|
}
|
|
1071
1324
|
/**
|
|
1072
|
-
* Parses the session config from query parameters.
|
|
1073
|
-
* Returns empty object on parse failure (fail secure).
|
|
1074
|
-
*
|
|
1075
1325
|
* @param query - Query parameters from the request
|
|
1076
1326
|
* @returns Parsed and filtered config object
|
|
1077
|
-
* @private
|
|
1078
1327
|
*/
|
|
1079
1328
|
parseQueryConfig(e) {
|
|
1080
1329
|
const t = e[this.queryParamName];
|
|
@@ -1083,19 +1332,15 @@ class fe {
|
|
|
1083
1332
|
try {
|
|
1084
1333
|
let s;
|
|
1085
1334
|
this.encoding === "base64" ? s = Buffer.from(t, "base64").toString("utf-8") : s = t;
|
|
1086
|
-
const
|
|
1087
|
-
return typeof
|
|
1335
|
+
const o = JSON.parse(s);
|
|
1336
|
+
return typeof o != "object" || o === null || Array.isArray(o) ? {} : this.filterAllowedKeys(o);
|
|
1088
1337
|
} catch {
|
|
1089
1338
|
return {};
|
|
1090
1339
|
}
|
|
1091
1340
|
}
|
|
1092
1341
|
/**
|
|
1093
|
-
* Filters the parsed config to only include allowed keys.
|
|
1094
|
-
* If no allowedKeys whitelist is configured, returns the full object.
|
|
1095
|
-
*
|
|
1096
1342
|
* @param parsed - The parsed config object
|
|
1097
1343
|
* @returns Filtered config with only allowed keys
|
|
1098
|
-
* @private
|
|
1099
1344
|
*/
|
|
1100
1345
|
filterAllowedKeys(e) {
|
|
1101
1346
|
if (!this.allowedKeys)
|
|
@@ -1106,12 +1351,9 @@ class fe {
|
|
|
1106
1351
|
return t;
|
|
1107
1352
|
}
|
|
1108
1353
|
/**
|
|
1109
|
-
* Merges the base context with the session config.
|
|
1110
|
-
*
|
|
1111
1354
|
* @param baseContext - The base context from server configuration
|
|
1112
1355
|
* @param sessionConfig - The parsed session config
|
|
1113
1356
|
* @returns Merged context
|
|
1114
|
-
* @private
|
|
1115
1357
|
*/
|
|
1116
1358
|
mergeContexts(e, t) {
|
|
1117
1359
|
return Object.keys(t).length === 0 ? e : typeof e != "object" || e === null || Array.isArray(e) ? t : this.mergeStrategy === "deep" ? this.deepMerge(
|
|
@@ -1123,73 +1365,65 @@ class fe {
|
|
|
1123
1365
|
};
|
|
1124
1366
|
}
|
|
1125
1367
|
/**
|
|
1126
|
-
* Performs a deep merge of two objects.
|
|
1127
|
-
* Session config values override base context values.
|
|
1128
|
-
*
|
|
1129
1368
|
* @param base - The base object
|
|
1130
1369
|
* @param override - The override object
|
|
1131
1370
|
* @returns Deep merged object
|
|
1132
|
-
* @private
|
|
1133
1371
|
*/
|
|
1134
1372
|
deepMerge(e, t) {
|
|
1135
1373
|
const s = { ...e };
|
|
1136
|
-
for (const [
|
|
1137
|
-
const
|
|
1138
|
-
typeof
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
) : s[
|
|
1374
|
+
for (const [o, n] of Object.entries(t)) {
|
|
1375
|
+
const i = s[o];
|
|
1376
|
+
typeof n == "object" && n !== null && !Array.isArray(n) && typeof i == "object" && i !== null && !Array.isArray(i) ? s[o] = this.deepMerge(
|
|
1377
|
+
i,
|
|
1378
|
+
n
|
|
1379
|
+
) : s[o] = n;
|
|
1142
1380
|
}
|
|
1143
1381
|
return s;
|
|
1144
1382
|
}
|
|
1145
1383
|
/**
|
|
1146
|
-
* Generates a deterministic cache key suffix based on the session config.
|
|
1147
|
-
* Returns 'default' when no session config is present.
|
|
1148
|
-
*
|
|
1149
1384
|
* @param sessionConfig - The parsed session config
|
|
1150
1385
|
* @returns Hash string or 'default'
|
|
1151
|
-
* @private
|
|
1152
1386
|
*/
|
|
1153
1387
|
generateCacheKeySuffix(e) {
|
|
1154
1388
|
if (Object.keys(e).length === 0)
|
|
1155
1389
|
return "default";
|
|
1156
1390
|
const t = Object.keys(e).sort(), s = {};
|
|
1157
|
-
for (const
|
|
1158
|
-
s[
|
|
1159
|
-
const
|
|
1160
|
-
return
|
|
1391
|
+
for (const n of t)
|
|
1392
|
+
s[n] = e[n];
|
|
1393
|
+
const o = JSON.stringify(s);
|
|
1394
|
+
return be("sha256").update(o).digest("hex").slice(0, 16);
|
|
1161
1395
|
}
|
|
1162
1396
|
}
|
|
1163
|
-
function
|
|
1164
|
-
|
|
1397
|
+
function X(r) {
|
|
1398
|
+
xe(r), Se(r), Ee(r), Ce(r), Ae(r);
|
|
1165
1399
|
}
|
|
1166
|
-
function
|
|
1167
|
-
if (!
|
|
1400
|
+
function xe(r) {
|
|
1401
|
+
if (!r || typeof r != "object")
|
|
1168
1402
|
throw new Error(
|
|
1169
1403
|
"Session context configuration must be an object"
|
|
1170
1404
|
);
|
|
1171
1405
|
}
|
|
1172
|
-
function
|
|
1173
|
-
if (
|
|
1406
|
+
function Se(r) {
|
|
1407
|
+
if (r.enabled !== void 0 && typeof r.enabled != "boolean")
|
|
1174
1408
|
throw new Error(
|
|
1175
|
-
`enabled must be a boolean, got ${typeof
|
|
1409
|
+
`enabled must be a boolean, got ${typeof r.enabled}`
|
|
1176
1410
|
);
|
|
1177
1411
|
}
|
|
1178
|
-
function
|
|
1179
|
-
if (
|
|
1180
|
-
if (typeof
|
|
1412
|
+
function Ee(r) {
|
|
1413
|
+
if (r.queryParam !== void 0) {
|
|
1414
|
+
if (typeof r.queryParam != "object" || r.queryParam === null)
|
|
1181
1415
|
throw new Error("queryParam must be an object");
|
|
1182
|
-
if (
|
|
1416
|
+
if (r.queryParam.name !== void 0 && (typeof r.queryParam.name != "string" || r.queryParam.name.length === 0))
|
|
1183
1417
|
throw new Error("queryParam.name must be a non-empty string");
|
|
1184
|
-
if (
|
|
1418
|
+
if (r.queryParam.encoding !== void 0 && r.queryParam.encoding !== "base64" && r.queryParam.encoding !== "json")
|
|
1185
1419
|
throw new Error(
|
|
1186
|
-
`Invalid queryParam.encoding: "${
|
|
1420
|
+
`Invalid queryParam.encoding: "${r.queryParam.encoding}". Must be "base64" or "json"`
|
|
1187
1421
|
);
|
|
1188
|
-
if (
|
|
1189
|
-
if (!Array.isArray(
|
|
1422
|
+
if (r.queryParam.allowedKeys !== void 0) {
|
|
1423
|
+
if (!Array.isArray(r.queryParam.allowedKeys))
|
|
1190
1424
|
throw new Error("queryParam.allowedKeys must be an array of strings");
|
|
1191
|
-
for (let e = 0; e <
|
|
1192
|
-
const t =
|
|
1425
|
+
for (let e = 0; e < r.queryParam.allowedKeys.length; e++) {
|
|
1426
|
+
const t = r.queryParam.allowedKeys[e];
|
|
1193
1427
|
if (typeof t != "string" || t.length === 0)
|
|
1194
1428
|
throw new Error(
|
|
1195
1429
|
`queryParam.allowedKeys[${e}] must be a non-empty string`
|
|
@@ -1198,172 +1432,244 @@ function ge(o) {
|
|
|
1198
1432
|
}
|
|
1199
1433
|
}
|
|
1200
1434
|
}
|
|
1201
|
-
function
|
|
1202
|
-
if (
|
|
1435
|
+
function Ce(r) {
|
|
1436
|
+
if (r.contextResolver !== void 0 && typeof r.contextResolver != "function")
|
|
1203
1437
|
throw new Error(
|
|
1204
1438
|
"contextResolver must be a function: (request, baseContext, parsedQueryConfig?) => unknown"
|
|
1205
1439
|
);
|
|
1206
1440
|
}
|
|
1207
|
-
function
|
|
1208
|
-
if (
|
|
1441
|
+
function Ae(r) {
|
|
1442
|
+
if (r.merge !== void 0 && r.merge !== "shallow" && r.merge !== "deep")
|
|
1209
1443
|
throw new Error(
|
|
1210
|
-
`Invalid merge strategy: "${
|
|
1444
|
+
`Invalid merge strategy: "${r.merge}". Must be "shallow" or "deep"`
|
|
1211
1445
|
);
|
|
1212
1446
|
}
|
|
1213
|
-
const
|
|
1214
|
-
mode:
|
|
1215
|
-
toolsets:
|
|
1447
|
+
const Pe = g.object({
|
|
1448
|
+
mode: g.enum(["DYNAMIC", "STATIC"]).optional(),
|
|
1449
|
+
toolsets: g.union([g.array(g.string()), g.literal("ALL")]).optional()
|
|
1216
1450
|
}).strict();
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
${JSON.stringify(u, null, 2)}
|
|
1451
|
+
function Me(r) {
|
|
1452
|
+
try {
|
|
1453
|
+
Pe.parse(r);
|
|
1454
|
+
} catch (e) {
|
|
1455
|
+
if (e instanceof g.ZodError) {
|
|
1456
|
+
const t = e.format();
|
|
1457
|
+
throw new Error(
|
|
1458
|
+
`Invalid startup configuration:
|
|
1459
|
+
${JSON.stringify(t, null, 2)}
|
|
1227
1460
|
|
|
1228
1461
|
Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
throw a;
|
|
1462
|
+
);
|
|
1232
1463
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
)
|
|
1238
|
-
|
|
1239
|
-
const s = o.createServer(), r = (a) => typeof a?.server?.notification == "function", i = (a) => typeof a?.notifyToolsListChanged == "function", n = async (a) => {
|
|
1464
|
+
throw e;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
function Ie() {
|
|
1468
|
+
const r = (t) => typeof t?.server?.notification == "function", e = (t) => typeof t?.notifyToolsListChanged == "function";
|
|
1469
|
+
return async (t) => {
|
|
1240
1470
|
try {
|
|
1241
|
-
if (r(
|
|
1242
|
-
await
|
|
1471
|
+
if (r(t)) {
|
|
1472
|
+
await t.server.notification({
|
|
1243
1473
|
method: "notifications/tools/list_changed"
|
|
1244
1474
|
});
|
|
1245
1475
|
return;
|
|
1246
1476
|
}
|
|
1247
|
-
|
|
1248
|
-
} catch (
|
|
1249
|
-
if ((
|
|
1477
|
+
e(t) && await t.notifyToolsListChanged();
|
|
1478
|
+
} catch (s) {
|
|
1479
|
+
if ((s instanceof Error ? s.message : String(s)) === "Not connected")
|
|
1250
1480
|
return;
|
|
1251
|
-
console.warn("Failed to send tools list changed notification:",
|
|
1481
|
+
console.warn("Failed to send tools list changed notification:", s);
|
|
1252
1482
|
}
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
const c = new he(
|
|
1265
|
-
l.getManager(),
|
|
1266
|
-
(a) => {
|
|
1267
|
-
const u = a ?? o.context;
|
|
1268
|
-
if (e === "STATIC")
|
|
1269
|
-
return { server: s, orchestrator: l };
|
|
1270
|
-
const h = o.createServer(), g = new C({
|
|
1271
|
-
server: h,
|
|
1272
|
-
catalog: o.catalog,
|
|
1273
|
-
moduleLoaders: o.moduleLoaders,
|
|
1274
|
-
exposurePolicy: o.exposurePolicy,
|
|
1275
|
-
context: u,
|
|
1276
|
-
notifyToolsListChanged: async () => n(h),
|
|
1277
|
-
startup: o.startup,
|
|
1278
|
-
registerMetaTools: o.registerMetaTools !== void 0 ? o.registerMetaTools : e === "DYNAMIC"
|
|
1279
|
-
});
|
|
1280
|
-
return { server: h, orchestrator: g };
|
|
1281
|
-
},
|
|
1282
|
-
o.http,
|
|
1283
|
-
o.configSchema,
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function $e(r, e) {
|
|
1486
|
+
return r !== void 0 ? r : e === "DYNAMIC";
|
|
1487
|
+
}
|
|
1488
|
+
async function rt(r) {
|
|
1489
|
+
Re(r);
|
|
1490
|
+
const e = r.startup?.mode ?? "DYNAMIC", t = $e(r.registerMetaTools, e), s = Le(r, e), o = Ie(), n = r.createServer(), i = ee(
|
|
1491
|
+
n,
|
|
1492
|
+
r,
|
|
1493
|
+
e,
|
|
1284
1494
|
t,
|
|
1285
|
-
o
|
|
1495
|
+
o
|
|
1496
|
+
);
|
|
1497
|
+
e === "STATIC" && await i.ensureReady();
|
|
1498
|
+
const a = je(
|
|
1499
|
+
r,
|
|
1500
|
+
e,
|
|
1501
|
+
n,
|
|
1502
|
+
i,
|
|
1503
|
+
t,
|
|
1504
|
+
o
|
|
1505
|
+
), l = Ne(
|
|
1506
|
+
r,
|
|
1507
|
+
i.getManager(),
|
|
1508
|
+
a,
|
|
1509
|
+
s
|
|
1286
1510
|
);
|
|
1287
1511
|
return {
|
|
1288
|
-
server:
|
|
1289
|
-
start:
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1512
|
+
server: n,
|
|
1513
|
+
start: () => l.start(),
|
|
1514
|
+
close: () => l.stop()
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
function Re(r) {
|
|
1518
|
+
if (r.startup && Me(r.startup), typeof r.createServer != "function")
|
|
1519
|
+
throw new Error("createMcpServer: `createServer` (factory) is required");
|
|
1520
|
+
}
|
|
1521
|
+
function Le(r, e) {
|
|
1522
|
+
if (!r.sessionContext) return;
|
|
1523
|
+
X(r.sessionContext);
|
|
1524
|
+
const t = k.builder().enabled(r.sessionContext.enabled ?? !0).queryParam(r.sessionContext.queryParam).contextResolver(r.sessionContext.contextResolver).merge(r.sessionContext.merge ?? "shallow").build();
|
|
1525
|
+
return e === "STATIC" && r.sessionContext.enabled !== !1 && console.warn(
|
|
1526
|
+
"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."
|
|
1527
|
+
), t;
|
|
1528
|
+
}
|
|
1529
|
+
function ee(r, e, t, s, o, n) {
|
|
1530
|
+
const i = A.builder().server(r).catalog(e.catalog).moduleLoaders(e.moduleLoaders ?? {}).context(n !== void 0 ? n : e.context).notifyToolsListChanged(async () => o(r)).registerMetaTools(s);
|
|
1531
|
+
return e.exposurePolicy && i.exposurePolicy(e.exposurePolicy), e.startup && i.startup(e.startup), i.build();
|
|
1532
|
+
}
|
|
1533
|
+
function je(r, e, t, s, o, n) {
|
|
1534
|
+
return (i) => {
|
|
1535
|
+
if (e === "STATIC")
|
|
1536
|
+
return { server: t, orchestrator: s };
|
|
1537
|
+
const a = i ?? r.context, l = r.createServer(), c = ee(
|
|
1538
|
+
l,
|
|
1539
|
+
r,
|
|
1540
|
+
e,
|
|
1541
|
+
o,
|
|
1542
|
+
n,
|
|
1543
|
+
a
|
|
1544
|
+
);
|
|
1545
|
+
return { server: l, orchestrator: c };
|
|
1295
1546
|
};
|
|
1296
1547
|
}
|
|
1297
|
-
function
|
|
1298
|
-
|
|
1548
|
+
function Ne(r, e, t, s) {
|
|
1549
|
+
const o = O.builder().defaultManager(e).createBundle(t).host(r.http?.host ?? "0.0.0.0").port(r.http?.port ?? 3e3).basePath(r.http?.basePath ?? "/").cors(r.http?.cors ?? !0).logger(r.http?.logger ?? !1);
|
|
1550
|
+
return r.http?.app && o.app(r.http.app), r.http?.customEndpoints && o.customEndpoints(r.http.customEndpoints), r.configSchema && o.configSchema(r.configSchema), s && o.sessionContextResolver(s), r.context !== void 0 && o.baseContext(r.context), o.build();
|
|
1551
|
+
}
|
|
1552
|
+
function Oe(r) {
|
|
1553
|
+
ke(r), De(r), ze(r), qe(r);
|
|
1299
1554
|
}
|
|
1300
|
-
function
|
|
1301
|
-
if (!
|
|
1555
|
+
function ke(r) {
|
|
1556
|
+
if (!r || typeof r != "object")
|
|
1302
1557
|
throw new Error(
|
|
1303
1558
|
"Permission configuration is required for createPermissionBasedMcpServer"
|
|
1304
1559
|
);
|
|
1305
1560
|
}
|
|
1306
|
-
function
|
|
1307
|
-
if (!
|
|
1561
|
+
function De(r) {
|
|
1562
|
+
if (!r.source)
|
|
1308
1563
|
throw new Error('Permission source must be either "headers" or "config"');
|
|
1309
|
-
if (
|
|
1564
|
+
if (r.source !== "headers" && r.source !== "config")
|
|
1310
1565
|
throw new Error(
|
|
1311
|
-
`Invalid permission source: "${
|
|
1566
|
+
`Invalid permission source: "${r.source}". Must be either "headers" or "config"`
|
|
1312
1567
|
);
|
|
1313
1568
|
}
|
|
1314
|
-
function
|
|
1315
|
-
if (
|
|
1569
|
+
function ze(r) {
|
|
1570
|
+
if (r.source === "config" && !r.staticMap && !r.resolver)
|
|
1316
1571
|
throw new Error(
|
|
1317
1572
|
"Config-based permissions require at least one of: staticMap or resolver function"
|
|
1318
1573
|
);
|
|
1319
1574
|
}
|
|
1320
|
-
function
|
|
1321
|
-
if (
|
|
1322
|
-
if (typeof
|
|
1575
|
+
function qe(r) {
|
|
1576
|
+
if (r.staticMap !== void 0) {
|
|
1577
|
+
if (typeof r.staticMap != "object" || r.staticMap === null)
|
|
1323
1578
|
throw new Error(
|
|
1324
1579
|
"staticMap must be an object mapping client IDs to toolset arrays"
|
|
1325
1580
|
);
|
|
1326
|
-
|
|
1581
|
+
Fe(r.staticMap);
|
|
1327
1582
|
}
|
|
1328
|
-
if (
|
|
1583
|
+
if (r.resolver !== void 0 && typeof r.resolver != "function")
|
|
1329
1584
|
throw new Error(
|
|
1330
1585
|
"resolver must be a synchronous function: (clientId: string) => string[]"
|
|
1331
1586
|
);
|
|
1332
|
-
if (
|
|
1587
|
+
if (r.defaultPermissions !== void 0 && !Array.isArray(r.defaultPermissions))
|
|
1333
1588
|
throw new Error("defaultPermissions must be an array of toolset names");
|
|
1334
|
-
if (
|
|
1589
|
+
if (r.headerName !== void 0 && (typeof r.headerName != "string" || r.headerName.length === 0))
|
|
1335
1590
|
throw new Error("headerName must be a non-empty string");
|
|
1336
1591
|
}
|
|
1337
|
-
function
|
|
1338
|
-
for (const [e, t] of Object.entries(
|
|
1592
|
+
function Fe(r) {
|
|
1593
|
+
for (const [e, t] of Object.entries(r))
|
|
1339
1594
|
if (!Array.isArray(t))
|
|
1340
1595
|
throw new Error(
|
|
1341
1596
|
`staticMap value for client "${e}" must be an array of toolset names`
|
|
1342
1597
|
);
|
|
1343
1598
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1599
|
+
function _e(r, e) {
|
|
1600
|
+
return async (t) => {
|
|
1601
|
+
const s = e.resolvePermissions(
|
|
1602
|
+
t.clientId,
|
|
1603
|
+
t.headers
|
|
1604
|
+
), o = r(s), n = o.orchestrator.getManager(), i = [], a = [];
|
|
1605
|
+
if (s.length > 0) {
|
|
1606
|
+
const l = await n.enableToolsets(s);
|
|
1607
|
+
for (const c of l.results)
|
|
1608
|
+
c.success ? i.push(c.name) : (a.push(c.name), console.warn(
|
|
1609
|
+
`Failed to enable toolset '${c.name}' for client '${t.clientId}': ${c.message}`
|
|
1610
|
+
));
|
|
1611
|
+
if (i.length === 0 && a.length > 0)
|
|
1612
|
+
throw new Error(
|
|
1613
|
+
`All requested toolsets failed to enable for client '${t.clientId}'. Requested: [${s.join(", ")}]. Check that toolset names in permissions match the catalog.`
|
|
1614
|
+
);
|
|
1615
|
+
}
|
|
1616
|
+
return {
|
|
1617
|
+
server: o.server,
|
|
1618
|
+
orchestrator: o.orchestrator,
|
|
1619
|
+
allowedToolsets: i,
|
|
1620
|
+
failedToolsets: a
|
|
1621
|
+
};
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
function Ke(r) {
|
|
1625
|
+
if (!r) return;
|
|
1626
|
+
const e = {
|
|
1627
|
+
namespaceToolsWithSetKey: r.namespaceToolsWithSetKey
|
|
1628
|
+
};
|
|
1629
|
+
return r.allowlist !== void 0 && console.warn(
|
|
1630
|
+
"Permission-based servers: exposurePolicy.allowlist is ignored. Allowed toolsets are determined by client permissions."
|
|
1631
|
+
), r.denylist !== void 0 && console.warn(
|
|
1632
|
+
"Permission-based servers: exposurePolicy.denylist is ignored. Use permission configuration to control toolset access."
|
|
1633
|
+
), r.maxActiveToolsets !== void 0 && console.warn(
|
|
1634
|
+
"Permission-based servers: exposurePolicy.maxActiveToolsets is ignored. Toolset count is determined by client permissions."
|
|
1635
|
+
), r.onLimitExceeded !== void 0 && console.warn(
|
|
1636
|
+
"Permission-based servers: exposurePolicy.onLimitExceeded is ignored. No toolset limits are enforced."
|
|
1637
|
+
), e;
|
|
1638
|
+
}
|
|
1639
|
+
var y, te, se, re, oe, ne;
|
|
1640
|
+
const z = class z {
|
|
1350
1641
|
constructor(e) {
|
|
1351
|
-
|
|
1642
|
+
x(this, y);
|
|
1352
1643
|
d(this, "cache", /* @__PURE__ */ new Map());
|
|
1353
1644
|
d(this, "normalizedHeaderName");
|
|
1354
1645
|
this.config = e, this.normalizedHeaderName = (e.headerName || "mcp-toolset-permissions").toLowerCase();
|
|
1355
1646
|
}
|
|
1647
|
+
static builder() {
|
|
1648
|
+
const e = {}, t = {
|
|
1649
|
+
source(s) {
|
|
1650
|
+
return e.source = s, t;
|
|
1651
|
+
},
|
|
1652
|
+
headerName(s) {
|
|
1653
|
+
return e.headerName = s, t;
|
|
1654
|
+
},
|
|
1655
|
+
staticMap(s) {
|
|
1656
|
+
return e.staticMap = s, t;
|
|
1657
|
+
},
|
|
1658
|
+
resolver(s) {
|
|
1659
|
+
return e.resolver = s, t;
|
|
1660
|
+
},
|
|
1661
|
+
defaultPermissions(s) {
|
|
1662
|
+
return e.defaultPermissions = s, t;
|
|
1663
|
+
},
|
|
1664
|
+
build() {
|
|
1665
|
+
return new z(e);
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
return t;
|
|
1669
|
+
}
|
|
1356
1670
|
/**
|
|
1357
|
-
* Resolves permissions for a client based on the configured source.
|
|
1358
|
-
* Results are cached to improve performance for subsequent requests from the same client.
|
|
1359
|
-
* Handles all errors gracefully by returning empty permissions on failure.
|
|
1360
|
-
*
|
|
1361
|
-
* Note on caching: For header-based permissions, permissions are cached by clientId.
|
|
1362
|
-
* This means subsequent requests from the same client will use cached permissions,
|
|
1363
|
-
* even if headers change. Use invalidateCache(clientId) to force re-resolution.
|
|
1364
|
-
*
|
|
1365
1671
|
* @param clientId - The unique identifier for the client
|
|
1366
|
-
* @param headers - Optional request headers
|
|
1672
|
+
* @param headers - Optional request headers
|
|
1367
1673
|
* @returns Array of toolset names the client is allowed to access
|
|
1368
1674
|
*/
|
|
1369
1675
|
resolvePermissions(e, t) {
|
|
@@ -1371,48 +1677,37 @@ class Ee {
|
|
|
1371
1677
|
return this.cache.get(e);
|
|
1372
1678
|
let s;
|
|
1373
1679
|
try {
|
|
1374
|
-
this.config.source === "headers" ? s = m(this,
|
|
1680
|
+
this.config.source === "headers" ? s = m(this, y, te).call(this, t) : s = m(this, y, re).call(this, e), Array.isArray(s) || (console.warn(
|
|
1375
1681
|
`Permission resolution returned non-array for client ${e}, using empty permissions`
|
|
1376
1682
|
), s = []), s = s.filter(
|
|
1377
|
-
(
|
|
1683
|
+
(o) => typeof o == "string" && o.trim().length > 0
|
|
1378
1684
|
);
|
|
1379
|
-
} catch (
|
|
1685
|
+
} catch (o) {
|
|
1380
1686
|
console.error(
|
|
1381
1687
|
`Unexpected error resolving permissions for client ${e}:`,
|
|
1382
|
-
|
|
1688
|
+
o
|
|
1383
1689
|
), s = [];
|
|
1384
1690
|
}
|
|
1385
1691
|
return this.cache.set(e, s), s;
|
|
1386
1692
|
}
|
|
1387
1693
|
/**
|
|
1388
|
-
* Invalidates cached permissions for a specific client.
|
|
1389
|
-
* Call this when you know a client's permissions have changed.
|
|
1390
1694
|
* @param clientId - The client ID to invalidate
|
|
1391
1695
|
*/
|
|
1392
1696
|
invalidateCache(e) {
|
|
1393
1697
|
this.cache.delete(e);
|
|
1394
1698
|
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Clears the permission cache.
|
|
1397
|
-
* Useful for cleanup during server shutdown or when permissions need to be refreshed.
|
|
1398
|
-
*/
|
|
1399
1699
|
clearCache() {
|
|
1400
1700
|
this.cache.clear();
|
|
1401
1701
|
}
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
* Parses permissions from request headers.
|
|
1405
|
-
* Extracts comma-separated toolset names from the configured header.
|
|
1406
|
-
* Handles malformed headers gracefully by returning empty permissions.
|
|
1407
|
-
* Uses case-insensitive header lookup per RFC 7230.
|
|
1702
|
+
};
|
|
1703
|
+
y = new WeakSet(), /**
|
|
1408
1704
|
* @param headers - Request headers containing permission data
|
|
1409
|
-
* @returns Array of toolset names from headers
|
|
1410
|
-
* @private
|
|
1705
|
+
* @returns Array of toolset names from headers
|
|
1411
1706
|
*/
|
|
1412
|
-
|
|
1707
|
+
te = function(e) {
|
|
1413
1708
|
if (!e)
|
|
1414
1709
|
return [];
|
|
1415
|
-
const t = m(this,
|
|
1710
|
+
const t = m(this, y, se).call(this, e, this.normalizedHeaderName);
|
|
1416
1711
|
if (!t)
|
|
1417
1712
|
return [];
|
|
1418
1713
|
try {
|
|
@@ -1424,47 +1719,37 @@ H = function(e) {
|
|
|
1424
1719
|
), [];
|
|
1425
1720
|
}
|
|
1426
1721
|
}, /**
|
|
1427
|
-
* Finds a header value using case-insensitive key matching.
|
|
1428
|
-
* HTTP headers are case-insensitive per RFC 7230.
|
|
1429
1722
|
* @param headers - The headers object to search
|
|
1430
1723
|
* @param normalizedKey - The lowercase key to search for
|
|
1431
|
-
* @returns The header value if found
|
|
1432
|
-
* @private
|
|
1724
|
+
* @returns The header value if found
|
|
1433
1725
|
*/
|
|
1434
|
-
|
|
1726
|
+
se = function(e, t) {
|
|
1435
1727
|
if (e[t] !== void 0)
|
|
1436
1728
|
return e[t];
|
|
1437
|
-
for (const [s,
|
|
1729
|
+
for (const [s, o] of Object.entries(e))
|
|
1438
1730
|
if (s.toLowerCase() === t)
|
|
1439
|
-
return
|
|
1731
|
+
return o;
|
|
1440
1732
|
}, /**
|
|
1441
|
-
* Resolves permissions from server-side configuration.
|
|
1442
|
-
* Tries resolver function first (if provided), then falls back to static map,
|
|
1443
|
-
* and finally to default permissions. Handles errors gracefully.
|
|
1444
1733
|
* @param clientId - The unique identifier for the client
|
|
1445
1734
|
* @returns Array of toolset names from configuration
|
|
1446
|
-
* @private
|
|
1447
1735
|
*/
|
|
1448
|
-
|
|
1736
|
+
re = function(e) {
|
|
1449
1737
|
if (this.config.resolver) {
|
|
1450
|
-
const t = m(this,
|
|
1738
|
+
const t = m(this, y, oe).call(this, e);
|
|
1451
1739
|
if (t !== null)
|
|
1452
1740
|
return t;
|
|
1453
1741
|
}
|
|
1454
1742
|
if (this.config.staticMap) {
|
|
1455
|
-
const t = m(this,
|
|
1743
|
+
const t = m(this, y, ne).call(this, e);
|
|
1456
1744
|
if (t !== null)
|
|
1457
1745
|
return t;
|
|
1458
1746
|
}
|
|
1459
1747
|
return this.config.defaultPermissions || [];
|
|
1460
1748
|
}, /**
|
|
1461
|
-
* Attempts to resolve permissions using the configured resolver function.
|
|
1462
|
-
* Handles errors gracefully and returns null on failure to allow fallback.
|
|
1463
1749
|
* @param clientId - The unique identifier for the client
|
|
1464
|
-
* @returns Array of toolset names if successful, null if resolver fails
|
|
1465
|
-
* @private
|
|
1750
|
+
* @returns Array of toolset names if successful, null if resolver fails
|
|
1466
1751
|
*/
|
|
1467
|
-
|
|
1752
|
+
oe = function(e) {
|
|
1468
1753
|
try {
|
|
1469
1754
|
const t = this.config.resolver(e);
|
|
1470
1755
|
return Array.isArray(t) ? t : (console.warn(
|
|
@@ -1477,61 +1762,28 @@ U = function(e) {
|
|
|
1477
1762
|
), null;
|
|
1478
1763
|
}
|
|
1479
1764
|
}, /**
|
|
1480
|
-
* Looks up permissions in the static map configuration.
|
|
1481
|
-
* Returns null if client is not found to allow fallback to defaults.
|
|
1482
1765
|
* @param clientId - The unique identifier for the client
|
|
1483
1766
|
* @returns Array of toolset names if found, null if client not in map
|
|
1484
|
-
* @private
|
|
1485
1767
|
*/
|
|
1486
|
-
|
|
1768
|
+
ne = function(e) {
|
|
1487
1769
|
const t = this.config.staticMap[e];
|
|
1488
1770
|
return t !== void 0 ? Array.isArray(t) ? t : [] : null;
|
|
1489
1771
|
};
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
if (s.length > 0) {
|
|
1497
|
-
const c = await i.enableToolsets(s);
|
|
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}`
|
|
1501
|
-
));
|
|
1502
|
-
if (n.length === 0 && l.length > 0)
|
|
1503
|
-
throw new Error(
|
|
1504
|
-
`All requested toolsets failed to enable for client '${t.clientId}'. Requested: [${s.join(", ")}]. Check that toolset names in permissions match the catalog.`
|
|
1505
|
-
);
|
|
1506
|
-
}
|
|
1507
|
-
return {
|
|
1508
|
-
server: r.server,
|
|
1509
|
-
orchestrator: r.orchestrator,
|
|
1510
|
-
allowedToolsets: n,
|
|
1511
|
-
failedToolsets: l
|
|
1512
|
-
};
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
|
-
var f, J, Q, G, Z, X, ee, te, se, M, oe;
|
|
1516
|
-
class Me {
|
|
1517
|
-
/**
|
|
1518
|
-
* Creates a new PermissionAwareFastifyTransport instance.
|
|
1519
|
-
* @param defaultManager - Default tool manager for status endpoints
|
|
1520
|
-
* @param createPermissionAwareBundle - Function to create permission-aware bundles
|
|
1521
|
-
* @param options - Transport configuration options
|
|
1522
|
-
* @param configSchema - Optional JSON schema for configuration discovery
|
|
1523
|
-
*/
|
|
1524
|
-
constructor(e, t, s = {}, r) {
|
|
1525
|
-
A(this, f);
|
|
1772
|
+
let I = z;
|
|
1773
|
+
const Be = g.string({ message: "Missing required mcp-client-id header" }).trim().min(1, "mcp-client-id header must not be empty");
|
|
1774
|
+
var h, ie, ae, le, ce, de, ue, he, fe, R, me;
|
|
1775
|
+
const q = class q {
|
|
1776
|
+
constructor(e, t, s = {}, o) {
|
|
1777
|
+
x(this, h);
|
|
1526
1778
|
d(this, "options");
|
|
1527
1779
|
d(this, "defaultManager");
|
|
1528
1780
|
d(this, "createPermissionAwareBundle");
|
|
1529
1781
|
d(this, "app", null);
|
|
1530
1782
|
d(this, "configSchema");
|
|
1531
1783
|
// Per-client server bundles and per-client session transports
|
|
1532
|
-
d(this, "clientCache", new
|
|
1784
|
+
d(this, "clientCache", new E({
|
|
1533
1785
|
onEvict: (e, t) => {
|
|
1534
|
-
m(this,
|
|
1786
|
+
m(this, h, ie).call(this, t);
|
|
1535
1787
|
}
|
|
1536
1788
|
}));
|
|
1537
1789
|
this.defaultManager = e, this.createPermissionAwareBundle = t, this.options = {
|
|
@@ -1542,29 +1794,66 @@ class Me {
|
|
|
1542
1794
|
logger: s.logger ?? !1,
|
|
1543
1795
|
app: s.app,
|
|
1544
1796
|
customEndpoints: s.customEndpoints
|
|
1545
|
-
}, this.configSchema =
|
|
1797
|
+
}, this.configSchema = o;
|
|
1798
|
+
}
|
|
1799
|
+
static builder() {
|
|
1800
|
+
let e, t;
|
|
1801
|
+
const s = {};
|
|
1802
|
+
let o;
|
|
1803
|
+
const n = {
|
|
1804
|
+
defaultManager(i) {
|
|
1805
|
+
return e = i, n;
|
|
1806
|
+
},
|
|
1807
|
+
createPermissionAwareBundle(i) {
|
|
1808
|
+
return t = i, n;
|
|
1809
|
+
},
|
|
1810
|
+
host(i) {
|
|
1811
|
+
return s.host = i, n;
|
|
1812
|
+
},
|
|
1813
|
+
port(i) {
|
|
1814
|
+
return s.port = i, n;
|
|
1815
|
+
},
|
|
1816
|
+
basePath(i) {
|
|
1817
|
+
return s.basePath = i, n;
|
|
1818
|
+
},
|
|
1819
|
+
cors(i) {
|
|
1820
|
+
return s.cors = i, n;
|
|
1821
|
+
},
|
|
1822
|
+
logger(i) {
|
|
1823
|
+
return s.logger = i, n;
|
|
1824
|
+
},
|
|
1825
|
+
app(i) {
|
|
1826
|
+
return s.app = i, n;
|
|
1827
|
+
},
|
|
1828
|
+
customEndpoints(i) {
|
|
1829
|
+
return s.customEndpoints = i, n;
|
|
1830
|
+
},
|
|
1831
|
+
configSchema(i) {
|
|
1832
|
+
return o = i, n;
|
|
1833
|
+
},
|
|
1834
|
+
build() {
|
|
1835
|
+
return new q(e, t, s, o);
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
return n;
|
|
1546
1839
|
}
|
|
1547
|
-
/**
|
|
1548
|
-
* Starts the Fastify server and registers all MCP endpoints.
|
|
1549
|
-
* Sets up routes for health checks, tool status, and MCP protocol handling.
|
|
1550
|
-
*/
|
|
1551
1840
|
async start() {
|
|
1552
1841
|
if (this.app) return;
|
|
1553
|
-
const e = this.options.app ??
|
|
1554
|
-
this.options.cors && await e.register(
|
|
1555
|
-
const t = m(this,
|
|
1556
|
-
m(this,
|
|
1842
|
+
const e = this.options.app ?? U({ logger: this.options.logger });
|
|
1843
|
+
this.options.cors && await e.register(J, { origin: !0 });
|
|
1844
|
+
const t = m(this, h, ae).call(this, this.options.basePath);
|
|
1845
|
+
m(this, h, le).call(this, e, t), m(this, h, ce).call(this, e, t), m(this, h, de).call(this, e, t), m(this, h, ue).call(this, e, t), m(this, h, he).call(this, e, t), m(this, h, fe).call(this, e, t), this.options.customEndpoints && this.options.customEndpoints.length > 0 && Z(e, t, this.options.customEndpoints, {
|
|
1557
1846
|
contextExtractor: async (s) => {
|
|
1558
|
-
const
|
|
1847
|
+
const o = m(this, h, R).call(this, s);
|
|
1559
1848
|
try {
|
|
1560
|
-
const
|
|
1849
|
+
const n = await this.createPermissionAwareBundle(o);
|
|
1561
1850
|
return {
|
|
1562
|
-
allowedToolsets:
|
|
1563
|
-
failedToolsets:
|
|
1851
|
+
allowedToolsets: n.allowedToolsets,
|
|
1852
|
+
failedToolsets: n.failedToolsets
|
|
1564
1853
|
};
|
|
1565
|
-
} catch (
|
|
1854
|
+
} catch (n) {
|
|
1566
1855
|
return console.warn(
|
|
1567
|
-
`Permission resolution failed for custom endpoint: ${
|
|
1856
|
+
`Permission resolution failed for custom endpoint: ${n}`
|
|
1568
1857
|
), {
|
|
1569
1858
|
allowedToolsets: [],
|
|
1570
1859
|
failedToolsets: []
|
|
@@ -1573,62 +1862,47 @@ class Me {
|
|
|
1573
1862
|
}
|
|
1574
1863
|
}), this.options.app || await e.listen({ host: this.options.host, port: this.options.port }), this.app = e;
|
|
1575
1864
|
}
|
|
1576
|
-
/**
|
|
1577
|
-
* Stops the Fastify server and cleans up all resources.
|
|
1578
|
-
* Closes all client sessions and clears the cache.
|
|
1579
|
-
*/
|
|
1580
1865
|
async stop() {
|
|
1581
1866
|
this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
|
|
1582
1867
|
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
* Cleans up resources associated with a client bundle.
|
|
1586
|
-
* Closes all sessions within the bundle.
|
|
1868
|
+
};
|
|
1869
|
+
h = new WeakSet(), /**
|
|
1587
1870
|
* @param bundle - The client bundle to clean up
|
|
1588
|
-
* @private
|
|
1589
1871
|
*/
|
|
1590
|
-
|
|
1872
|
+
ie = function(e) {
|
|
1591
1873
|
for (const [t, s] of e.sessions.entries())
|
|
1592
1874
|
try {
|
|
1593
|
-
typeof s.close == "function" && s.close().catch((
|
|
1594
|
-
console.warn(`Error closing session ${t}:`,
|
|
1875
|
+
typeof s.close == "function" && s.close().catch((o) => {
|
|
1876
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
1595
1877
|
});
|
|
1596
|
-
} catch (
|
|
1597
|
-
console.warn(`Error closing session ${t}:`,
|
|
1878
|
+
} catch (o) {
|
|
1879
|
+
console.warn(`Error closing session ${t}:`, o);
|
|
1598
1880
|
}
|
|
1599
1881
|
e.sessions.clear();
|
|
1600
1882
|
}, /**
|
|
1601
|
-
* Normalizes the base path by removing trailing slashes.
|
|
1602
1883
|
* @param basePath - The base path to normalize
|
|
1603
1884
|
* @returns Normalized base path without trailing slash
|
|
1604
|
-
* @private
|
|
1605
1885
|
*/
|
|
1606
|
-
|
|
1886
|
+
ae = function(e) {
|
|
1607
1887
|
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
1608
1888
|
}, /**
|
|
1609
|
-
* Registers the health check endpoint.
|
|
1610
1889
|
* @param app - Fastify instance
|
|
1611
1890
|
* @param base - Base path for routes
|
|
1612
|
-
* @private
|
|
1613
1891
|
*/
|
|
1614
|
-
|
|
1892
|
+
le = function(e, t) {
|
|
1615
1893
|
e.get(`${t}/healthz`, async () => ({ ok: !0 }));
|
|
1616
1894
|
}, /**
|
|
1617
|
-
* Registers the tools status endpoint.
|
|
1618
1895
|
* @param app - Fastify instance
|
|
1619
1896
|
* @param base - Base path for routes
|
|
1620
|
-
* @private
|
|
1621
1897
|
*/
|
|
1622
|
-
|
|
1898
|
+
ce = function(e, t) {
|
|
1623
1899
|
e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
|
|
1624
1900
|
}, /**
|
|
1625
|
-
* Registers the MCP configuration discovery endpoint.
|
|
1626
1901
|
* @param app - Fastify instance
|
|
1627
1902
|
* @param base - Base path for routes
|
|
1628
|
-
* @private
|
|
1629
1903
|
*/
|
|
1630
|
-
|
|
1631
|
-
e.get(`${t}/.well-known/mcp-config`, async (s,
|
|
1904
|
+
de = function(e, t) {
|
|
1905
|
+
e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
1632
1906
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1633
1907
|
title: "MCP Session Configuration",
|
|
1634
1908
|
description: "Schema for the /mcp endpoint configuration",
|
|
@@ -1639,108 +1913,110 @@ X = function(e, t) {
|
|
|
1639
1913
|
"x-query-style": "dot+bracket"
|
|
1640
1914
|
}));
|
|
1641
1915
|
}, /**
|
|
1642
|
-
* Registers the POST /mcp endpoint for JSON-RPC requests.
|
|
1643
|
-
* Extracts client context, resolves permissions, and handles MCP protocol.
|
|
1644
1916
|
* @param app - Fastify instance
|
|
1645
1917
|
* @param base - Base path for routes
|
|
1646
|
-
* @private
|
|
1647
1918
|
*/
|
|
1648
|
-
|
|
1919
|
+
ue = function(e, t) {
|
|
1649
1920
|
e.post(
|
|
1650
1921
|
`${t}/mcp`,
|
|
1651
|
-
async (s,
|
|
1652
|
-
const
|
|
1653
|
-
|
|
1654
|
-
|
|
1922
|
+
async (s, o) => {
|
|
1923
|
+
const n = Be.safeParse(
|
|
1924
|
+
s.headers["mcp-client-id"]
|
|
1925
|
+
);
|
|
1926
|
+
if (!n.success)
|
|
1927
|
+
return o.code(400), {
|
|
1928
|
+
jsonrpc: "2.0",
|
|
1929
|
+
error: { code: -32600, message: n.error.issues[0].message },
|
|
1930
|
+
id: null
|
|
1931
|
+
};
|
|
1932
|
+
const i = m(this, h, R).call(this, s);
|
|
1933
|
+
let a = this.clientCache.get(i.clientId);
|
|
1934
|
+
if (!a)
|
|
1655
1935
|
try {
|
|
1656
1936
|
const u = await this.createPermissionAwareBundle(i);
|
|
1657
1937
|
u.failedToolsets.length > 0 && console.warn(
|
|
1658
1938
|
`Client ${i.clientId} had ${u.failedToolsets.length} toolsets fail to enable: [${u.failedToolsets.join(", ")}]. Successfully enabled: [${u.allowedToolsets.join(", ")}]`
|
|
1659
1939
|
);
|
|
1660
|
-
const
|
|
1661
|
-
|
|
1940
|
+
const f = u.sessions;
|
|
1941
|
+
a = {
|
|
1662
1942
|
server: u.server,
|
|
1663
1943
|
orchestrator: u.orchestrator,
|
|
1664
1944
|
allowedToolsets: u.allowedToolsets,
|
|
1665
1945
|
failedToolsets: u.failedToolsets,
|
|
1666
|
-
sessions:
|
|
1667
|
-
},
|
|
1946
|
+
sessions: f instanceof Map ? f : /* @__PURE__ */ new Map()
|
|
1947
|
+
}, this.clientCache.set(i.clientId, a);
|
|
1668
1948
|
} catch (u) {
|
|
1669
1949
|
return console.error(
|
|
1670
1950
|
`Failed to create permission-aware bundle for client ${i.clientId}:`,
|
|
1671
1951
|
u
|
|
1672
|
-
),
|
|
1952
|
+
), o.code(403), m(this, h, me).call(this, "Access denied");
|
|
1673
1953
|
}
|
|
1674
|
-
const
|
|
1675
|
-
let
|
|
1676
|
-
if (
|
|
1677
|
-
|
|
1678
|
-
else if (!
|
|
1954
|
+
const l = s.headers["mcp-session-id"];
|
|
1955
|
+
let c;
|
|
1956
|
+
if (l && a.sessions.get(l))
|
|
1957
|
+
c = a.sessions.get(l);
|
|
1958
|
+
else if (!l && G(s.body)) {
|
|
1679
1959
|
const u = S();
|
|
1680
|
-
|
|
1960
|
+
c = new Q({
|
|
1681
1961
|
sessionIdGenerator: () => u,
|
|
1682
|
-
onsessioninitialized: (
|
|
1683
|
-
|
|
1962
|
+
onsessioninitialized: (f) => {
|
|
1963
|
+
a.sessions.set(f, c);
|
|
1684
1964
|
}
|
|
1685
1965
|
});
|
|
1686
1966
|
try {
|
|
1687
|
-
await
|
|
1967
|
+
await a.server.connect(c);
|
|
1688
1968
|
} catch {
|
|
1689
|
-
return
|
|
1969
|
+
return o.code(500), {
|
|
1690
1970
|
jsonrpc: "2.0",
|
|
1691
1971
|
error: { code: -32603, message: "Error initializing server." },
|
|
1692
1972
|
id: null
|
|
1693
1973
|
};
|
|
1694
1974
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1975
|
+
c.onclose = () => {
|
|
1976
|
+
c?.sessionId && a.sessions.delete(c.sessionId);
|
|
1697
1977
|
};
|
|
1698
1978
|
} else
|
|
1699
|
-
return
|
|
1979
|
+
return o.code(400), {
|
|
1700
1980
|
jsonrpc: "2.0",
|
|
1701
1981
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
1702
1982
|
id: null
|
|
1703
1983
|
};
|
|
1704
|
-
return await
|
|
1984
|
+
return await c.handleRequest(
|
|
1705
1985
|
s.raw,
|
|
1706
|
-
|
|
1986
|
+
o.raw,
|
|
1707
1987
|
s.body
|
|
1708
|
-
),
|
|
1988
|
+
), o;
|
|
1709
1989
|
}
|
|
1710
1990
|
);
|
|
1711
1991
|
}, /**
|
|
1712
|
-
* Registers the GET /mcp endpoint for SSE notifications.
|
|
1713
1992
|
* @param app - Fastify instance
|
|
1714
1993
|
* @param base - Base path for routes
|
|
1715
|
-
* @private
|
|
1716
1994
|
*/
|
|
1717
|
-
|
|
1718
|
-
e.get(`${t}/mcp`, async (s,
|
|
1719
|
-
const
|
|
1720
|
-
if (!
|
|
1721
|
-
return
|
|
1722
|
-
const
|
|
1995
|
+
he = function(e, t) {
|
|
1996
|
+
e.get(`${t}/mcp`, async (s, o) => {
|
|
1997
|
+
const n = s.headers["mcp-client-id"]?.trim(), i = n && n.length > 0 ? n : "";
|
|
1998
|
+
if (!i)
|
|
1999
|
+
return o.code(400), "Missing mcp-client-id";
|
|
2000
|
+
const a = this.clientCache.get(i);
|
|
2001
|
+
if (!a)
|
|
2002
|
+
return o.code(400), "Invalid or expired client";
|
|
2003
|
+
const l = s.headers["mcp-session-id"];
|
|
1723
2004
|
if (!l)
|
|
1724
|
-
return
|
|
1725
|
-
const c =
|
|
1726
|
-
|
|
1727
|
-
return r.code(400), "Missing mcp-session-id";
|
|
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");
|
|
2005
|
+
return o.code(400), "Missing mcp-session-id";
|
|
2006
|
+
const c = a.sessions.get(l);
|
|
2007
|
+
return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
|
|
1730
2008
|
});
|
|
1731
2009
|
}, /**
|
|
1732
|
-
* Registers the DELETE /mcp endpoint for session termination.
|
|
1733
2010
|
* @param app - Fastify instance
|
|
1734
2011
|
* @param base - Base path for routes
|
|
1735
|
-
* @private
|
|
1736
2012
|
*/
|
|
1737
|
-
|
|
2013
|
+
fe = function(e, t) {
|
|
1738
2014
|
e.delete(
|
|
1739
2015
|
`${t}/mcp`,
|
|
1740
|
-
async (s,
|
|
1741
|
-
const
|
|
1742
|
-
if (!
|
|
1743
|
-
return
|
|
2016
|
+
async (s, o) => {
|
|
2017
|
+
const n = s.headers["mcp-client-id"]?.trim(), i = n && n.length > 0 ? n : "", a = s.headers["mcp-session-id"];
|
|
2018
|
+
if (!i || !a)
|
|
2019
|
+
return o.code(400), {
|
|
1744
2020
|
jsonrpc: "2.0",
|
|
1745
2021
|
error: {
|
|
1746
2022
|
code: -32600,
|
|
@@ -1748,46 +2024,40 @@ se = function(e, t) {
|
|
|
1748
2024
|
},
|
|
1749
2025
|
id: null
|
|
1750
2026
|
};
|
|
1751
|
-
const
|
|
1752
|
-
if (!
|
|
1753
|
-
return
|
|
2027
|
+
const l = this.clientCache.get(i), c = l?.sessions.get(a);
|
|
2028
|
+
if (!l || !c)
|
|
2029
|
+
return o.code(404), {
|
|
1754
2030
|
jsonrpc: "2.0",
|
|
1755
2031
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
1756
2032
|
id: null
|
|
1757
2033
|
};
|
|
1758
2034
|
try {
|
|
1759
|
-
if (typeof
|
|
2035
|
+
if (typeof c.close == "function")
|
|
1760
2036
|
try {
|
|
1761
|
-
await
|
|
2037
|
+
await c.close();
|
|
1762
2038
|
} catch {
|
|
1763
2039
|
}
|
|
1764
2040
|
} finally {
|
|
1765
|
-
|
|
2041
|
+
c?.sessionId ? l.sessions.delete(c.sessionId) : l.sessions.delete(a);
|
|
1766
2042
|
}
|
|
1767
|
-
return
|
|
2043
|
+
return o.code(204).send(), o;
|
|
1768
2044
|
}
|
|
1769
2045
|
);
|
|
1770
2046
|
}, /**
|
|
1771
|
-
* Extracts client context from the request.
|
|
1772
|
-
* Generates anonymous client ID if not provided in headers.
|
|
1773
2047
|
* @param req - Fastify request object
|
|
1774
2048
|
* @returns Client request context with ID and headers
|
|
1775
|
-
* @private
|
|
1776
2049
|
*/
|
|
1777
|
-
|
|
1778
|
-
const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${S()}`,
|
|
1779
|
-
for (const [
|
|
1780
|
-
typeof
|
|
1781
|
-
return { clientId: s, headers:
|
|
2050
|
+
R = function(e) {
|
|
2051
|
+
const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${S()}`, o = {};
|
|
2052
|
+
for (const [n, i] of Object.entries(e.headers))
|
|
2053
|
+
typeof i == "string" && (o[n] = i);
|
|
2054
|
+
return { clientId: s, headers: o };
|
|
1782
2055
|
}, /**
|
|
1783
|
-
* Creates a safe error response that doesn't expose unauthorized toolset information.
|
|
1784
|
-
* Used for permission-related errors to prevent information leakage.
|
|
1785
2056
|
* @param message - Generic error message to return to client
|
|
1786
|
-
* @param code - JSON-RPC error code
|
|
2057
|
+
* @param code - JSON-RPC error code
|
|
1787
2058
|
* @returns JSON-RPC error response object
|
|
1788
|
-
* @private
|
|
1789
2059
|
*/
|
|
1790
|
-
|
|
2060
|
+
me = function(e = "Access denied", t = -32e3) {
|
|
1791
2061
|
return {
|
|
1792
2062
|
jsonrpc: "2.0",
|
|
1793
2063
|
error: {
|
|
@@ -1797,97 +2067,64 @@ oe = function(e = "Access denied", t = -32e3) {
|
|
|
1797
2067
|
id: null
|
|
1798
2068
|
};
|
|
1799
2069
|
};
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
2070
|
+
let $ = q;
|
|
2071
|
+
async function ot(r) {
|
|
2072
|
+
He(r);
|
|
2073
|
+
const e = Ke(r.exposurePolicy), t = Ve(r), s = r.createServer(), o = pe(s, r, e), n = _e(
|
|
2074
|
+
We(r, e),
|
|
2075
|
+
t
|
|
2076
|
+
), i = Ye(r, o.getManager(), n);
|
|
2077
|
+
return {
|
|
2078
|
+
server: s,
|
|
2079
|
+
start: () => i.start(),
|
|
2080
|
+
close: async () => {
|
|
2081
|
+
try {
|
|
2082
|
+
await i.stop();
|
|
2083
|
+
} finally {
|
|
2084
|
+
t.clearCache();
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1804
2087
|
};
|
|
1805
|
-
return o.allowlist !== void 0 && console.warn(
|
|
1806
|
-
"Permission-based servers: exposurePolicy.allowlist is ignored. Allowed toolsets are determined by client permissions."
|
|
1807
|
-
), o.denylist !== void 0 && console.warn(
|
|
1808
|
-
"Permission-based servers: exposurePolicy.denylist is ignored. Use permission configuration to control toolset access."
|
|
1809
|
-
), o.maxActiveToolsets !== void 0 && console.warn(
|
|
1810
|
-
"Permission-based servers: exposurePolicy.maxActiveToolsets is ignored. Toolset count is determined by client permissions."
|
|
1811
|
-
), o.onLimitExceeded !== void 0 && console.warn(
|
|
1812
|
-
"Permission-based servers: exposurePolicy.onLimitExceeded is ignored. No toolset limits are enforced."
|
|
1813
|
-
), e;
|
|
1814
2088
|
}
|
|
1815
|
-
|
|
1816
|
-
if (!
|
|
2089
|
+
function He(r) {
|
|
2090
|
+
if (!r.permissions)
|
|
1817
2091
|
throw new Error(
|
|
1818
2092
|
"Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
|
|
1819
2093
|
);
|
|
1820
|
-
if (
|
|
2094
|
+
if (Oe(r.permissions), r.sessionContext && (X(r.sessionContext), console.warn(
|
|
1821
2095
|
"Session context support for permission-based servers is limited. The base context will be used for module loaders."
|
|
1822
|
-
)),
|
|
2096
|
+
)), r.startup)
|
|
1823
2097
|
throw new Error(
|
|
1824
2098
|
"Permission-based servers determine toolsets from client permissions. The 'startup' option is not allowed. Remove it from your configuration."
|
|
1825
2099
|
);
|
|
1826
|
-
if (typeof
|
|
2100
|
+
if (typeof r.createServer != "function")
|
|
1827
2101
|
throw new Error(
|
|
1828
2102
|
"createPermissionBasedMcpServer: `createServer` (factory) is required"
|
|
1829
2103
|
);
|
|
1830
|
-
const e = Ie(
|
|
1831
|
-
o.exposurePolicy
|
|
1832
|
-
), t = new Ee(o.permissions), s = o.createServer(), r = new C({
|
|
1833
|
-
server: s,
|
|
1834
|
-
catalog: o.catalog,
|
|
1835
|
-
moduleLoaders: o.moduleLoaders,
|
|
1836
|
-
exposurePolicy: e,
|
|
1837
|
-
context: o.context,
|
|
1838
|
-
notifyToolsListChanged: void 0,
|
|
1839
|
-
// No notifications in STATIC mode
|
|
1840
|
-
startup: { mode: "STATIC", toolsets: [] },
|
|
1841
|
-
registerMetaTools: !1
|
|
1842
|
-
}), i = Pe(
|
|
1843
|
-
(l) => {
|
|
1844
|
-
const c = o.createServer(), a = new C({
|
|
1845
|
-
server: c,
|
|
1846
|
-
catalog: o.catalog,
|
|
1847
|
-
moduleLoaders: o.moduleLoaders,
|
|
1848
|
-
exposurePolicy: e,
|
|
1849
|
-
context: o.context,
|
|
1850
|
-
notifyToolsListChanged: void 0,
|
|
1851
|
-
// No notifications in STATIC mode
|
|
1852
|
-
startup: { mode: "STATIC", toolsets: [] },
|
|
1853
|
-
// Empty - we'll enable manually
|
|
1854
|
-
registerMetaTools: !1
|
|
1855
|
-
// No meta-tools - toolsets are fixed per client
|
|
1856
|
-
});
|
|
1857
|
-
return { server: c, orchestrator: a };
|
|
1858
|
-
},
|
|
1859
|
-
t
|
|
1860
|
-
), n = new Me(
|
|
1861
|
-
r.getManager(),
|
|
1862
|
-
i,
|
|
1863
|
-
o.http,
|
|
1864
|
-
o.configSchema
|
|
1865
|
-
);
|
|
1866
|
-
return {
|
|
1867
|
-
server: s,
|
|
1868
|
-
start: async () => {
|
|
1869
|
-
await n.start();
|
|
1870
|
-
},
|
|
1871
|
-
close: async () => {
|
|
1872
|
-
try {
|
|
1873
|
-
await n.stop();
|
|
1874
|
-
} finally {
|
|
1875
|
-
t.clearCache();
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
};
|
|
1879
2104
|
}
|
|
1880
|
-
function
|
|
1881
|
-
|
|
2105
|
+
function Ve(r) {
|
|
2106
|
+
const e = I.builder().source(r.permissions.source).headerName(r.permissions.headerName ?? "mcp-toolset-permissions").staticMap(r.permissions.staticMap ?? {}).defaultPermissions(r.permissions.defaultPermissions ?? []);
|
|
2107
|
+
return r.permissions.resolver && e.resolver(r.permissions.resolver), e.build();
|
|
2108
|
+
}
|
|
2109
|
+
function pe(r, e, t) {
|
|
2110
|
+
const s = A.builder().server(r).catalog(e.catalog).moduleLoaders(e.moduleLoaders ?? {}).context(e.context).startup({ mode: "STATIC", toolsets: [] }).registerMetaTools(!1);
|
|
2111
|
+
return t && s.exposurePolicy(t), s.build();
|
|
2112
|
+
}
|
|
2113
|
+
function We(r, e) {
|
|
2114
|
+
return (t) => {
|
|
2115
|
+
const s = r.createServer(), o = pe(s, r, e);
|
|
2116
|
+
return { server: s, orchestrator: o };
|
|
2117
|
+
};
|
|
1882
2118
|
}
|
|
1883
|
-
function
|
|
1884
|
-
|
|
2119
|
+
function Ye(r, e, t) {
|
|
2120
|
+
const s = $.builder().defaultManager(e).createPermissionAwareBundle(t).host(r.http?.host ?? "0.0.0.0").port(r.http?.port ?? 3e3).basePath(r.http?.basePath ?? "/").cors(r.http?.cors ?? !0).logger(r.http?.logger ?? !1);
|
|
2121
|
+
return r.http?.app && s.app(r.http.app), r.http?.customEndpoints && s.customEndpoints(r.http.customEndpoints), r.configSchema && s.configSchema(r.configSchema), s.build();
|
|
1885
2122
|
}
|
|
1886
2123
|
export {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2124
|
+
k as SessionContextResolver,
|
|
2125
|
+
rt as createMcpServer,
|
|
2126
|
+
ot as createPermissionBasedMcpServer,
|
|
2127
|
+
tt as defineEndpoint,
|
|
2128
|
+
st as definePermissionAwareEndpoint
|
|
1892
2129
|
};
|
|
1893
2130
|
//# sourceMappingURL=index.js.map
|