toolception 0.2.5 → 0.3.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,10 +1,16 @@
1
- import { z as f } from "zod";
2
- import p from "fastify";
3
- import A from "@fastify/cors";
4
- import { randomUUID as m } from "node:crypto";
5
- import { StreamableHTTPServerTransport as w } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
- import { isInitializeRequest as b } from "@modelcontextprotocol/sdk/types.js";
7
- const g = {
1
+ var w = (r) => {
2
+ throw TypeError(r);
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 = {
8
14
  dynamic: [
9
15
  "dynamic-tool-discovery",
10
16
  "dynamicToolDiscovery",
@@ -12,89 +18,89 @@ const g = {
12
18
  ],
13
19
  toolsets: ["tool-sets", "toolSets", "FMP_TOOL_SETS"]
14
20
  };
15
- class S {
21
+ class Y {
16
22
  constructor(e = {}) {
17
23
  this.keys = {
18
- dynamic: e.keys?.dynamic ?? g.dynamic,
19
- toolsets: e.keys?.toolsets ?? g.toolsets
24
+ dynamic: e.keys?.dynamic ?? b.dynamic,
25
+ toolsets: e.keys?.toolsets ?? b.toolsets
20
26
  };
21
27
  }
22
- resolveMode(e, s) {
23
- return this.isDynamicEnabled(s) ? "DYNAMIC" : this.getToolsetsString(s) ? "STATIC" : this.isDynamicEnabled(e) ? "DYNAMIC" : this.getToolsetsString(e) ? "STATIC" : null;
28
+ resolveMode(e, t) {
29
+ return this.isDynamicEnabled(t) ? "DYNAMIC" : this.getToolsetsString(t) ? "STATIC" : this.isDynamicEnabled(e) ? "DYNAMIC" : this.getToolsetsString(e) ? "STATIC" : null;
24
30
  }
25
- parseCommaSeparatedToolSets(e, s) {
31
+ parseCommaSeparatedToolSets(e, t) {
26
32
  if (!e || typeof e != "string") return [];
27
- const t = e.split(",").map((i) => i.trim()).filter((i) => i.length > 0), o = new Set(Object.keys(s)), r = [];
28
- for (const i of t)
29
- o.has(i) ? r.push(i) : console.warn(
30
- `Invalid toolset '${i}' ignored. Available: ${Array.from(
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(
31
37
  o
32
38
  ).join(", ")}`
33
39
  );
34
- return r;
40
+ return i;
35
41
  }
36
- getModulesForToolSets(e, s) {
37
- const t = /* @__PURE__ */ new Set();
42
+ getModulesForToolSets(e, t) {
43
+ const s = /* @__PURE__ */ new Set();
38
44
  for (const o of e) {
39
- const r = s[o];
40
- r && (r.modules || []).forEach((i) => t.add(i));
45
+ const i = t[o];
46
+ i && (i.modules || []).forEach((a) => s.add(a));
41
47
  }
42
- return Array.from(t);
48
+ return Array.from(s);
43
49
  }
44
- validateToolsetName(e, s) {
50
+ validateToolsetName(e, t) {
45
51
  if (!e || typeof e != "string")
46
52
  return {
47
53
  isValid: !1,
48
54
  error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${Object.keys(
49
- s
55
+ t
50
56
  ).join(", ")}`
51
57
  };
52
- const t = e.trim();
53
- return t.length === 0 ? {
58
+ const s = e.trim();
59
+ return s.length === 0 ? {
54
60
  isValid: !1,
55
61
  error: `Empty toolset name provided. Available toolsets: ${Object.keys(
56
- s
62
+ t
57
63
  ).join(", ")}`
58
- } : s[t] ? { isValid: !0, sanitized: t } : {
64
+ } : t[s] ? { isValid: !0, sanitized: s } : {
59
65
  isValid: !1,
60
- error: `Toolset '${t}' not found. Available toolsets: ${Object.keys(
61
- s
66
+ error: `Toolset '${s}' not found. Available toolsets: ${Object.keys(
67
+ t
62
68
  ).join(", ")}`
63
69
  };
64
70
  }
65
- validateToolsetModules(e, s) {
71
+ validateToolsetModules(e, t) {
66
72
  try {
67
- const t = this.getModulesForToolSets(e, s);
68
- return !t || t.length === 0 ? {
73
+ const s = this.getModulesForToolSets(e, t);
74
+ return !s || s.length === 0 ? {
69
75
  isValid: !1,
70
76
  error: `No modules found for toolsets: ${e.join(", ")}`
71
- } : { isValid: !0, modules: t };
72
- } catch (t) {
77
+ } : { isValid: !0, modules: s };
78
+ } catch (s) {
73
79
  return {
74
80
  isValid: !1,
75
- error: `Error resolving modules for ${e.join(", ")}: ${t instanceof Error ? t.message : "Unknown error"}`
81
+ error: `Error resolving modules for ${e.join(", ")}: ${s instanceof Error ? s.message : "Unknown error"}`
76
82
  };
77
83
  }
78
84
  }
79
85
  isDynamicEnabled(e) {
80
86
  if (!e) return !1;
81
- for (const s of this.keys.dynamic) {
82
- const t = e[s];
83
- if (t === !0 || typeof t == "string" && t.trim().toLowerCase() === "true")
87
+ for (const t of this.keys.dynamic) {
88
+ const s = e[t];
89
+ if (s === !0 || typeof s == "string" && s.trim().toLowerCase() === "true")
84
90
  return !0;
85
91
  }
86
92
  return !1;
87
93
  }
88
94
  getToolsetsString(e) {
89
95
  if (e)
90
- for (const s of this.keys.toolsets) {
91
- const t = e[s];
92
- if (typeof t == "string" && t.trim().length > 0)
93
- return t;
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;
94
100
  }
95
101
  }
96
102
  }
97
- class x {
103
+ class U {
98
104
  constructor(e) {
99
105
  this.catalog = e.catalog, this.moduleLoaders = e.moduleLoaders ?? {};
100
106
  }
@@ -112,80 +118,80 @@ class x {
112
118
  ", "
113
119
  )}`
114
120
  };
115
- const s = e.trim();
116
- return s.length === 0 ? {
121
+ const t = e.trim();
122
+ return t.length === 0 ? {
117
123
  isValid: !1,
118
124
  error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(
119
125
  ", "
120
126
  )}`
121
- } : this.catalog[s] ? { isValid: !0, sanitized: s } : {
127
+ } : this.catalog[t] ? { isValid: !0, sanitized: t } : {
122
128
  isValid: !1,
123
- error: `Toolset '${s}' not found. Available toolsets: ${this.getAvailableToolsets().join(
129
+ error: `Toolset '${t}' not found. Available toolsets: ${this.getAvailableToolsets().join(
124
130
  ", "
125
131
  )}`
126
132
  };
127
133
  }
128
- async resolveToolsForToolsets(e, s) {
129
- const t = [];
134
+ async resolveToolsForToolsets(e, t) {
135
+ const s = [];
130
136
  for (const o of e) {
131
- const r = this.catalog[o];
132
- if (r && (Array.isArray(r.tools) && r.tools.length > 0 && t.push(...r.tools), Array.isArray(r.modules) && r.modules.length > 0))
133
- for (const i of r.modules) {
134
- const l = this.moduleLoaders[i];
137
+ 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];
135
141
  if (l)
136
142
  try {
137
- const n = await l(s);
138
- Array.isArray(n) && n.length > 0 && t.push(...n);
143
+ const n = await l(t);
144
+ Array.isArray(n) && n.length > 0 && s.push(...n);
139
145
  } catch (n) {
140
146
  console.warn(
141
- `Module loader '${i}' failed for toolset '${o}':`,
147
+ `Module loader '${a}' failed for toolset '${o}':`,
142
148
  n
143
149
  );
144
150
  }
145
151
  }
146
152
  }
147
- return t;
153
+ return s;
148
154
  }
149
155
  }
150
- class T extends Error {
151
- constructor(e, s, t, o) {
152
- super(e), this.name = "ToolingError", this.code = s, this.details = t;
156
+ class A extends Error {
157
+ constructor(e, t, s, o) {
158
+ super(e), this.name = "ToolingError", this.code = t, this.details = s;
153
159
  }
154
160
  }
155
- class v {
161
+ class I {
156
162
  constructor(e = {}) {
157
163
  this.names = /* @__PURE__ */ new Set(), this.toolsetToNames = /* @__PURE__ */ new Map(), this.options = {
158
164
  namespaceWithToolset: e.namespaceWithToolset ?? !0
159
165
  };
160
166
  }
161
- getSafeName(e, s) {
162
- return !this.options.namespaceWithToolset || s.startsWith(`${e}.`) ? s : `${e}.${s}`;
167
+ getSafeName(e, t) {
168
+ return !this.options.namespaceWithToolset || t.startsWith(`${e}.`) ? t : `${e}.${t}`;
163
169
  }
164
170
  has(e) {
165
171
  return this.names.has(e);
166
172
  }
167
173
  add(e) {
168
174
  if (this.names.has(e))
169
- throw new T(
175
+ throw new A(
170
176
  `Tool name collision: '${e}' already registered`,
171
177
  "E_TOOL_NAME_CONFLICT"
172
178
  );
173
179
  this.names.add(e);
174
180
  }
175
- addForToolset(e, s) {
176
- this.add(s);
177
- const t = this.toolsetToNames.get(e) ?? /* @__PURE__ */ new Set();
178
- t.add(s), this.toolsetToNames.set(e, t);
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);
179
185
  }
180
- mapAndValidate(e, s) {
181
- return s.map((t) => {
182
- const o = this.getSafeName(e, t.name);
186
+ mapAndValidate(e, t) {
187
+ return t.map((s) => {
188
+ const o = this.getSafeName(e, s.name);
183
189
  if (this.has(o))
184
- throw new T(
190
+ throw new A(
185
191
  `Tool name collision for '${o}'`,
186
192
  "E_TOOL_NAME_CONFLICT"
187
193
  );
188
- return { ...t, name: o };
194
+ return { ...s, name: o };
189
195
  });
190
196
  }
191
197
  list() {
@@ -193,14 +199,14 @@ class v {
193
199
  }
194
200
  listByToolset() {
195
201
  const e = {};
196
- for (const [s, t] of this.toolsetToNames.entries())
197
- e[s] = Array.from(t);
202
+ for (const [t, s] of this.toolsetToNames.entries())
203
+ e[t] = Array.from(s);
198
204
  return e;
199
205
  }
200
206
  }
201
- class C {
207
+ class W {
202
208
  constructor(e) {
203
- 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 v({ namespaceWithToolset: !0 });
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 });
204
210
  }
205
211
  getAvailableToolsets() {
206
212
  return this.resolver.getAvailableToolsets();
@@ -215,81 +221,81 @@ class C {
215
221
  return this.activeToolsets.has(e);
216
222
  }
217
223
  async enableToolset(e) {
218
- const s = this.resolver.validateToolsetName(e);
219
- if (!s.isValid || !s.sanitized)
224
+ const t = this.resolver.validateToolsetName(e);
225
+ if (!t.isValid || !t.sanitized)
220
226
  return {
221
227
  success: !1,
222
- message: s.error || "Unknown validation error"
228
+ message: t.error || "Unknown validation error"
223
229
  };
224
- const t = s.sanitized;
225
- if (this.activeToolsets.has(t))
230
+ const s = t.sanitized;
231
+ if (this.activeToolsets.has(s))
226
232
  return {
227
233
  success: !1,
228
- message: `Toolset '${t}' is already enabled.`
234
+ message: `Toolset '${s}' is already enabled.`
229
235
  };
230
236
  try {
231
237
  const o = await this.resolver.resolveToolsForToolsets(
232
- [t],
238
+ [s],
233
239
  this.context
234
240
  );
235
- if (this.exposurePolicy?.allowlist && !this.exposurePolicy.allowlist.includes(t))
241
+ if (this.exposurePolicy?.allowlist && !this.exposurePolicy.allowlist.includes(s))
236
242
  return {
237
243
  success: !1,
238
- message: `Toolset '${t}' is not allowed by policy.`
244
+ message: `Toolset '${s}' is not allowed by policy.`
239
245
  };
240
- if (this.exposurePolicy?.denylist && this.exposurePolicy.denylist.includes(t))
246
+ if (this.exposurePolicy?.denylist && this.exposurePolicy.denylist.includes(s))
241
247
  return {
242
248
  success: !1,
243
- message: `Toolset '${t}' is denied by policy.`
249
+ message: `Toolset '${s}' is denied by policy.`
244
250
  };
245
251
  if (this.exposurePolicy?.maxActiveToolsets !== void 0 && this.activeToolsets.size + 1 > this.exposurePolicy.maxActiveToolsets)
246
252
  return this.exposurePolicy.onLimitExceeded?.(
247
- [t],
253
+ [s],
248
254
  Array.from(this.activeToolsets)
249
255
  ), {
250
256
  success: !1,
251
257
  message: `Activation exceeds maxActiveToolsets (${this.exposurePolicy.maxActiveToolsets}).`
252
258
  };
253
259
  if (o && o.length > 0) {
254
- const r = this.toolRegistry.mapAndValidate(
255
- t,
260
+ const i = this.toolRegistry.mapAndValidate(
261
+ s,
256
262
  o
257
263
  );
258
- this.registerDirectTools(r, t);
264
+ this.registerDirectTools(i, s);
259
265
  }
260
- this.activeToolsets.add(t);
266
+ this.activeToolsets.add(s);
261
267
  try {
262
268
  await this.onToolsListChanged?.();
263
- } catch (r) {
264
- console.warn("Failed to send tool list change notification:", r);
269
+ } catch (i) {
270
+ console.warn("Failed to send tool list change notification:", i);
265
271
  }
266
272
  return {
267
273
  success: !0,
268
- message: `Toolset '${t}' enabled successfully. Registered ${o?.length ?? 0} tools.`
274
+ message: `Toolset '${s}' enabled successfully. Registered ${o?.length ?? 0} tools.`
269
275
  };
270
276
  } catch (o) {
271
- return this.activeToolsets.delete(t), {
277
+ return this.activeToolsets.delete(s), {
272
278
  success: !1,
273
- message: `Failed to enable toolset '${t}': ${o instanceof Error ? o.message : "Unknown error"}`
279
+ message: `Failed to enable toolset '${s}': ${o instanceof Error ? o.message : "Unknown error"}`
274
280
  };
275
281
  }
276
282
  }
277
283
  async disableToolset(e) {
278
- const s = this.resolver.validateToolsetName(e);
279
- if (!s.isValid || !s.sanitized) {
284
+ const t = this.resolver.validateToolsetName(e);
285
+ if (!t.isValid || !t.sanitized) {
280
286
  const o = Array.from(this.activeToolsets).join(", ") || "none";
281
287
  return {
282
288
  success: !1,
283
- message: `${s.error || "Unknown validation error"} Active toolsets: ${o}`
289
+ message: `${t.error || "Unknown validation error"} Active toolsets: ${o}`
284
290
  };
285
291
  }
286
- const t = s.sanitized;
287
- if (!this.activeToolsets.has(t))
292
+ const s = t.sanitized;
293
+ if (!this.activeToolsets.has(s))
288
294
  return {
289
295
  success: !1,
290
- message: `Toolset '${t}' is not currently active. Active toolsets: ${Array.from(this.activeToolsets).join(", ") || "none"}`
296
+ message: `Toolset '${s}' is not currently active. Active toolsets: ${Array.from(this.activeToolsets).join(", ") || "none"}`
291
297
  };
292
- this.activeToolsets.delete(t);
298
+ this.activeToolsets.delete(s);
293
299
  try {
294
300
  await this.onToolsListChanged?.();
295
301
  } catch (o) {
@@ -297,7 +303,7 @@ class C {
297
303
  }
298
304
  return {
299
305
  success: !0,
300
- message: `Toolset '${t}' disabled successfully. Individual tools remain registered due to MCP limitations.`
306
+ message: `Toolset '${s}' disabled successfully. Individual tools remain registered due to MCP limitations.`
301
307
  };
302
308
  }
303
309
  getStatus() {
@@ -312,38 +318,38 @@ class C {
312
318
  };
313
319
  }
314
320
  async enableToolsets(e) {
315
- const s = [];
316
- for (const r of e)
321
+ const t = [];
322
+ for (const i of e)
317
323
  try {
318
- const i = await this.enableToolset(r);
319
- s.push({ name: r, ...i });
320
- } catch (i) {
321
- s.push({
322
- name: r,
324
+ const a = await this.enableToolset(i);
325
+ t.push({ name: i, ...a });
326
+ } catch (a) {
327
+ t.push({
328
+ name: i,
323
329
  success: !1,
324
- message: i instanceof Error ? i.message : "Unknown error",
330
+ message: a instanceof Error ? a.message : "Unknown error",
325
331
  code: "E_INTERNAL"
326
332
  });
327
333
  }
328
- const t = s.every((r) => r.success), o = t ? "All toolsets enabled" : "Some toolsets failed to enable";
329
- if (s.length > 0)
334
+ const s = t.every((i) => i.success), o = s ? "All toolsets enabled" : "Some toolsets failed to enable";
335
+ if (t.length > 0)
330
336
  try {
331
337
  await this.onToolsListChanged?.();
332
338
  } catch {
333
339
  }
334
- return { success: t, results: s, message: o };
340
+ return { success: s, results: t, message: o };
335
341
  }
336
- registerDirectTools(e, s) {
337
- for (const t of e)
342
+ registerDirectTools(e, t) {
343
+ for (const s of e)
338
344
  try {
339
345
  this.server.tool(
340
- t.name,
341
- t.description,
342
- t.inputSchema,
343
- async (o) => await t.handler(o)
344
- ), s ? this.toolRegistry.addForToolset(s, t.name) : this.toolRegistry.add(t.name);
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);
345
351
  } catch (o) {
346
- throw console.error(`Failed to register direct tool '${t.name}':`, o), o;
352
+ throw console.error(`Failed to register direct tool '${s.name}':`, o), o;
347
353
  }
348
354
  }
349
355
  async enableAllToolsets() {
@@ -351,34 +357,34 @@ class C {
351
357
  return this.enableToolsets(e);
352
358
  }
353
359
  }
354
- function I(a, e, s) {
355
- const t = s?.mode ?? "DYNAMIC";
356
- a.tool(
360
+ function H(r, e, t) {
361
+ const s = t?.mode ?? "DYNAMIC";
362
+ r.tool(
357
363
  "enable_toolset",
358
364
  "Enable a toolset by name",
359
- { name: f.string().describe("Toolset name") },
365
+ { name: T.string().describe("Toolset name") },
360
366
  async (o) => {
361
- const { name: r } = o, i = await e.enableToolset(r);
367
+ const { name: i } = o, a = await e.enableToolset(i);
362
368
  return {
363
- content: [{ type: "text", text: JSON.stringify(i) }]
369
+ content: [{ type: "text", text: JSON.stringify(a) }]
364
370
  };
365
371
  }
366
- ), a.tool(
372
+ ), r.tool(
367
373
  "disable_toolset",
368
374
  "Disable a toolset by name (state only)",
369
- { name: f.string().describe("Toolset name") },
375
+ { name: T.string().describe("Toolset name") },
370
376
  async (o) => {
371
- const { name: r } = o, i = await e.disableToolset(r);
377
+ const { name: i } = o, a = await e.disableToolset(i);
372
378
  return {
373
- content: [{ type: "text", text: JSON.stringify(i) }]
379
+ content: [{ type: "text", text: JSON.stringify(a) }]
374
380
  };
375
381
  }
376
- ), t === "DYNAMIC" && (a.tool(
382
+ ), s === "DYNAMIC" && (r.tool(
377
383
  "list_toolsets",
378
384
  "List available toolsets with active status and definitions",
379
385
  {},
380
386
  async () => {
381
- const o = e.getAvailableToolsets(), r = e.getStatus().toolsetToTools, i = o.map((l) => {
387
+ const o = e.getAvailableToolsets(), i = e.getStatus().toolsetToTools, a = o.map((l) => {
382
388
  const n = e.getToolsetDefinition(l);
383
389
  return {
384
390
  key: l,
@@ -389,95 +395,95 @@ function I(a, e, s) {
389
395
  modules: n.modules ?? [],
390
396
  decisionCriteria: n.decisionCriteria ?? void 0
391
397
  } : null,
392
- tools: r[l] ?? []
398
+ tools: i[l] ?? []
393
399
  };
394
400
  });
395
401
  return {
396
402
  content: [
397
- { type: "text", text: JSON.stringify({ toolsets: i }) }
403
+ { type: "text", text: JSON.stringify({ toolsets: a }) }
398
404
  ]
399
405
  };
400
406
  }
401
- ), a.tool(
407
+ ), r.tool(
402
408
  "describe_toolset",
403
409
  "Describe a toolset with definition, active status and tools",
404
- { name: f.string().describe("Toolset name") },
410
+ { name: T.string().describe("Toolset name") },
405
411
  async (o) => {
406
- const { name: r } = o, i = e.getToolsetDefinition(r), l = e.getStatus().toolsetToTools;
407
- if (!i)
412
+ const { name: i } = o, a = e.getToolsetDefinition(i), l = e.getStatus().toolsetToTools;
413
+ if (!a)
408
414
  return {
409
415
  content: [
410
416
  {
411
417
  type: "text",
412
- text: JSON.stringify({ error: `Unknown toolset '${r}'` })
418
+ text: JSON.stringify({ error: `Unknown toolset '${i}'` })
413
419
  }
414
420
  ]
415
421
  };
416
422
  const n = {
417
- key: r,
418
- active: e.isActive(r),
423
+ key: i,
424
+ active: e.isActive(i),
419
425
  definition: {
420
- name: i.name,
421
- description: i.description,
422
- modules: i.modules ?? [],
423
- decisionCriteria: i.decisionCriteria ?? void 0
426
+ name: a.name,
427
+ description: a.description,
428
+ modules: a.modules ?? [],
429
+ decisionCriteria: a.decisionCriteria ?? void 0
424
430
  },
425
- tools: l[r] ?? []
431
+ tools: l[i] ?? []
426
432
  };
427
433
  return {
428
434
  content: [{ type: "text", text: JSON.stringify(n) }]
429
435
  };
430
436
  }
431
- )), a.tool(
437
+ )), r.tool(
432
438
  "list_tools",
433
439
  "List currently registered tool names (best effort)",
434
440
  {},
435
441
  async () => {
436
- const o = e.getStatus(), r = {
442
+ const o = e.getStatus(), i = {
437
443
  tools: o.tools,
438
444
  toolsetToTools: o.toolsetToTools
439
445
  };
440
446
  return {
441
- content: [{ type: "text", text: JSON.stringify(r) }]
447
+ content: [{ type: "text", text: JSON.stringify(i) }]
442
448
  };
443
449
  }
444
450
  );
445
451
  }
446
452
  class y {
447
453
  constructor(e) {
448
- this.toolsetValidator = new S();
449
- const s = e.startup ?? {}, t = this.resolveStartupConfig(s, e.catalog);
450
- this.mode = t.mode, this.resolver = new x({
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({
451
457
  catalog: e.catalog,
452
458
  moduleLoaders: e.moduleLoaders
453
459
  });
454
- const o = new v({
460
+ const o = new I({
455
461
  namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
456
462
  });
457
- this.manager = new C({
463
+ this.manager = new W({
458
464
  server: e.server,
459
465
  resolver: this.resolver,
460
466
  context: e.context,
461
467
  onToolsListChanged: e.notifyToolsListChanged,
462
468
  exposurePolicy: e.exposurePolicy,
463
469
  toolRegistry: o
464
- }), e.registerMetaTools !== !1 && I(e.server, this.manager, { mode: this.mode });
465
- const r = t.toolsets;
466
- r === "ALL" ? this.manager.enableToolsets(this.resolver.getAvailableToolsets()) : Array.isArray(r) && r.length > 0 && this.manager.enableToolsets(r);
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);
467
473
  }
468
- resolveStartupConfig(e, s) {
474
+ resolveStartupConfig(e, t) {
469
475
  if (e.mode) {
470
476
  if (e.mode === "DYNAMIC" && e.toolsets)
471
477
  return console.warn("startup.toolsets provided but ignored in DYNAMIC mode"), { mode: "DYNAMIC" };
472
478
  if (e.mode === "STATIC") {
473
479
  if (e.toolsets === "ALL")
474
480
  return { mode: "STATIC", toolsets: "ALL" };
475
- const t = Array.isArray(e.toolsets) ? e.toolsets : [], o = [];
476
- for (const r of t) {
477
- const { isValid: i, sanitized: l, error: n } = this.toolsetValidator.validateToolsetName(r, s);
478
- i && l ? o.push(l) : n && console.warn(n);
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);
479
485
  }
480
- if (t.length > 0 && o.length === 0)
486
+ if (s.length > 0 && o.length === 0)
481
487
  throw new Error(
482
488
  "STATIC mode requires valid toolsets or 'ALL'; none were valid"
483
489
  );
@@ -487,16 +493,16 @@ class y {
487
493
  }
488
494
  if (e.toolsets === "ALL") return { mode: "STATIC", toolsets: "ALL" };
489
495
  if (Array.isArray(e.toolsets) && e.toolsets.length > 0) {
490
- const t = [];
496
+ const s = [];
491
497
  for (const o of e.toolsets) {
492
- const { isValid: r, sanitized: i, error: l } = this.toolsetValidator.validateToolsetName(o, s);
493
- r && i ? t.push(i) : l && console.warn(l);
498
+ const { isValid: i, sanitized: a, error: l } = this.toolsetValidator.validateToolsetName(o, t);
499
+ i && a ? s.push(a) : l && console.warn(l);
494
500
  }
495
- if (t.length === 0)
501
+ if (s.length === 0)
496
502
  throw new Error(
497
503
  "STATIC mode requires valid toolsets or 'ALL'; none were valid"
498
504
  );
499
- return { mode: "STATIC", toolsets: t };
505
+ return { mode: "STATIC", toolsets: s };
500
506
  }
501
507
  return { mode: "DYNAMIC" };
502
508
  }
@@ -507,11 +513,11 @@ class y {
507
513
  return this.manager;
508
514
  }
509
515
  }
510
- class M {
516
+ class P {
511
517
  constructor(e = {}) {
512
518
  this.storage = /* @__PURE__ */ new Map(), this.maxSize = e.maxSize ?? 1e3, this.ttlMs = e.ttlMs ?? 1e3 * 60 * 60;
513
- const s = e.pruneIntervalMs ?? 1e3 * 60 * 10;
514
- this.pruneInterval = setInterval(() => this.pruneExpired(), s);
519
+ const t = e.pruneIntervalMs ?? 1e3 * 60 * 10;
520
+ this.pruneInterval = setInterval(() => this.pruneExpired(), t);
515
521
  }
516
522
  getEntryCount() {
517
523
  return this.storage.size;
@@ -523,13 +529,13 @@ class M {
523
529
  return this.ttlMs;
524
530
  }
525
531
  get(e) {
526
- const s = this.storage.get(e);
527
- 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;
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;
528
534
  }
529
- set(e, s) {
535
+ set(e, t) {
530
536
  this.storage.size >= this.maxSize && this.evictLeastRecentlyUsed();
531
- const t = { resource: s, lastAccessed: Date.now() };
532
- this.storage.set(e, t);
537
+ const s = { resource: t, lastAccessed: Date.now() };
538
+ this.storage.set(e, s);
533
539
  }
534
540
  delete(e) {
535
541
  this.storage.delete(e);
@@ -543,27 +549,27 @@ class M {
543
549
  }
544
550
  pruneExpired() {
545
551
  const e = Date.now();
546
- for (const [s, t] of this.storage.entries())
547
- e - t.lastAccessed > this.ttlMs && this.delete(s);
552
+ for (const [t, s] of this.storage.entries())
553
+ e - s.lastAccessed > this.ttlMs && this.delete(t);
548
554
  }
549
555
  }
550
- class L {
551
- constructor(e, s, t = {}, o) {
552
- this.app = null, this.clientCache = new M(), this.defaultManager = e, this.createBundle = s, this.options = {
553
- host: t.host ?? "0.0.0.0",
554
- port: t.port ?? 3e3,
555
- basePath: t.basePath ?? "/",
556
- cors: t.cors ?? !0,
557
- logger: t.logger ?? !1,
558
- app: t.app
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
559
565
  }, this.configSchema = o;
560
566
  }
561
567
  async start() {
562
568
  if (this.app) return;
563
- const e = this.options.app ?? p({ logger: this.options.logger });
564
- this.options.cors && await e.register(A, { origin: !0 });
565
- const s = this.options.basePath.endsWith("/") ? this.options.basePath.slice(0, -1) : this.options.basePath;
566
- 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 ?? {
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 ?? {
567
573
  $schema: "https://json-schema.org/draft/2020-12/schema",
568
574
  title: "MCP Session Configuration",
569
575
  description: "Schema for the /mcp endpoint configuration",
@@ -573,32 +579,32 @@ class L {
573
579
  "x-mcp-version": "1.0",
574
580
  "x-query-style": "dot+bracket"
575
581
  })), e.post(
576
- `${s}/mcp`,
577
- async (t, o) => {
578
- const r = t.headers["mcp-client-id"]?.trim(), i = r && r.length > 0 ? r : `anon-${m()}`, l = !i.startsWith("anon-");
579
- let n = l ? this.clientCache.get(i) : null;
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;
580
586
  if (!n) {
581
- const h = this.createBundle(), u = h.sessions;
587
+ const f = this.createBundle(), g = f.sessions;
582
588
  n = {
583
- server: h.server,
584
- orchestrator: h.orchestrator,
585
- sessions: u instanceof Map ? u : /* @__PURE__ */ new Map()
586
- }, l && this.clientCache.set(i, n);
589
+ server: f.server,
590
+ orchestrator: f.orchestrator,
591
+ sessions: g instanceof Map ? g : /* @__PURE__ */ new Map()
592
+ }, l && this.clientCache.set(a, n);
587
593
  }
588
- const c = t.headers["mcp-session-id"];
589
- let d;
594
+ const c = s.headers["mcp-session-id"];
595
+ let h;
590
596
  if (c && n.sessions.get(c))
591
- d = n.sessions.get(c);
592
- else if (!c && b(t.body)) {
593
- const h = m();
594
- d = new w({
595
- sessionIdGenerator: () => h,
596
- onsessioninitialized: (u) => {
597
- n.sessions.set(u, d);
597
+ h = n.sessions.get(c);
598
+ else if (!c && x(s.body)) {
599
+ const f = p();
600
+ h = new C({
601
+ sessionIdGenerator: () => f,
602
+ onsessioninitialized: (g) => {
603
+ n.sessions.set(g, h);
598
604
  }
599
605
  });
600
606
  try {
601
- await n.server.connect(d);
607
+ await n.server.connect(h);
602
608
  } catch {
603
609
  return o.code(500), {
604
610
  jsonrpc: "2.0",
@@ -606,8 +612,8 @@ class L {
606
612
  id: null
607
613
  };
608
614
  }
609
- d.onclose = () => {
610
- d?.sessionId && n.sessions.delete(d.sessionId);
615
+ h.onclose = () => {
616
+ h?.sessionId && n.sessions.delete(h.sessionId);
611
617
  };
612
618
  } else
613
619
  return o.code(400), {
@@ -615,29 +621,29 @@ class L {
615
621
  error: { code: -32e3, message: "Session not found or expired" },
616
622
  id: null
617
623
  };
618
- return await d.handleRequest(
619
- t.raw,
624
+ return await h.handleRequest(
625
+ s.raw,
620
626
  o.raw,
621
- t.body
627
+ s.body
622
628
  ), o;
623
629
  }
624
- ), e.get(`${s}/mcp`, async (t, o) => {
625
- const r = t.headers["mcp-client-id"]?.trim(), i = r && r.length > 0 ? r : "";
626
- if (!i)
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)
627
633
  return o.code(400), "Missing mcp-client-id";
628
- const l = this.clientCache.get(i);
634
+ const l = this.clientCache.get(a);
629
635
  if (!l)
630
636
  return o.code(400), "Invalid or expired client";
631
- const n = t.headers["mcp-session-id"];
637
+ const n = s.headers["mcp-session-id"];
632
638
  if (!n)
633
639
  return o.code(400), "Missing mcp-session-id";
634
640
  const c = l.sessions.get(n);
635
- return c ? (await c.handleRequest(t.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
641
+ return c ? (await c.handleRequest(s.raw, o.raw), o) : (o.code(400), "Invalid or expired session ID");
636
642
  }), e.delete(
637
- `${s}/mcp`,
638
- async (t, o) => {
639
- const r = t.headers["mcp-client-id"]?.trim(), i = r && r.length > 0 ? r : "", l = t.headers["mcp-session-id"];
640
- if (!i || !l)
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)
641
647
  return o.code(400), {
642
648
  jsonrpc: "2.0",
643
649
  error: {
@@ -646,7 +652,7 @@ class L {
646
652
  },
647
653
  id: null
648
654
  };
649
- const n = this.clientCache.get(i), c = n?.sessions.get(l);
655
+ const n = this.clientCache.get(a), c = n?.sessions.get(l);
650
656
  if (!n || !c)
651
657
  return o.code(404), {
652
658
  jsonrpc: "2.0",
@@ -670,13 +676,13 @@ class L {
670
676
  this.app && (this.options.app || await this.app.close(), this.app = null);
671
677
  }
672
678
  }
673
- async function O(a) {
674
- const e = a.startup?.mode ?? "DYNAMIC";
675
- if (typeof a.createServer != "function")
679
+ async function de(r) {
680
+ const e = r.startup?.mode ?? "DYNAMIC";
681
+ if (typeof r.createServer != "function")
676
682
  throw new Error("createMcpServer: `createServer` (factory) is required");
677
- const s = a.createServer(), t = (n) => typeof n?.server?.notification == "function", o = (n) => typeof n?.notifyToolsListChanged == "function", r = async (n) => {
683
+ const t = r.createServer(), s = (n) => typeof n?.server?.notification == "function", o = (n) => typeof n?.notifyToolsListChanged == "function", i = async (n) => {
678
684
  try {
679
- if (t(n)) {
685
+ if (s(n)) {
680
686
  await n.server.notification({
681
687
  method: "notifications/tools/list_changed"
682
688
  });
@@ -685,37 +691,37 @@ async function O(a) {
685
691
  o(n) && await n.notifyToolsListChanged();
686
692
  } catch {
687
693
  }
688
- }, i = new y({
689
- server: s,
690
- catalog: a.catalog,
691
- moduleLoaders: a.moduleLoaders,
692
- exposurePolicy: a.exposurePolicy,
693
- context: a.context,
694
- notifyToolsListChanged: async () => r(s),
695
- startup: a.startup,
696
- registerMetaTools: a.registerMetaTools !== void 0 ? a.registerMetaTools : e === "DYNAMIC"
697
- }), l = new L(
698
- i.getManager(),
694
+ }, a = new y({
695
+ server: t,
696
+ catalog: r.catalog,
697
+ moduleLoaders: r.moduleLoaders,
698
+ exposurePolicy: r.exposurePolicy,
699
+ context: r.context,
700
+ notifyToolsListChanged: async () => i(t),
701
+ startup: r.startup,
702
+ registerMetaTools: r.registerMetaTools !== void 0 ? r.registerMetaTools : e === "DYNAMIC"
703
+ }), l = new J(
704
+ a.getManager(),
699
705
  () => {
700
706
  if (e === "STATIC")
701
- return { server: s, orchestrator: i };
702
- const n = a.createServer(), c = new y({
707
+ return { server: t, orchestrator: a };
708
+ const n = r.createServer(), c = new y({
703
709
  server: n,
704
- catalog: a.catalog,
705
- moduleLoaders: a.moduleLoaders,
706
- exposurePolicy: a.exposurePolicy,
707
- context: a.context,
708
- notifyToolsListChanged: async () => r(n),
709
- startup: a.startup,
710
- registerMetaTools: a.registerMetaTools !== void 0 ? a.registerMetaTools : e === "DYNAMIC"
710
+ catalog: r.catalog,
711
+ moduleLoaders: r.moduleLoaders,
712
+ exposurePolicy: r.exposurePolicy,
713
+ context: r.context,
714
+ notifyToolsListChanged: async () => i(n),
715
+ startup: r.startup,
716
+ registerMetaTools: r.registerMetaTools !== void 0 ? r.registerMetaTools : e === "DYNAMIC"
711
717
  });
712
718
  return { server: n, orchestrator: c };
713
719
  },
714
- a.http,
715
- a.configSchema
720
+ r.http,
721
+ r.configSchema
716
722
  );
717
723
  return {
718
- server: s,
724
+ server: t,
719
725
  start: async () => {
720
726
  await l.start();
721
727
  },
@@ -724,7 +730,476 @@ async function O(a) {
724
730
  }
725
731
  };
726
732
  }
733
+ function q(r) {
734
+ G(r), K(r), Q(r), X(r);
735
+ }
736
+ function G(r) {
737
+ if (!r || typeof r != "object")
738
+ throw new Error(
739
+ "Permission configuration is required for createPermissionBasedMcpServer"
740
+ );
741
+ }
742
+ function K(r) {
743
+ if (!r.source)
744
+ throw new Error('Permission source must be either "headers" or "config"');
745
+ if (r.source !== "headers" && r.source !== "config")
746
+ throw new Error(
747
+ `Invalid permission source: "${r.source}". Must be either "headers" or "config"`
748
+ );
749
+ }
750
+ function Q(r) {
751
+ if (r.source === "config" && !r.staticMap && !r.resolver)
752
+ throw new Error(
753
+ "Config-based permissions require at least one of: staticMap or resolver function"
754
+ );
755
+ }
756
+ function X(r) {
757
+ if (r.staticMap !== void 0) {
758
+ if (typeof r.staticMap != "object" || r.staticMap === null)
759
+ throw new Error(
760
+ "staticMap must be an object mapping client IDs to toolset arrays"
761
+ );
762
+ Z(r.staticMap);
763
+ }
764
+ if (r.resolver !== void 0 && typeof r.resolver != "function")
765
+ throw new Error(
766
+ "resolver must be a synchronous function: (clientId: string) => string[]"
767
+ );
768
+ if (r.defaultPermissions !== void 0 && !Array.isArray(r.defaultPermissions))
769
+ throw new Error("defaultPermissions must be an array of toolset names");
770
+ if (r.headerName !== void 0 && (typeof r.headerName != "string" || r.headerName.length === 0))
771
+ throw new Error("headerName must be a non-empty string");
772
+ }
773
+ function Z(r) {
774
+ for (const [e, t] of Object.entries(r))
775
+ if (!Array.isArray(t))
776
+ throw new Error(
777
+ `staticMap value for client "${e}" must be an array of toolset names`
778
+ );
779
+ }
780
+ var m, $, E, L, N;
781
+ class ee {
782
+ /**
783
+ * Creates a new PermissionResolver instance.
784
+ * @param config - The permission configuration defining how permissions are resolved
785
+ */
786
+ constructor(e) {
787
+ v(this, m);
788
+ this.config = e, this.cache = /* @__PURE__ */ new Map();
789
+ }
790
+ /**
791
+ * Resolves permissions for a client based on the configured source.
792
+ * Results are cached to improve performance for subsequent requests from the same client.
793
+ * Handles all errors gracefully by returning empty permissions on failure.
794
+ * @param clientId - The unique identifier for the client
795
+ * @param headers - Optional request headers (required for header-based permissions)
796
+ * @returns Array of toolset names the client is allowed to access
797
+ */
798
+ resolvePermissions(e, t) {
799
+ if (this.cache.has(e))
800
+ return this.cache.get(e);
801
+ let s;
802
+ 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(
804
+ `Permission resolution returned non-array for client ${e}, using empty permissions`
805
+ ), s = []), s = s.filter(
806
+ (o) => typeof o == "string" && o.trim().length > 0
807
+ );
808
+ } catch (o) {
809
+ console.error(
810
+ `Unexpected error resolving permissions for client ${e}:`,
811
+ o
812
+ ), s = [];
813
+ }
814
+ return this.cache.set(e, s), s;
815
+ }
816
+ /**
817
+ * Clears the permission cache.
818
+ * Useful for cleanup during server shutdown or when permissions need to be refreshed.
819
+ */
820
+ clearCache() {
821
+ this.cache.clear();
822
+ }
823
+ }
824
+ m = new WeakSet(), /**
825
+ * Parses permissions from request headers.
826
+ * Extracts comma-separated toolset names from the configured header.
827
+ * Handles malformed headers gracefully by returning empty permissions.
828
+ * @param headers - Request headers containing permission data
829
+ * @returns Array of toolset names from headers, or empty array if header is missing/malformed
830
+ * @private
831
+ */
832
+ $ = function(e) {
833
+ const t = this.config.headerName || "mcp-toolset-permissions", s = e?.[t];
834
+ if (!s)
835
+ return [];
836
+ try {
837
+ return s.split(",").map((o) => o.trim()).filter((o) => o.length > 0);
838
+ } catch (o) {
839
+ return console.warn(
840
+ `Failed to parse permission header '${t}':`,
841
+ o
842
+ ), [];
843
+ }
844
+ }, /**
845
+ * Resolves permissions from server-side configuration.
846
+ * Tries resolver function first (if provided), then falls back to static map,
847
+ * and finally to default permissions. Handles errors gracefully.
848
+ * @param clientId - The unique identifier for the client
849
+ * @returns Array of toolset names from configuration
850
+ * @private
851
+ */
852
+ E = function(e) {
853
+ if (this.config.resolver) {
854
+ const t = u(this, m, L).call(this, e);
855
+ if (t !== null)
856
+ return t;
857
+ }
858
+ if (this.config.staticMap) {
859
+ const t = u(this, m, N).call(this, e);
860
+ if (t !== null)
861
+ return t;
862
+ }
863
+ return this.config.defaultPermissions || [];
864
+ }, /**
865
+ * Attempts to resolve permissions using the configured resolver function.
866
+ * Handles errors gracefully and returns null on failure to allow fallback.
867
+ * @param clientId - The unique identifier for the client
868
+ * @returns Array of toolset names if successful, null if resolver fails or returns invalid data
869
+ * @private
870
+ */
871
+ L = function(e) {
872
+ try {
873
+ const t = this.config.resolver(e);
874
+ return Array.isArray(t) ? t : (console.warn(
875
+ `Permission resolver returned non-array for client ${e}, using fallback`
876
+ ), null);
877
+ } catch (t) {
878
+ const s = t instanceof Error ? t.message : String(t);
879
+ return console.warn(
880
+ `Permission resolver declined client ${e} (${s}), trying fallback`
881
+ ), null;
882
+ }
883
+ }, /**
884
+ * Looks up permissions in the static map configuration.
885
+ * Returns null if client is not found to allow fallback to defaults.
886
+ * @param clientId - The unique identifier for the client
887
+ * @returns Array of toolset names if found, null if client not in map
888
+ * @private
889
+ */
890
+ N = function(e) {
891
+ const t = this.config.staticMap[e];
892
+ return t !== void 0 ? Array.isArray(t) ? t : [] : null;
893
+ };
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), {
901
+ server: o.server,
902
+ orchestrator: o.orchestrator,
903
+ allowedToolsets: s
904
+ };
905
+ };
906
+ }
907
+ var d, D, j, z, O, R, k, F, V, _;
908
+ class te {
909
+ /**
910
+ * Creates a new PermissionAwareFastifyTransport instance.
911
+ * @param defaultManager - Default tool manager for status endpoints
912
+ * @param createPermissionAwareBundle - Function to create permission-aware bundles
913
+ * @param options - Transport configuration options
914
+ * @param configSchema - Optional JSON schema for configuration discovery
915
+ */
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
925
+ }, this.configSchema = o;
926
+ }
927
+ /**
928
+ * Starts the Fastify server and registers all MCP endpoints.
929
+ * Sets up routes for health checks, tool status, and MCP protocol handling.
930
+ */
931
+ async start() {
932
+ 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;
937
+ }
938
+ /**
939
+ * Stops the Fastify server and cleans up resources.
940
+ */
941
+ async stop() {
942
+ this.app && (this.options.app || await this.app.close(), this.app = null);
943
+ }
944
+ }
945
+ d = new WeakSet(), /**
946
+ * Normalizes the base path by removing trailing slashes.
947
+ * @param basePath - The base path to normalize
948
+ * @returns Normalized base path without trailing slash
949
+ * @private
950
+ */
951
+ D = function(e) {
952
+ return e.endsWith("/") ? e.slice(0, -1) : e;
953
+ }, /**
954
+ * Registers the health check endpoint.
955
+ * @param app - Fastify instance
956
+ * @param base - Base path for routes
957
+ * @private
958
+ */
959
+ j = function(e, t) {
960
+ e.get(`${t}/healthz`, async () => ({ ok: !0 }));
961
+ }, /**
962
+ * Registers the tools status endpoint.
963
+ * @param app - Fastify instance
964
+ * @param base - Base path for routes
965
+ * @private
966
+ */
967
+ z = function(e, t) {
968
+ e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
969
+ }, /**
970
+ * Registers the MCP configuration discovery endpoint.
971
+ * @param app - Fastify instance
972
+ * @param base - Base path for routes
973
+ * @private
974
+ */
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 ?? {
977
+ $schema: "https://json-schema.org/draft/2020-12/schema",
978
+ title: "MCP Session Configuration",
979
+ description: "Schema for the /mcp endpoint configuration",
980
+ type: "object",
981
+ properties: {},
982
+ required: [],
983
+ "x-mcp-version": "1.0",
984
+ "x-query-style": "dot+bracket"
985
+ }));
986
+ }, /**
987
+ * Registers the POST /mcp endpoint for JSON-RPC requests.
988
+ * Extracts client context, resolves permissions, and handles MCP protocol.
989
+ * @param app - Fastify instance
990
+ * @param base - Base path for routes
991
+ * @private
992
+ */
993
+ R = function(e, t) {
994
+ 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;
999
+ if (!l)
1000
+ try {
1001
+ const h = await this.createPermissionAwareBundle(i), f = h.sessions;
1002
+ 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) {
1009
+ return console.error(
1010
+ `Failed to create permission-aware bundle for client ${i.clientId}:`,
1011
+ h
1012
+ ), o.code(403), u(this, d, _).call(this, "Access denied");
1013
+ }
1014
+ const n = s.headers["mcp-session-id"];
1015
+ 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);
1024
+ }
1025
+ });
1026
+ try {
1027
+ await l.server.connect(c);
1028
+ } catch {
1029
+ return o.code(500), {
1030
+ jsonrpc: "2.0",
1031
+ error: { code: -32603, message: "Error initializing server." },
1032
+ id: null
1033
+ };
1034
+ }
1035
+ c.onclose = () => {
1036
+ c?.sessionId && l.sessions.delete(c.sessionId);
1037
+ };
1038
+ } else
1039
+ return o.code(400), {
1040
+ jsonrpc: "2.0",
1041
+ error: { code: -32e3, message: "Session not found or expired" },
1042
+ id: null
1043
+ };
1044
+ return await c.handleRequest(
1045
+ s.raw,
1046
+ o.raw,
1047
+ s.body
1048
+ ), o;
1049
+ }
1050
+ );
1051
+ }, /**
1052
+ * Registers the GET /mcp endpoint for SSE notifications.
1053
+ * @param app - Fastify instance
1054
+ * @param base - Base path for routes
1055
+ * @private
1056
+ */
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)
1061
+ return o.code(400), "Missing mcp-client-id";
1062
+ const l = this.clientCache.get(a);
1063
+ if (!l)
1064
+ return o.code(400), "Invalid or expired client";
1065
+ const n = s.headers["mcp-session-id"];
1066
+ if (!n)
1067
+ 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");
1070
+ });
1071
+ }, /**
1072
+ * Registers the DELETE /mcp endpoint for session termination.
1073
+ * @param app - Fastify instance
1074
+ * @param base - Base path for routes
1075
+ * @private
1076
+ */
1077
+ F = function(e, t) {
1078
+ 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)
1083
+ return o.code(400), {
1084
+ jsonrpc: "2.0",
1085
+ error: {
1086
+ code: -32600,
1087
+ message: "Missing mcp-client-id or mcp-session-id header"
1088
+ },
1089
+ id: null
1090
+ };
1091
+ const n = this.clientCache.get(a), c = n?.sessions.get(l);
1092
+ if (!n || !c)
1093
+ return o.code(404), {
1094
+ jsonrpc: "2.0",
1095
+ error: { code: -32e3, message: "Session not found or expired" },
1096
+ id: null
1097
+ };
1098
+ try {
1099
+ if (typeof c.close == "function")
1100
+ try {
1101
+ await c.close();
1102
+ } catch {
1103
+ }
1104
+ } finally {
1105
+ c?.sessionId ? n.sessions.delete(c.sessionId) : n.sessions.delete(l);
1106
+ }
1107
+ return o.code(204).send(), o;
1108
+ }
1109
+ );
1110
+ }, /**
1111
+ * Extracts client context from the request.
1112
+ * Generates anonymous client ID if not provided in headers.
1113
+ * @param req - Fastify request object
1114
+ * @returns Client request context with ID and headers
1115
+ * @private
1116
+ */
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 };
1122
+ }, /**
1123
+ * Creates a safe error response that doesn't expose unauthorized toolset information.
1124
+ * Used for permission-related errors to prevent information leakage.
1125
+ * @param message - Generic error message to return to client
1126
+ * @param code - JSON-RPC error code (default: -32000 for server error)
1127
+ * @returns JSON-RPC error response object
1128
+ * @private
1129
+ */
1130
+ _ = function(e = "Access denied", t = -32e3) {
1131
+ return {
1132
+ jsonrpc: "2.0",
1133
+ error: {
1134
+ code: t,
1135
+ message: e
1136
+ },
1137
+ id: null
1138
+ };
1139
+ };
1140
+ async function he(r) {
1141
+ if (!r.permissions)
1142
+ throw new Error(
1143
+ "Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
1144
+ );
1145
+ if (q(r.permissions), r.startup)
1146
+ throw new Error(
1147
+ "Permission-based servers determine toolsets from client permissions. The 'startup' option is not allowed. Remove it from your configuration."
1148
+ );
1149
+ if (typeof r.createServer != "function")
1150
+ throw new Error(
1151
+ "createPermissionBasedMcpServer: `createServer` (factory) is required"
1152
+ );
1153
+ const e = new ee(r.permissions), t = r.createServer(), s = new y({
1154
+ server: t,
1155
+ catalog: r.catalog,
1156
+ moduleLoaders: r.moduleLoaders,
1157
+ exposurePolicy: r.exposurePolicy,
1158
+ context: r.context,
1159
+ notifyToolsListChanged: void 0,
1160
+ // No notifications in STATIC mode
1161
+ startup: { mode: "STATIC", toolsets: [] },
1162
+ registerMetaTools: !1
1163
+ }), o = se(
1164
+ (a) => {
1165
+ const l = r.createServer(), n = new y({
1166
+ server: l,
1167
+ catalog: r.catalog,
1168
+ moduleLoaders: r.moduleLoaders,
1169
+ exposurePolicy: r.exposurePolicy,
1170
+ context: r.context,
1171
+ notifyToolsListChanged: void 0,
1172
+ // No notifications in STATIC mode
1173
+ startup: { mode: "STATIC", toolsets: [] },
1174
+ // Empty - we'll enable manually
1175
+ registerMetaTools: !1
1176
+ // No meta-tools - toolsets are fixed per client
1177
+ });
1178
+ return { server: l, orchestrator: n };
1179
+ },
1180
+ e
1181
+ ), i = new te(
1182
+ s.getManager(),
1183
+ o,
1184
+ r.http,
1185
+ r.configSchema
1186
+ );
1187
+ return {
1188
+ server: t,
1189
+ start: async () => {
1190
+ await i.start();
1191
+ },
1192
+ close: async () => {
1193
+ try {
1194
+ await i.stop();
1195
+ } finally {
1196
+ e.clearCache();
1197
+ }
1198
+ }
1199
+ };
1200
+ }
727
1201
  export {
728
- O as createMcpServer
1202
+ de as createMcpServer,
1203
+ he as createPermissionBasedMcpServer
729
1204
  };
730
1205
  //# sourceMappingURL=index.js.map