toolception 0.3.0 → 0.4.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/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
- var w = (r) => {
1
+ var A = (r) => {
2
2
  throw TypeError(r);
3
3
  };
4
- var B = (r, e, t) => e.has(r) || w("Cannot " + t);
5
- var v = (r, e, t) => e.has(r) ? w("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, t);
6
- var u = (r, e, t) => (B(r, e, "access private method"), t);
7
- import { z as T } from "zod";
8
- import S from "fastify";
9
- import M from "@fastify/cors";
10
- import { randomUUID as p } from "node:crypto";
11
- import { StreamableHTTPServerTransport as C } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
12
- import { isInitializeRequest as x } from "@modelcontextprotocol/sdk/types.js";
13
- const b = {
4
+ var W = (r, e, s) => e.has(r) || A("Cannot " + s);
5
+ var y = (r, e, s) => e.has(r) ? A("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, s);
6
+ var u = (r, e, s) => (W(r, e, "access private method"), s);
7
+ import { z as w } from "zod";
8
+ import M from "fastify";
9
+ import x from "@fastify/cors";
10
+ import { randomUUID as v } from "node:crypto";
11
+ import { StreamableHTTPServerTransport as E } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
12
+ import { isInitializeRequest as I } from "@modelcontextprotocol/sdk/types.js";
13
+ const S = {
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 Y {
21
+ class q {
22
22
  constructor(e = {}) {
23
23
  this.keys = {
24
- dynamic: e.keys?.dynamic ?? b.dynamic,
25
- toolsets: e.keys?.toolsets ?? b.toolsets
24
+ dynamic: e.keys?.dynamic ?? S.dynamic,
25
+ toolsets: e.keys?.toolsets ?? S.toolsets
26
26
  };
27
27
  }
28
- resolveMode(e, t) {
29
- return this.isDynamicEnabled(t) ? "DYNAMIC" : this.getToolsetsString(t) ? "STATIC" : this.isDynamicEnabled(e) ? "DYNAMIC" : this.getToolsetsString(e) ? "STATIC" : null;
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, t) {
31
+ parseCommaSeparatedToolSets(e, s) {
32
32
  if (!e || typeof e != "string") return [];
33
- const s = e.split(",").map((a) => a.trim()).filter((a) => a.length > 0), o = new Set(Object.keys(t)), i = [];
34
- for (const a of s)
35
- o.has(a) ? i.push(a) : console.warn(
36
- `Invalid toolset '${a}' ignored. Available: ${Array.from(
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, t) {
43
- const s = /* @__PURE__ */ new Set();
42
+ getModulesForToolSets(e, s) {
43
+ const t = /* @__PURE__ */ new Set();
44
44
  for (const o of e) {
45
- const i = t[o];
46
- i && (i.modules || []).forEach((a) => s.add(a));
45
+ const i = s[o];
46
+ i && (i.modules || []).forEach((n) => t.add(n));
47
47
  }
48
- return Array.from(s);
48
+ return Array.from(t);
49
49
  }
50
- validateToolsetName(e, t) {
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
- t
55
+ s
56
56
  ).join(", ")}`
57
57
  };
58
- const s = e.trim();
59
- return s.length === 0 ? {
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
- t
62
+ s
63
63
  ).join(", ")}`
64
- } : t[s] ? { isValid: !0, sanitized: s } : {
64
+ } : s[t] ? { isValid: !0, sanitized: t } : {
65
65
  isValid: !1,
66
- error: `Toolset '${s}' not found. Available toolsets: ${Object.keys(
67
- t
66
+ error: `Toolset '${t}' not found. Available toolsets: ${Object.keys(
67
+ s
68
68
  ).join(", ")}`
69
69
  };
70
70
  }
71
- validateToolsetModules(e, t) {
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 s = this.getModulesForToolSets(e, t);
74
- return !s || s.length === 0 ? {
75
- isValid: !1,
76
- error: `No modules found for toolsets: ${e.join(", ")}`
77
- } : { isValid: !0, modules: s };
78
- } catch (s) {
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(", ")}: ${s instanceof Error ? s.message : "Unknown error"}`
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 t of this.keys.dynamic) {
88
- const s = e[t];
89
- if (s === !0 || typeof s == "string" && s.trim().toLowerCase() === "true")
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 t of this.keys.toolsets) {
97
- const s = e[t];
98
- if (typeof s == "string" && s.trim().length > 0)
99
- return s;
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 U {
112
+ class J {
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 t = e.trim();
122
- return t.length === 0 ? {
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[t] ? { isValid: !0, sanitized: t } : {
136
+ } : this.catalog[s] ? { isValid: !0, sanitized: s } : {
128
137
  isValid: !1,
129
- error: `Toolset '${t}' not found. Available toolsets: ${this.getAvailableToolsets().join(
138
+ error: `Toolset '${s}' not found. Available toolsets: ${this.getAvailableToolsets().join(
130
139
  ", "
131
140
  )}`
132
141
  };
133
142
  }
134
- async resolveToolsForToolsets(e, t) {
135
- const s = [];
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 && s.push(...i.tools), Array.isArray(i.modules) && i.modules.length > 0))
139
- for (const a of i.modules) {
140
- const l = this.moduleLoaders[a];
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 n = await l(t);
144
- Array.isArray(n) && n.length > 0 && s.push(...n);
145
- } catch (n) {
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 '${a}' failed for toolset '${o}':`,
148
- n
156
+ `Module loader '${n}' failed for toolset '${o}':`,
157
+ a
149
158
  );
150
159
  }
151
160
  }
152
161
  }
153
- return s;
162
+ return t;
154
163
  }
155
164
  }
156
- class A extends Error {
157
- constructor(e, t, s, o) {
158
- super(e), this.name = "ToolingError", this.code = t, this.details = s;
165
+ class C 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 I {
170
+ class P {
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, t) {
168
- return !this.options.namespaceWithToolset || t.startsWith(`${e}.`) ? t : `${e}.${t}`;
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 A(
184
+ throw new C(
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, t) {
182
- this.add(t);
183
- const s = this.toolsetToNames.get(e) ?? /* @__PURE__ */ new Set();
184
- s.add(t), this.toolsetToNames.set(e, s);
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, t) {
187
- return t.map((s) => {
188
- const o = this.getSafeName(e, s.name);
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 A(
199
+ throw new C(
191
200
  `Tool name collision for '${o}'`,
192
201
  "E_TOOL_NAME_CONFLICT"
193
202
  );
194
- return { ...s, name: o };
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 [t, s] of this.toolsetToNames.entries())
203
- e[t] = Array.from(s);
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 W {
216
+ class G {
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 I({ namespaceWithToolset: !0 });
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 P({ 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
- async enableToolset(e) {
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 s = t.sanitized;
231
- if (this.activeToolsets.has(s))
260
+ const o = t.sanitized;
261
+ if (this.activeToolsets.has(o))
232
262
  return {
233
263
  success: !1,
234
- message: `Toolset '${s}' is already enabled.`
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 o = await this.resolver.resolveToolsForToolsets(
238
- [s],
271
+ const l = await this.resolver.resolveToolsForToolsets(
272
+ [o],
239
273
  this.context
240
274
  );
241
- if (this.exposurePolicy?.allowlist && !this.exposurePolicy.allowlist.includes(s))
242
- return {
243
- success: !1,
244
- message: `Toolset '${s}' is not allowed by policy.`
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
- this.registerDirectTools(i, s);
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 '${s}' enabled successfully. Registered ${o?.length ?? 0} tools.`
285
+ message: `Toolset '${o}' enabled successfully. Registered ${l?.length ?? 0} tools.`
275
286
  };
276
- } catch (o) {
277
- return this.activeToolsets.delete(s), {
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 '${s}': ${o instanceof Error ? o.message : "Unknown error"}`
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 t = this.resolver.validateToolsetName(e);
285
- if (!t.isValid || !t.sanitized) {
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: `${t.error || "Unknown validation error"} Active toolsets: ${o}`
290
- };
291
- }
292
- const s = t.sanitized;
293
- if (!this.activeToolsets.has(s))
294
- return {
295
- success: !1,
296
- message: `Toolset '${s}' is not currently active. Active toolsets: ${Array.from(this.activeToolsets).join(", ") || "none"}`
343
+ message: `${s.error || "Unknown validation error"} Active toolsets: ${o}`
297
344
  };
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
345
  }
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 '${s}' disabled successfully. Individual tools remain registered due to MCP limitations.`
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 t = [];
322
- for (const i of e)
373
+ const s = [];
374
+ for (const n of e)
323
375
  try {
324
- const a = await this.enableToolset(i);
325
- t.push({ name: i, ...a });
326
- } catch (a) {
327
- t.push({
328
- name: i,
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: a instanceof Error ? a.message : "Unknown error",
382
+ message: l instanceof Error ? l.message : "Unknown error",
331
383
  code: "E_INTERNAL"
332
384
  });
333
385
  }
334
- const s = t.every((i) => i.success), o = s ? "All toolsets enabled" : "Some toolsets failed to enable";
335
- if (t.length > 0)
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 H(r, e, t) {
361
- const s = t?.mode ?? "DYNAMIC";
362
- r.tool(
398
+ function K(r, e, s) {
399
+ (s?.mode ?? "DYNAMIC") === "DYNAMIC" && (r.tool(
363
400
  "enable_toolset",
364
401
  "Enable a toolset by name",
365
- { name: T.string().describe("Toolset name") },
402
+ { name: w.string().describe("Toolset name") },
366
403
  async (o) => {
367
- const { name: i } = o, a = await e.enableToolset(i);
404
+ const i = await e.enableToolset(o.name);
368
405
  return {
369
- content: [{ type: "text", text: JSON.stringify(a) }]
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: T.string().describe("Toolset name") },
412
+ { name: w.string().describe("Toolset name") },
376
413
  async (o) => {
377
- const { name: i } = o, a = await e.disableToolset(i);
414
+ const i = await e.disableToolset(o.name);
378
415
  return {
379
- content: [{ type: "text", text: JSON.stringify(a) }]
416
+ content: [{ type: "text", text: JSON.stringify(i) }]
380
417
  };
381
418
  }
382
- ), s === "DYNAMIC" && (r.tool(
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, a = o.map((l) => {
388
- const n = e.getToolsetDefinition(l);
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: n ? {
393
- name: n.name,
394
- description: n.description,
395
- modules: n.modules ?? [],
396
- decisionCriteria: n.decisionCriteria ?? void 0
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: a }) }
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: T.string().describe("Toolset name") },
447
+ { name: w.string().describe("Toolset name") },
411
448
  async (o) => {
412
- const { name: i } = o, a = e.getToolsetDefinition(i), l = e.getStatus().toolsetToTools;
413
- if (!a)
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 '${i}'` })
455
+ text: JSON.stringify({ error: `Unknown toolset '${o.name}'` })
419
456
  }
420
457
  ]
421
458
  };
422
- const n = {
423
- key: i,
424
- active: e.isActive(i),
459
+ const l = {
460
+ key: o.name,
461
+ active: e.isActive(o.name),
425
462
  definition: {
426
- name: a.name,
427
- description: a.description,
428
- modules: a.modules ?? [],
429
- decisionCriteria: a.decisionCriteria ?? void 0
463
+ name: i.name,
464
+ description: i.description,
465
+ modules: i.modules ?? [],
466
+ decisionCriteria: i.decisionCriteria ?? void 0
430
467
  },
431
- tools: l[i] ?? []
468
+ tools: n[o.name] ?? []
432
469
  };
433
470
  return {
434
- content: [{ type: "text", text: JSON.stringify(n) }]
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 y {
489
+ class T {
453
490
  constructor(e) {
454
- this.toolsetValidator = new Y();
455
- const t = e.startup ?? {}, s = this.resolveStartupConfig(t, e.catalog);
456
- this.mode = s.mode, this.resolver = new U({
491
+ this.initError = null, this.toolsetValidator = new q();
492
+ const s = e.startup ?? {}, t = this.resolveStartupConfig(s, e.catalog);
493
+ this.mode = t.mode, this.resolver = new J({
457
494
  catalog: e.catalog,
458
495
  moduleLoaders: e.moduleLoaders
459
496
  });
460
- const o = new I({
497
+ const o = new P({
461
498
  namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
462
499
  });
463
- this.manager = new W({
500
+ this.manager = new G({
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 && H(e.server, this.manager, { mode: this.mode });
471
- const i = s.toolsets;
472
- i === "ALL" ? this.manager.enableToolsets(this.resolver.getAvailableToolsets()) : Array.isArray(i) && i.length > 0 && this.manager.enableToolsets(i);
507
+ }), e.registerMetaTools !== !1 && K(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;
473
533
  }
474
- resolveStartupConfig(e, t) {
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;
541
+ }
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 s = Array.isArray(e.toolsets) ? e.toolsets : [], o = [];
482
- for (const i of s) {
483
- const { isValid: a, sanitized: l, error: n } = this.toolsetValidator.validateToolsetName(i, t);
484
- a && l ? o.push(l) : n && console.warn(n);
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 (s.length > 0 && o.length === 0)
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 s = [];
564
+ const t = [];
497
565
  for (const o of e.toolsets) {
498
- const { isValid: i, sanitized: a, error: l } = this.toolsetValidator.validateToolsetName(o, t);
499
- i && a ? s.push(a) : l && console.warn(l);
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 (s.length === 0)
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: s };
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
- class P {
584
+ var p, b;
585
+ class $ {
517
586
  constructor(e = {}) {
518
- this.storage = /* @__PURE__ */ new Map(), this.maxSize = e.maxSize ?? 1e3, this.ttlMs = e.ttlMs ?? 1e3 * 60 * 60;
519
- const t = e.pruneIntervalMs ?? 1e3 * 60 * 10;
520
- this.pruneInterval = setInterval(() => this.pruneExpired(), t);
587
+ y(this, p);
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,98 @@ class P {
529
599
  return this.ttlMs;
530
600
  }
531
601
  get(e) {
532
- const t = this.storage.get(e);
533
- return t ? Date.now() - t.lastAccessed > this.ttlMs ? (this.delete(e), null) : (t.lastAccessed = Date.now(), this.storage.delete(e), this.storage.set(e, t), t.resource) : null;
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, t) {
605
+ set(e, s) {
536
606
  this.storage.size >= this.maxSize && this.evictLeastRecentlyUsed();
537
- const s = { resource: t, lastAccessed: Date.now() };
538
- this.storage.set(e, s);
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.delete(e);
616
+ const s = this.storage.get(e);
617
+ s && (this.storage.delete(e), u(this, p, b).call(this, e, s.resource));
542
618
  }
543
- stop() {
544
- this.pruneInterval && (clearInterval(this.pruneInterval), this.pruneInterval = void 0);
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();
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
+ u(this, p, b).call(this, s, t.resource);
545
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, s] of this.storage.entries())
553
- e - s.lastAccessed > this.ttlMs && this.delete(t);
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);
554
654
  }
555
655
  }
556
- class J {
557
- constructor(e, t, s = {}, o) {
558
- this.app = null, this.clientCache = new P(), this.defaultManager = e, this.createBundle = t, this.options = {
559
- host: s.host ?? "0.0.0.0",
560
- port: s.port ?? 3e3,
561
- basePath: s.basePath ?? "/",
562
- cors: s.cors ?? !0,
563
- logger: s.logger ?? !1,
564
- app: s.app
656
+ p = 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
+ b = 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
+ class Q {
674
+ constructor(e, s, t = {}, o) {
675
+ this.app = null, this.clientCache = new $({
676
+ onEvict: (i, n) => {
677
+ this.cleanupBundle(n);
678
+ }
679
+ }), this.defaultManager = e, this.createBundle = s, this.options = {
680
+ host: t.host ?? "0.0.0.0",
681
+ port: t.port ?? 3e3,
682
+ basePath: t.basePath ?? "/",
683
+ cors: t.cors ?? !0,
684
+ logger: t.logger ?? !1,
685
+ app: t.app
565
686
  }, this.configSchema = o;
566
687
  }
567
688
  async start() {
568
689
  if (this.app) return;
569
- const e = this.options.app ?? S({ logger: this.options.logger });
570
- this.options.cors && await e.register(M, { origin: !0 });
571
- const t = this.options.basePath.endsWith("/") ? this.options.basePath.slice(0, -1) : this.options.basePath;
572
- e.get(`${t}/healthz`, async () => ({ ok: !0 })), e.get(`${t}/tools`, async () => this.defaultManager.getStatus()), e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
690
+ const e = this.options.app ?? M({ logger: this.options.logger });
691
+ this.options.cors && await e.register(x, { origin: !0 });
692
+ const s = this.options.basePath.endsWith("/") ? this.options.basePath.slice(0, -1) : this.options.basePath;
693
+ 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
694
  $schema: "https://json-schema.org/draft/2020-12/schema",
574
695
  title: "MCP Session Configuration",
575
696
  description: "Schema for the /mcp endpoint configuration",
@@ -579,32 +700,32 @@ class J {
579
700
  "x-mcp-version": "1.0",
580
701
  "x-query-style": "dot+bracket"
581
702
  })), e.post(
582
- `${t}/mcp`,
583
- async (s, o) => {
584
- const i = s.headers["mcp-client-id"]?.trim(), a = i && i.length > 0 ? i : `anon-${p()}`, l = !a.startsWith("anon-");
585
- let n = l ? this.clientCache.get(a) : null;
586
- if (!n) {
587
- const f = this.createBundle(), g = f.sessions;
588
- n = {
589
- server: f.server,
590
- orchestrator: f.orchestrator,
703
+ `${s}/mcp`,
704
+ async (t, o) => {
705
+ const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : `anon-${v()}`, l = !n.startsWith("anon-");
706
+ let a = l ? this.clientCache.get(n) : null;
707
+ if (!a) {
708
+ const m = this.createBundle(), g = m.sessions;
709
+ a = {
710
+ server: m.server,
711
+ orchestrator: m.orchestrator,
591
712
  sessions: g instanceof Map ? g : /* @__PURE__ */ new Map()
592
- }, l && this.clientCache.set(a, n);
713
+ }, l && this.clientCache.set(n, a);
593
714
  }
594
- const c = s.headers["mcp-session-id"];
595
- let h;
596
- if (c && n.sessions.get(c))
597
- h = n.sessions.get(c);
598
- else if (!c && x(s.body)) {
599
- const f = p();
600
- h = new C({
601
- sessionIdGenerator: () => f,
715
+ const c = t.headers["mcp-session-id"];
716
+ let d;
717
+ if (c && a.sessions.get(c))
718
+ d = a.sessions.get(c);
719
+ else if (!c && I(t.body)) {
720
+ const m = v();
721
+ d = new E({
722
+ sessionIdGenerator: () => m,
602
723
  onsessioninitialized: (g) => {
603
- n.sessions.set(g, h);
724
+ a.sessions.set(g, d);
604
725
  }
605
726
  });
606
727
  try {
607
- await n.server.connect(h);
728
+ await a.server.connect(d);
608
729
  } catch {
609
730
  return o.code(500), {
610
731
  jsonrpc: "2.0",
@@ -612,8 +733,8 @@ class J {
612
733
  id: null
613
734
  };
614
735
  }
615
- h.onclose = () => {
616
- h?.sessionId && n.sessions.delete(h.sessionId);
736
+ d.onclose = () => {
737
+ d?.sessionId && a.sessions.delete(d.sessionId);
617
738
  };
618
739
  } else
619
740
  return o.code(400), {
@@ -621,29 +742,29 @@ class J {
621
742
  error: { code: -32e3, message: "Session not found or expired" },
622
743
  id: null
623
744
  };
624
- return await h.handleRequest(
625
- s.raw,
745
+ return await d.handleRequest(
746
+ t.raw,
626
747
  o.raw,
627
- s.body
748
+ t.body
628
749
  ), o;
629
750
  }
630
- ), e.get(`${t}/mcp`, async (s, o) => {
631
- const i = s.headers["mcp-client-id"]?.trim(), a = i && i.length > 0 ? i : "";
632
- if (!a)
751
+ ), e.get(`${s}/mcp`, async (t, o) => {
752
+ const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
753
+ if (!n)
633
754
  return o.code(400), "Missing mcp-client-id";
634
- const l = this.clientCache.get(a);
755
+ const l = this.clientCache.get(n);
635
756
  if (!l)
636
757
  return o.code(400), "Invalid or expired client";
637
- const n = s.headers["mcp-session-id"];
638
- if (!n)
758
+ const a = t.headers["mcp-session-id"];
759
+ if (!a)
639
760
  return o.code(400), "Missing mcp-session-id";
640
- const c = l.sessions.get(n);
641
- return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
761
+ const c = l.sessions.get(a);
762
+ return c ? (await c.handleRequest(t.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
642
763
  }), e.delete(
643
- `${t}/mcp`,
644
- async (s, o) => {
645
- const i = s.headers["mcp-client-id"]?.trim(), a = i && i.length > 0 ? i : "", l = s.headers["mcp-session-id"];
646
- if (!a || !l)
764
+ `${s}/mcp`,
765
+ async (t, o) => {
766
+ const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "", l = t.headers["mcp-session-id"];
767
+ if (!n || !l)
647
768
  return o.code(400), {
648
769
  jsonrpc: "2.0",
649
770
  error: {
@@ -652,8 +773,8 @@ class J {
652
773
  },
653
774
  id: null
654
775
  };
655
- const n = this.clientCache.get(a), c = n?.sessions.get(l);
656
- if (!n || !c)
776
+ const a = this.clientCache.get(n), c = a?.sessions.get(l);
777
+ if (!a || !c)
657
778
  return o.code(404), {
658
779
  jsonrpc: "2.0",
659
780
  error: { code: -32e3, message: "Session not found or expired" },
@@ -666,62 +787,84 @@ class J {
666
787
  } catch {
667
788
  }
668
789
  } finally {
669
- c?.sessionId ? n.sessions.delete(c.sessionId) : n.sessions.delete(l);
790
+ c?.sessionId ? a.sessions.delete(c.sessionId) : a.sessions.delete(l);
670
791
  }
671
792
  return o.code(204).send(), o;
672
793
  }
673
794
  ), this.options.app || await e.listen({ host: this.options.host, port: this.options.port }), this.app = e;
674
795
  }
796
+ /**
797
+ * Stops the Fastify server and cleans up all resources.
798
+ * Closes all client sessions and clears the cache.
799
+ */
675
800
  async stop() {
676
- this.app && (this.options.app || await this.app.close(), this.app = null);
801
+ this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
802
+ }
803
+ /**
804
+ * Cleans up resources associated with a client bundle.
805
+ * Closes all sessions within the bundle.
806
+ * @param bundle - The client bundle to clean up
807
+ * @private
808
+ */
809
+ cleanupBundle(e) {
810
+ for (const [s, t] of e.sessions.entries())
811
+ try {
812
+ typeof t.close == "function" && t.close().catch((o) => {
813
+ console.warn(`Error closing session ${s}:`, o);
814
+ });
815
+ } catch (o) {
816
+ console.warn(`Error closing session ${s}:`, o);
817
+ }
818
+ e.sessions.clear();
677
819
  }
678
820
  }
679
- async function de(r) {
821
+ async function ge(r) {
680
822
  const e = r.startup?.mode ?? "DYNAMIC";
681
823
  if (typeof r.createServer != "function")
682
824
  throw new Error("createMcpServer: `createServer` (factory) is required");
683
- const t = r.createServer(), s = (n) => typeof n?.server?.notification == "function", o = (n) => typeof n?.notifyToolsListChanged == "function", i = async (n) => {
825
+ const s = r.createServer(), t = (a) => typeof a?.server?.notification == "function", o = (a) => typeof a?.notifyToolsListChanged == "function", i = async (a) => {
684
826
  try {
685
- if (s(n)) {
686
- await n.server.notification({
827
+ if (t(a)) {
828
+ await a.server.notification({
687
829
  method: "notifications/tools/list_changed"
688
830
  });
689
831
  return;
690
832
  }
691
- o(n) && await n.notifyToolsListChanged();
692
- } catch {
833
+ o(a) && await a.notifyToolsListChanged();
834
+ } catch (c) {
835
+ console.warn("Failed to send tools list changed notification:", c);
693
836
  }
694
- }, a = new y({
695
- server: t,
837
+ }, n = new T({
838
+ server: s,
696
839
  catalog: r.catalog,
697
840
  moduleLoaders: r.moduleLoaders,
698
841
  exposurePolicy: r.exposurePolicy,
699
842
  context: r.context,
700
- notifyToolsListChanged: async () => i(t),
843
+ notifyToolsListChanged: async () => i(s),
701
844
  startup: r.startup,
702
845
  registerMetaTools: r.registerMetaTools !== void 0 ? r.registerMetaTools : e === "DYNAMIC"
703
- }), l = new J(
704
- a.getManager(),
846
+ }), l = new Q(
847
+ n.getManager(),
705
848
  () => {
706
849
  if (e === "STATIC")
707
- return { server: t, orchestrator: a };
708
- const n = r.createServer(), c = new y({
709
- server: n,
850
+ return { server: s, orchestrator: n };
851
+ const a = r.createServer(), c = new T({
852
+ server: a,
710
853
  catalog: r.catalog,
711
854
  moduleLoaders: r.moduleLoaders,
712
855
  exposurePolicy: r.exposurePolicy,
713
856
  context: r.context,
714
- notifyToolsListChanged: async () => i(n),
857
+ notifyToolsListChanged: async () => i(a),
715
858
  startup: r.startup,
716
859
  registerMetaTools: r.registerMetaTools !== void 0 ? r.registerMetaTools : e === "DYNAMIC"
717
860
  });
718
- return { server: n, orchestrator: c };
861
+ return { server: a, orchestrator: c };
719
862
  },
720
863
  r.http,
721
864
  r.configSchema
722
865
  );
723
866
  return {
724
- server: t,
867
+ server: s,
725
868
  start: async () => {
726
869
  await l.start();
727
870
  },
@@ -730,16 +873,16 @@ async function de(r) {
730
873
  }
731
874
  };
732
875
  }
733
- function q(r) {
734
- G(r), K(r), Q(r), X(r);
876
+ function X(r) {
877
+ Z(r), ee(r), se(r), te(r);
735
878
  }
736
- function G(r) {
879
+ function Z(r) {
737
880
  if (!r || typeof r != "object")
738
881
  throw new Error(
739
882
  "Permission configuration is required for createPermissionBasedMcpServer"
740
883
  );
741
884
  }
742
- function K(r) {
885
+ function ee(r) {
743
886
  if (!r.source)
744
887
  throw new Error('Permission source must be either "headers" or "config"');
745
888
  if (r.source !== "headers" && r.source !== "config")
@@ -747,19 +890,19 @@ function K(r) {
747
890
  `Invalid permission source: "${r.source}". Must be either "headers" or "config"`
748
891
  );
749
892
  }
750
- function Q(r) {
893
+ function se(r) {
751
894
  if (r.source === "config" && !r.staticMap && !r.resolver)
752
895
  throw new Error(
753
896
  "Config-based permissions require at least one of: staticMap or resolver function"
754
897
  );
755
898
  }
756
- function X(r) {
899
+ function te(r) {
757
900
  if (r.staticMap !== void 0) {
758
901
  if (typeof r.staticMap != "object" || r.staticMap === null)
759
902
  throw new Error(
760
903
  "staticMap must be an object mapping client IDs to toolset arrays"
761
904
  );
762
- Z(r.staticMap);
905
+ oe(r.staticMap);
763
906
  }
764
907
  if (r.resolver !== void 0 && typeof r.resolver != "function")
765
908
  throw new Error(
@@ -770,48 +913,61 @@ function X(r) {
770
913
  if (r.headerName !== void 0 && (typeof r.headerName != "string" || r.headerName.length === 0))
771
914
  throw new Error("headerName must be a non-empty string");
772
915
  }
773
- function Z(r) {
774
- for (const [e, t] of Object.entries(r))
775
- if (!Array.isArray(t))
916
+ function oe(r) {
917
+ for (const [e, s] of Object.entries(r))
918
+ if (!Array.isArray(s))
776
919
  throw new Error(
777
920
  `staticMap value for client "${e}" must be an array of toolset names`
778
921
  );
779
922
  }
780
- var m, $, E, L, N;
781
- class ee {
923
+ var f, L, j, N, k, D;
924
+ class re {
782
925
  /**
783
926
  * Creates a new PermissionResolver instance.
784
927
  * @param config - The permission configuration defining how permissions are resolved
785
928
  */
786
929
  constructor(e) {
787
- v(this, m);
788
- this.config = e, this.cache = /* @__PURE__ */ new Map();
930
+ y(this, f);
931
+ this.config = e, this.cache = /* @__PURE__ */ new Map(), this.normalizedHeaderName = (e.headerName || "mcp-toolset-permissions").toLowerCase();
789
932
  }
790
933
  /**
791
934
  * Resolves permissions for a client based on the configured source.
792
935
  * Results are cached to improve performance for subsequent requests from the same client.
793
936
  * Handles all errors gracefully by returning empty permissions on failure.
937
+ *
938
+ * Note on caching: For header-based permissions, permissions are cached by clientId.
939
+ * This means subsequent requests from the same client will use cached permissions,
940
+ * even if headers change. Use invalidateCache(clientId) to force re-resolution.
941
+ *
794
942
  * @param clientId - The unique identifier for the client
795
943
  * @param headers - Optional request headers (required for header-based permissions)
796
944
  * @returns Array of toolset names the client is allowed to access
797
945
  */
798
- resolvePermissions(e, t) {
946
+ resolvePermissions(e, s) {
799
947
  if (this.cache.has(e))
800
948
  return this.cache.get(e);
801
- let s;
949
+ let t;
802
950
  try {
803
- this.config.source === "headers" ? s = u(this, m, $).call(this, t) : s = u(this, m, E).call(this, e), Array.isArray(s) || (console.warn(
951
+ this.config.source === "headers" ? t = u(this, f, L).call(this, s) : t = u(this, f, N).call(this, e), Array.isArray(t) || (console.warn(
804
952
  `Permission resolution returned non-array for client ${e}, using empty permissions`
805
- ), s = []), s = s.filter(
953
+ ), t = []), t = t.filter(
806
954
  (o) => typeof o == "string" && o.trim().length > 0
807
955
  );
808
956
  } catch (o) {
809
957
  console.error(
810
958
  `Unexpected error resolving permissions for client ${e}:`,
811
959
  o
812
- ), s = [];
960
+ ), t = [];
813
961
  }
814
- return this.cache.set(e, s), s;
962
+ return this.cache.set(e, t), t;
963
+ }
964
+ /**
965
+ * Invalidates cached permissions for a specific client.
966
+ * Call this when you know a client's permissions have changed.
967
+ * @param clientId - The client ID to invalidate
968
+ */
969
+ invalidateCache(e) {
970
+ this.cache.delete(e);
815
971
  }
816
972
  /**
817
973
  * Clears the permission cache.
@@ -821,26 +977,43 @@ class ee {
821
977
  this.cache.clear();
822
978
  }
823
979
  }
824
- m = new WeakSet(), /**
980
+ f = new WeakSet(), /**
825
981
  * Parses permissions from request headers.
826
982
  * Extracts comma-separated toolset names from the configured header.
827
983
  * Handles malformed headers gracefully by returning empty permissions.
984
+ * Uses case-insensitive header lookup per RFC 7230.
828
985
  * @param headers - Request headers containing permission data
829
986
  * @returns Array of toolset names from headers, or empty array if header is missing/malformed
830
987
  * @private
831
988
  */
832
- $ = function(e) {
833
- const t = this.config.headerName || "mcp-toolset-permissions", s = e?.[t];
989
+ L = function(e) {
990
+ if (!e)
991
+ return [];
992
+ const s = u(this, f, j).call(this, e, this.normalizedHeaderName);
834
993
  if (!s)
835
994
  return [];
836
995
  try {
837
- return s.split(",").map((o) => o.trim()).filter((o) => o.length > 0);
838
- } catch (o) {
996
+ return s.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
997
+ } catch (t) {
839
998
  return console.warn(
840
- `Failed to parse permission header '${t}':`,
841
- o
999
+ `Failed to parse permission header '${this.normalizedHeaderName}':`,
1000
+ t
842
1001
  ), [];
843
1002
  }
1003
+ }, /**
1004
+ * Finds a header value using case-insensitive key matching.
1005
+ * HTTP headers are case-insensitive per RFC 7230.
1006
+ * @param headers - The headers object to search
1007
+ * @param normalizedKey - The lowercase key to search for
1008
+ * @returns The header value if found, undefined otherwise
1009
+ * @private
1010
+ */
1011
+ j = function(e, s) {
1012
+ if (e[s] !== void 0)
1013
+ return e[s];
1014
+ for (const [t, o] of Object.entries(e))
1015
+ if (t.toLowerCase() === s)
1016
+ return o;
844
1017
  }, /**
845
1018
  * Resolves permissions from server-side configuration.
846
1019
  * Tries resolver function first (if provided), then falls back to static map,
@@ -849,16 +1022,16 @@ $ = function(e) {
849
1022
  * @returns Array of toolset names from configuration
850
1023
  * @private
851
1024
  */
852
- E = function(e) {
1025
+ N = function(e) {
853
1026
  if (this.config.resolver) {
854
- const t = u(this, m, L).call(this, e);
855
- if (t !== null)
856
- return t;
1027
+ const s = u(this, f, k).call(this, e);
1028
+ if (s !== null)
1029
+ return s;
857
1030
  }
858
1031
  if (this.config.staticMap) {
859
- const t = u(this, m, N).call(this, e);
860
- if (t !== null)
861
- return t;
1032
+ const s = u(this, f, D).call(this, e);
1033
+ if (s !== null)
1034
+ return s;
862
1035
  }
863
1036
  return this.config.defaultPermissions || [];
864
1037
  }, /**
@@ -868,16 +1041,16 @@ E = function(e) {
868
1041
  * @returns Array of toolset names if successful, null if resolver fails or returns invalid data
869
1042
  * @private
870
1043
  */
871
- L = function(e) {
1044
+ k = function(e) {
872
1045
  try {
873
- const t = this.config.resolver(e);
874
- return Array.isArray(t) ? t : (console.warn(
1046
+ const s = this.config.resolver(e);
1047
+ return Array.isArray(s) ? s : (console.warn(
875
1048
  `Permission resolver returned non-array for client ${e}, using fallback`
876
1049
  ), null);
877
- } catch (t) {
878
- const s = t instanceof Error ? t.message : String(t);
1050
+ } catch (s) {
1051
+ const t = s instanceof Error ? s.message : String(s);
879
1052
  return console.warn(
880
- `Permission resolver declined client ${e} (${s}), trying fallback`
1053
+ `Permission resolver declined client ${e} (${t}), trying fallback`
881
1054
  ), null;
882
1055
  }
883
1056
  }, /**
@@ -887,25 +1060,37 @@ L = function(e) {
887
1060
  * @returns Array of toolset names if found, null if client not in map
888
1061
  * @private
889
1062
  */
890
- N = function(e) {
891
- const t = this.config.staticMap[e];
892
- return t !== void 0 ? Array.isArray(t) ? t : [] : null;
1063
+ D = function(e) {
1064
+ const s = this.config.staticMap[e];
1065
+ return s !== void 0 ? Array.isArray(s) ? s : [] : null;
893
1066
  };
894
- function se(r, e) {
895
- return async (t) => {
896
- const s = e.resolvePermissions(
897
- t.clientId,
898
- t.headers
899
- ), o = r(s), i = o.orchestrator.getManager();
900
- return s.length > 0 && await i.enableToolsets(s), {
1067
+ function ie(r, e) {
1068
+ return async (s) => {
1069
+ const t = e.resolvePermissions(
1070
+ s.clientId,
1071
+ s.headers
1072
+ ), o = r(t), i = o.orchestrator.getManager(), n = [], l = [];
1073
+ if (t.length > 0) {
1074
+ const a = await i.enableToolsets(t);
1075
+ for (const c of a.results)
1076
+ c.success ? n.push(c.name) : (l.push(c.name), console.warn(
1077
+ `Failed to enable toolset '${c.name}' for client '${s.clientId}': ${c.message}`
1078
+ ));
1079
+ if (n.length === 0 && l.length > 0)
1080
+ throw new Error(
1081
+ `All requested toolsets failed to enable for client '${s.clientId}'. Requested: [${t.join(", ")}]. Check that toolset names in permissions match the catalog.`
1082
+ );
1083
+ }
1084
+ return {
901
1085
  server: o.server,
902
1086
  orchestrator: o.orchestrator,
903
- allowedToolsets: s
1087
+ allowedToolsets: n,
1088
+ failedToolsets: l
904
1089
  };
905
1090
  };
906
1091
  }
907
- var d, D, j, z, O, R, k, F, V, _;
908
- class te {
1092
+ var h, z, R, O, F, V, _, B, Y, H, U;
1093
+ class ne {
909
1094
  /**
910
1095
  * Creates a new PermissionAwareFastifyTransport instance.
911
1096
  * @param defaultManager - Default tool manager for status endpoints
@@ -913,15 +1098,19 @@ class te {
913
1098
  * @param options - Transport configuration options
914
1099
  * @param configSchema - Optional JSON schema for configuration discovery
915
1100
  */
916
- constructor(e, t, s = {}, o) {
917
- v(this, d);
918
- this.app = null, this.clientCache = new P(), this.defaultManager = e, this.createPermissionAwareBundle = t, this.options = {
919
- host: s.host ?? "0.0.0.0",
920
- port: s.port ?? 3e3,
921
- basePath: s.basePath ?? "/",
922
- cors: s.cors ?? !0,
923
- logger: s.logger ?? !1,
924
- app: s.app
1101
+ constructor(e, s, t = {}, o) {
1102
+ y(this, h);
1103
+ this.app = null, this.clientCache = new $({
1104
+ onEvict: (i, n) => {
1105
+ u(this, h, z).call(this, n);
1106
+ }
1107
+ }), this.defaultManager = e, this.createPermissionAwareBundle = s, this.options = {
1108
+ host: t.host ?? "0.0.0.0",
1109
+ port: t.port ?? 3e3,
1110
+ basePath: t.basePath ?? "/",
1111
+ cors: t.cors ?? !0,
1112
+ logger: t.logger ?? !1,
1113
+ app: t.app
925
1114
  }, this.configSchema = o;
926
1115
  }
927
1116
  /**
@@ -930,25 +1119,42 @@ class te {
930
1119
  */
931
1120
  async start() {
932
1121
  if (this.app) return;
933
- const e = this.options.app ?? S({ logger: this.options.logger });
934
- this.options.cors && await e.register(M, { origin: !0 });
935
- const t = u(this, d, D).call(this, this.options.basePath);
936
- u(this, d, j).call(this, e, t), u(this, d, z).call(this, e, t), u(this, d, O).call(this, e, t), u(this, d, R).call(this, e, t), u(this, d, k).call(this, e, t), u(this, d, F).call(this, e, t), this.options.app || await e.listen({ host: this.options.host, port: this.options.port }), this.app = e;
1122
+ const e = this.options.app ?? M({ logger: this.options.logger });
1123
+ this.options.cors && await e.register(x, { origin: !0 });
1124
+ const s = u(this, h, R).call(this, this.options.basePath);
1125
+ u(this, h, O).call(this, e, s), u(this, h, F).call(this, e, s), u(this, h, V).call(this, e, s), u(this, h, _).call(this, e, s), u(this, h, B).call(this, e, s), u(this, h, Y).call(this, e, s), this.options.app || await e.listen({ host: this.options.host, port: this.options.port }), this.app = e;
937
1126
  }
938
1127
  /**
939
- * Stops the Fastify server and cleans up resources.
1128
+ * Stops the Fastify server and cleans up all resources.
1129
+ * Closes all client sessions and clears the cache.
940
1130
  */
941
1131
  async stop() {
942
- this.app && (this.options.app || await this.app.close(), this.app = null);
1132
+ this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
943
1133
  }
944
1134
  }
945
- d = new WeakSet(), /**
1135
+ h = new WeakSet(), /**
1136
+ * Cleans up resources associated with a client bundle.
1137
+ * Closes all sessions within the bundle.
1138
+ * @param bundle - The client bundle to clean up
1139
+ * @private
1140
+ */
1141
+ z = function(e) {
1142
+ for (const [s, t] of e.sessions.entries())
1143
+ try {
1144
+ typeof t.close == "function" && t.close().catch((o) => {
1145
+ console.warn(`Error closing session ${s}:`, o);
1146
+ });
1147
+ } catch (o) {
1148
+ console.warn(`Error closing session ${s}:`, o);
1149
+ }
1150
+ e.sessions.clear();
1151
+ }, /**
946
1152
  * Normalizes the base path by removing trailing slashes.
947
1153
  * @param basePath - The base path to normalize
948
1154
  * @returns Normalized base path without trailing slash
949
1155
  * @private
950
1156
  */
951
- D = function(e) {
1157
+ R = function(e) {
952
1158
  return e.endsWith("/") ? e.slice(0, -1) : e;
953
1159
  }, /**
954
1160
  * Registers the health check endpoint.
@@ -956,24 +1162,24 @@ D = function(e) {
956
1162
  * @param base - Base path for routes
957
1163
  * @private
958
1164
  */
959
- j = function(e, t) {
960
- e.get(`${t}/healthz`, async () => ({ ok: !0 }));
1165
+ O = function(e, s) {
1166
+ e.get(`${s}/healthz`, async () => ({ ok: !0 }));
961
1167
  }, /**
962
1168
  * Registers the tools status endpoint.
963
1169
  * @param app - Fastify instance
964
1170
  * @param base - Base path for routes
965
1171
  * @private
966
1172
  */
967
- z = function(e, t) {
968
- e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
1173
+ F = function(e, s) {
1174
+ e.get(`${s}/tools`, async () => this.defaultManager.getStatus());
969
1175
  }, /**
970
1176
  * Registers the MCP configuration discovery endpoint.
971
1177
  * @param app - Fastify instance
972
1178
  * @param base - Base path for routes
973
1179
  * @private
974
1180
  */
975
- O = function(e, t) {
976
- e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
1181
+ V = function(e, s) {
1182
+ e.get(`${s}/.well-known/mcp-config`, async (t, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
977
1183
  $schema: "https://json-schema.org/draft/2020-12/schema",
978
1184
  title: "MCP Session Configuration",
979
1185
  description: "Schema for the /mcp endpoint configuration",
@@ -990,37 +1196,42 @@ O = function(e, t) {
990
1196
  * @param base - Base path for routes
991
1197
  * @private
992
1198
  */
993
- R = function(e, t) {
1199
+ _ = function(e, s) {
994
1200
  e.post(
995
- `${t}/mcp`,
996
- async (s, o) => {
997
- const i = u(this, d, V).call(this, s), a = !i.clientId.startsWith("anon-");
998
- let l = a ? this.clientCache.get(i.clientId) : null;
1201
+ `${s}/mcp`,
1202
+ async (t, o) => {
1203
+ const i = u(this, h, H).call(this, t), n = !i.clientId.startsWith("anon-");
1204
+ let l = n ? this.clientCache.get(i.clientId) : null;
999
1205
  if (!l)
1000
1206
  try {
1001
- const h = await this.createPermissionAwareBundle(i), f = h.sessions;
1207
+ const d = await this.createPermissionAwareBundle(i);
1208
+ d.failedToolsets.length > 0 && console.warn(
1209
+ `Client ${i.clientId} had ${d.failedToolsets.length} toolsets fail to enable: [${d.failedToolsets.join(", ")}]. Successfully enabled: [${d.allowedToolsets.join(", ")}]`
1210
+ );
1211
+ const m = d.sessions;
1002
1212
  l = {
1003
- server: h.server,
1004
- orchestrator: h.orchestrator,
1005
- allowedToolsets: h.allowedToolsets,
1006
- sessions: f instanceof Map ? f : /* @__PURE__ */ new Map()
1007
- }, a && this.clientCache.set(i.clientId, l);
1008
- } catch (h) {
1213
+ server: d.server,
1214
+ orchestrator: d.orchestrator,
1215
+ allowedToolsets: d.allowedToolsets,
1216
+ failedToolsets: d.failedToolsets,
1217
+ sessions: m instanceof Map ? m : /* @__PURE__ */ new Map()
1218
+ }, n && this.clientCache.set(i.clientId, l);
1219
+ } catch (d) {
1009
1220
  return console.error(
1010
1221
  `Failed to create permission-aware bundle for client ${i.clientId}:`,
1011
- h
1012
- ), o.code(403), u(this, d, _).call(this, "Access denied");
1222
+ d
1223
+ ), o.code(403), u(this, h, U).call(this, "Access denied");
1013
1224
  }
1014
- const n = s.headers["mcp-session-id"];
1225
+ const a = t.headers["mcp-session-id"];
1015
1226
  let c;
1016
- if (n && l.sessions.get(n))
1017
- c = l.sessions.get(n);
1018
- else if (!n && x(s.body)) {
1019
- const h = p();
1020
- c = new C({
1021
- sessionIdGenerator: () => h,
1022
- onsessioninitialized: (f) => {
1023
- l.sessions.set(f, c);
1227
+ if (a && l.sessions.get(a))
1228
+ c = l.sessions.get(a);
1229
+ else if (!a && I(t.body)) {
1230
+ const d = v();
1231
+ c = new E({
1232
+ sessionIdGenerator: () => d,
1233
+ onsessioninitialized: (m) => {
1234
+ l.sessions.set(m, c);
1024
1235
  }
1025
1236
  });
1026
1237
  try {
@@ -1042,9 +1253,9 @@ R = function(e, t) {
1042
1253
  id: null
1043
1254
  };
1044
1255
  return await c.handleRequest(
1045
- s.raw,
1256
+ t.raw,
1046
1257
  o.raw,
1047
- s.body
1258
+ t.body
1048
1259
  ), o;
1049
1260
  }
1050
1261
  );
@@ -1054,19 +1265,19 @@ R = function(e, t) {
1054
1265
  * @param base - Base path for routes
1055
1266
  * @private
1056
1267
  */
1057
- k = function(e, t) {
1058
- e.get(`${t}/mcp`, async (s, o) => {
1059
- const i = s.headers["mcp-client-id"]?.trim(), a = i && i.length > 0 ? i : "";
1060
- if (!a)
1268
+ B = function(e, s) {
1269
+ e.get(`${s}/mcp`, async (t, o) => {
1270
+ const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
1271
+ if (!n)
1061
1272
  return o.code(400), "Missing mcp-client-id";
1062
- const l = this.clientCache.get(a);
1273
+ const l = this.clientCache.get(n);
1063
1274
  if (!l)
1064
1275
  return o.code(400), "Invalid or expired client";
1065
- const n = s.headers["mcp-session-id"];
1066
- if (!n)
1276
+ const a = t.headers["mcp-session-id"];
1277
+ if (!a)
1067
1278
  return o.code(400), "Missing mcp-session-id";
1068
- const c = l.sessions.get(n);
1069
- return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
1279
+ const c = l.sessions.get(a);
1280
+ return c ? (await c.handleRequest(t.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
1070
1281
  });
1071
1282
  }, /**
1072
1283
  * Registers the DELETE /mcp endpoint for session termination.
@@ -1074,12 +1285,12 @@ k = function(e, t) {
1074
1285
  * @param base - Base path for routes
1075
1286
  * @private
1076
1287
  */
1077
- F = function(e, t) {
1288
+ Y = function(e, s) {
1078
1289
  e.delete(
1079
- `${t}/mcp`,
1080
- async (s, o) => {
1081
- const i = s.headers["mcp-client-id"]?.trim(), a = i && i.length > 0 ? i : "", l = s.headers["mcp-session-id"];
1082
- if (!a || !l)
1290
+ `${s}/mcp`,
1291
+ async (t, o) => {
1292
+ const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "", l = t.headers["mcp-session-id"];
1293
+ if (!n || !l)
1083
1294
  return o.code(400), {
1084
1295
  jsonrpc: "2.0",
1085
1296
  error: {
@@ -1088,8 +1299,8 @@ F = function(e, t) {
1088
1299
  },
1089
1300
  id: null
1090
1301
  };
1091
- const n = this.clientCache.get(a), c = n?.sessions.get(l);
1092
- if (!n || !c)
1302
+ const a = this.clientCache.get(n), c = a?.sessions.get(l);
1303
+ if (!a || !c)
1093
1304
  return o.code(404), {
1094
1305
  jsonrpc: "2.0",
1095
1306
  error: { code: -32e3, message: "Session not found or expired" },
@@ -1102,7 +1313,7 @@ F = function(e, t) {
1102
1313
  } catch {
1103
1314
  }
1104
1315
  } finally {
1105
- c?.sessionId ? n.sessions.delete(c.sessionId) : n.sessions.delete(l);
1316
+ c?.sessionId ? a.sessions.delete(c.sessionId) : a.sessions.delete(l);
1106
1317
  }
1107
1318
  return o.code(204).send(), o;
1108
1319
  }
@@ -1114,11 +1325,11 @@ F = function(e, t) {
1114
1325
  * @returns Client request context with ID and headers
1115
1326
  * @private
1116
1327
  */
1117
- V = function(e) {
1118
- const t = e.headers["mcp-client-id"]?.trim(), s = t && t.length > 0 ? t : `anon-${p()}`, o = {};
1119
- for (const [i, a] of Object.entries(e.headers))
1120
- typeof a == "string" && (o[i] = a);
1121
- return { clientId: s, headers: o };
1328
+ H = function(e) {
1329
+ const s = e.headers["mcp-client-id"]?.trim(), t = s && s.length > 0 ? s : `anon-${v()}`, o = {};
1330
+ for (const [i, n] of Object.entries(e.headers))
1331
+ typeof n == "string" && (o[i] = n);
1332
+ return { clientId: t, headers: o };
1122
1333
  }, /**
1123
1334
  * Creates a safe error response that doesn't expose unauthorized toolset information.
1124
1335
  * Used for permission-related errors to prevent information leakage.
@@ -1127,22 +1338,37 @@ V = function(e) {
1127
1338
  * @returns JSON-RPC error response object
1128
1339
  * @private
1129
1340
  */
1130
- _ = function(e = "Access denied", t = -32e3) {
1341
+ U = function(e = "Access denied", s = -32e3) {
1131
1342
  return {
1132
1343
  jsonrpc: "2.0",
1133
1344
  error: {
1134
- code: t,
1345
+ code: s,
1135
1346
  message: e
1136
1347
  },
1137
1348
  id: null
1138
1349
  };
1139
1350
  };
1140
- async function he(r) {
1351
+ function ae(r) {
1352
+ if (!r) return;
1353
+ const e = {
1354
+ namespaceToolsWithSetKey: r.namespaceToolsWithSetKey
1355
+ };
1356
+ return r.allowlist !== void 0 && console.warn(
1357
+ "Permission-based servers: exposurePolicy.allowlist is ignored. Allowed toolsets are determined by client permissions."
1358
+ ), r.denylist !== void 0 && console.warn(
1359
+ "Permission-based servers: exposurePolicy.denylist is ignored. Use permission configuration to control toolset access."
1360
+ ), r.maxActiveToolsets !== void 0 && console.warn(
1361
+ "Permission-based servers: exposurePolicy.maxActiveToolsets is ignored. Toolset count is determined by client permissions."
1362
+ ), r.onLimitExceeded !== void 0 && console.warn(
1363
+ "Permission-based servers: exposurePolicy.onLimitExceeded is ignored. No toolset limits are enforced."
1364
+ ), e;
1365
+ }
1366
+ async function pe(r) {
1141
1367
  if (!r.permissions)
1142
1368
  throw new Error(
1143
1369
  "Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
1144
1370
  );
1145
- if (q(r.permissions), r.startup)
1371
+ if (X(r.permissions), r.startup)
1146
1372
  throw new Error(
1147
1373
  "Permission-based servers determine toolsets from client permissions. The 'startup' option is not allowed. Remove it from your configuration."
1148
1374
  );
@@ -1150,23 +1376,25 @@ async function he(r) {
1150
1376
  throw new Error(
1151
1377
  "createPermissionBasedMcpServer: `createServer` (factory) is required"
1152
1378
  );
1153
- const e = new ee(r.permissions), t = r.createServer(), s = new y({
1379
+ const e = ae(
1380
+ r.exposurePolicy
1381
+ ), s = new re(r.permissions), t = r.createServer(), o = new T({
1154
1382
  server: t,
1155
1383
  catalog: r.catalog,
1156
1384
  moduleLoaders: r.moduleLoaders,
1157
- exposurePolicy: r.exposurePolicy,
1385
+ exposurePolicy: e,
1158
1386
  context: r.context,
1159
1387
  notifyToolsListChanged: void 0,
1160
1388
  // No notifications in STATIC mode
1161
1389
  startup: { mode: "STATIC", toolsets: [] },
1162
1390
  registerMetaTools: !1
1163
- }), o = se(
1164
- (a) => {
1165
- const l = r.createServer(), n = new y({
1166
- server: l,
1391
+ }), i = ie(
1392
+ (l) => {
1393
+ const a = r.createServer(), c = new T({
1394
+ server: a,
1167
1395
  catalog: r.catalog,
1168
1396
  moduleLoaders: r.moduleLoaders,
1169
- exposurePolicy: r.exposurePolicy,
1397
+ exposurePolicy: e,
1170
1398
  context: r.context,
1171
1399
  notifyToolsListChanged: void 0,
1172
1400
  // No notifications in STATIC mode
@@ -1175,31 +1403,31 @@ async function he(r) {
1175
1403
  registerMetaTools: !1
1176
1404
  // No meta-tools - toolsets are fixed per client
1177
1405
  });
1178
- return { server: l, orchestrator: n };
1406
+ return { server: a, orchestrator: c };
1179
1407
  },
1180
- e
1181
- ), i = new te(
1182
- s.getManager(),
1183
- o,
1408
+ s
1409
+ ), n = new ne(
1410
+ o.getManager(),
1411
+ i,
1184
1412
  r.http,
1185
1413
  r.configSchema
1186
1414
  );
1187
1415
  return {
1188
1416
  server: t,
1189
1417
  start: async () => {
1190
- await i.start();
1418
+ await n.start();
1191
1419
  },
1192
1420
  close: async () => {
1193
1421
  try {
1194
- await i.stop();
1422
+ await n.stop();
1195
1423
  } finally {
1196
- e.clearCache();
1424
+ s.clearCache();
1197
1425
  }
1198
1426
  }
1199
1427
  };
1200
1428
  }
1201
1429
  export {
1202
- de as createMcpServer,
1203
- he as createPermissionBasedMcpServer
1430
+ ge as createMcpServer,
1431
+ pe as createPermissionBasedMcpServer
1204
1432
  };
1205
1433
  //# sourceMappingURL=index.js.map