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