toolception 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,12 +2,12 @@ var P = (r) => {
2
2
  throw TypeError(r);
3
3
  };
4
4
  var ee = (r, e, s) => e.has(r) || P("Cannot " + s);
5
- var T = (r, e, s) => e.has(r) ? P("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, s);
5
+ var w = (r, e, s) => e.has(r) ? P("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, s);
6
6
  var f = (r, e, s) => (ee(r, e, "access private method"), s);
7
- import { z as b } from "zod";
7
+ import { z as p } from "zod";
8
8
  import N from "fastify";
9
9
  import j from "@fastify/cors";
10
- import { randomUUID as y } from "node:crypto";
10
+ import { randomUUID as v } from "node:crypto";
11
11
  import { StreamableHTTPServerTransport as k } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
12
12
  import { isInitializeRequest as D } from "@modelcontextprotocol/sdk/types.js";
13
13
  const L = {
@@ -167,7 +167,7 @@ class R extends Error {
167
167
  super(e), this.name = "ToolingError", this.code = s, this.details = t;
168
168
  }
169
169
  }
170
- class z {
170
+ class O {
171
171
  constructor(e = {}) {
172
172
  this.names = /* @__PURE__ */ new Set(), this.toolsetToNames = /* @__PURE__ */ new Map(), this.options = {
173
173
  namespaceWithToolset: e.namespaceWithToolset ?? !0
@@ -215,7 +215,7 @@ class z {
215
215
  }
216
216
  class oe {
217
217
  constructor(e) {
218
- this.activeToolsets = /* @__PURE__ */ new Set(), this.server = e.server, this.resolver = e.resolver, this.context = e.context, this.onToolsListChanged = e.onToolsListChanged, this.exposurePolicy = e.exposurePolicy, this.toolRegistry = e.toolRegistry ?? new z({ namespaceWithToolset: !0 });
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 O({ namespaceWithToolset: !0 });
219
219
  }
220
220
  /**
221
221
  * Sends a tool list change notification if configured.
@@ -399,7 +399,7 @@ function re(r, e, s) {
399
399
  (s?.mode ?? "DYNAMIC") === "DYNAMIC" && (r.tool(
400
400
  "enable_toolset",
401
401
  "Enable a toolset by name",
402
- { name: b.string().describe("Toolset name") },
402
+ { name: p.string().describe("Toolset name") },
403
403
  async (o) => {
404
404
  const i = await e.enableToolset(o.name);
405
405
  return {
@@ -409,7 +409,7 @@ function re(r, e, s) {
409
409
  ), r.tool(
410
410
  "disable_toolset",
411
411
  "Disable a toolset by name (state only)",
412
- { name: b.string().describe("Toolset name") },
412
+ { name: p.string().describe("Toolset name") },
413
413
  async (o) => {
414
414
  const i = await e.disableToolset(o.name);
415
415
  return {
@@ -444,7 +444,7 @@ function re(r, e, s) {
444
444
  ), r.tool(
445
445
  "describe_toolset",
446
446
  "Describe a toolset with definition, active status and tools",
447
- { name: b.string().describe("Toolset name") },
447
+ { name: p.string().describe("Toolset name") },
448
448
  async (o) => {
449
449
  const i = e.getToolsetDefinition(o.name), n = e.getStatus().toolsetToTools;
450
450
  if (!i)
@@ -486,7 +486,7 @@ function re(r, e, s) {
486
486
  }
487
487
  );
488
488
  }
489
- class w {
489
+ class b {
490
490
  constructor(e) {
491
491
  this.initError = null, this.toolsetValidator = new se();
492
492
  const s = e.startup ?? {}, t = this.resolveStartupConfig(s, e.catalog);
@@ -494,7 +494,7 @@ class w {
494
494
  catalog: e.catalog,
495
495
  moduleLoaders: e.moduleLoaders
496
496
  });
497
- const o = new z({
497
+ const o = new O({
498
498
  namespaceWithToolset: e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
499
499
  });
500
500
  this.manager = new oe({
@@ -581,10 +581,10 @@ class w {
581
581
  return this.manager;
582
582
  }
583
583
  }
584
- var v, S;
585
- class O {
584
+ var T, S;
585
+ class z {
586
586
  constructor(e = {}) {
587
- T(this, v);
587
+ w(this, T);
588
588
  this.storage = /* @__PURE__ */ new Map(), this.maxSize = e.maxSize ?? 1e3, this.ttlMs = e.ttlMs ?? 1e3 * 60 * 60, this.onEvict = e.onEvict;
589
589
  const s = e.pruneIntervalMs ?? 1e3 * 60 * 10;
590
590
  this.pruneInterval = setInterval(() => this.pruneExpired(), s);
@@ -614,7 +614,7 @@ class O {
614
614
  */
615
615
  delete(e) {
616
616
  const s = this.storage.get(e);
617
- s && (this.storage.delete(e), f(this, v, S).call(this, e, s.resource));
617
+ s && (this.storage.delete(e), f(this, T, S).call(this, e, s.resource));
618
618
  }
619
619
  /**
620
620
  * Stops the background pruning interval and optionally clears all entries.
@@ -631,7 +631,7 @@ class O {
631
631
  const e = Array.from(this.storage.entries());
632
632
  this.storage.clear();
633
633
  for (const [s, t] of e)
634
- f(this, v, S).call(this, s, t.resource);
634
+ f(this, T, S).call(this, s, t.resource);
635
635
  }
636
636
  /**
637
637
  * Evicts the least recently used entry from the cache.
@@ -653,7 +653,7 @@ class O {
653
653
  this.delete(t);
654
654
  }
655
655
  }
656
- v = new WeakSet(), /**
656
+ T = new WeakSet(), /**
657
657
  * Safely calls the evict callback, catching and logging any errors.
658
658
  * @param key - The key being evicted
659
659
  * @param resource - The resource being evicted
@@ -685,7 +685,7 @@ function V(r, e, s, t) {
685
685
  const a = i.method.toLowerCase();
686
686
  r[a](n, async (c, d) => {
687
687
  try {
688
- const u = c.headers["mcp-client-id"]?.trim(), p = u && u.length > 0 ? u : `anon-${y()}`;
688
+ const u = c.headers["mcp-client-id"]?.trim(), y = u && u.length > 0 ? u : `anon-${v()}`;
689
689
  let C;
690
690
  if (i.bodySchema) {
691
691
  const m = i.bodySchema.safeParse(c.body);
@@ -693,26 +693,26 @@ function V(r, e, s, t) {
693
693
  return A(d, "body", m.error);
694
694
  C = m.data;
695
695
  }
696
- let x = {};
696
+ let I = {};
697
697
  if (i.querySchema) {
698
698
  const m = i.querySchema.safeParse(c.query);
699
699
  if (!m.success)
700
700
  return A(d, "query", m.error);
701
- x = m.data;
701
+ I = m.data;
702
702
  }
703
- let I = {};
703
+ let x = {};
704
704
  if (i.paramsSchema) {
705
705
  const m = i.paramsSchema.safeParse(c.params);
706
706
  if (!m.success)
707
707
  return A(d, "params", m.error);
708
- I = m.data;
708
+ x = m.data;
709
709
  }
710
710
  const M = {
711
711
  body: C,
712
- query: x,
713
- params: I,
712
+ query: I,
713
+ params: x,
714
714
  headers: c.headers,
715
- clientId: p
715
+ clientId: y
716
716
  };
717
717
  if (t?.contextExtractor) {
718
718
  const m = await t.contextExtractor(c);
@@ -757,7 +757,7 @@ function A(r, e, s) {
757
757
  }
758
758
  class ie {
759
759
  constructor(e, s, t = {}, o) {
760
- this.app = null, this.clientCache = new O({
760
+ this.app = null, this.clientCache = new z({
761
761
  onEvict: (i, n) => {
762
762
  this.cleanupBundle(n);
763
763
  }
@@ -788,14 +788,14 @@ class ie {
788
788
  })), e.post(
789
789
  `${s}/mcp`,
790
790
  async (t, o) => {
791
- const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : `anon-${y()}`, l = !n.startsWith("anon-");
791
+ const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : `anon-${v()}`, l = !n.startsWith("anon-");
792
792
  let a = l ? this.clientCache.get(n) : null;
793
793
  if (!a) {
794
- const u = this.createBundle(), p = u.sessions;
794
+ const u = this.createBundle(), y = u.sessions;
795
795
  a = {
796
796
  server: u.server,
797
797
  orchestrator: u.orchestrator,
798
- sessions: p instanceof Map ? p : /* @__PURE__ */ new Map()
798
+ sessions: y instanceof Map ? y : /* @__PURE__ */ new Map()
799
799
  }, l && this.clientCache.set(n, a);
800
800
  }
801
801
  const c = t.headers["mcp-session-id"];
@@ -803,11 +803,11 @@ class ie {
803
803
  if (c && a.sessions.get(c))
804
804
  d = a.sessions.get(c);
805
805
  else if (!c && D(t.body)) {
806
- const u = y();
806
+ const u = v();
807
807
  d = new k({
808
808
  sessionIdGenerator: () => u,
809
- onsessioninitialized: (p) => {
810
- a.sessions.set(p, d);
809
+ onsessioninitialized: (y) => {
810
+ a.sessions.set(y, d);
811
811
  }
812
812
  });
813
813
  try {
@@ -904,7 +904,26 @@ class ie {
904
904
  e.sessions.clear();
905
905
  }
906
906
  }
907
- async function Se(r) {
907
+ const ne = p.object({
908
+ mode: p.enum(["DYNAMIC", "STATIC"]).optional(),
909
+ toolsets: p.union([p.array(p.string()), p.literal("ALL")]).optional()
910
+ }).strict();
911
+ async function Ee(r) {
912
+ if (r.startup)
913
+ try {
914
+ ne.parse(r.startup);
915
+ } catch (a) {
916
+ if (a instanceof p.ZodError) {
917
+ const c = a.format();
918
+ throw new Error(
919
+ `Invalid startup configuration:
920
+ ${JSON.stringify(c, null, 2)}
921
+
922
+ Hint: Common mistake - use "toolsets" not "initialToolsets"`
923
+ );
924
+ }
925
+ throw a;
926
+ }
908
927
  const e = r.startup?.mode ?? "DYNAMIC";
909
928
  if (typeof r.createServer != "function")
910
929
  throw new Error("createMcpServer: `createServer` (factory) is required");
@@ -922,7 +941,7 @@ async function Se(r) {
922
941
  return;
923
942
  console.warn("Failed to send tools list changed notification:", c);
924
943
  }
925
- }, n = new w({
944
+ }, n = new b({
926
945
  server: s,
927
946
  catalog: r.catalog,
928
947
  moduleLoaders: r.moduleLoaders,
@@ -938,7 +957,7 @@ async function Se(r) {
938
957
  () => {
939
958
  if (e === "STATIC")
940
959
  return { server: s, orchestrator: n };
941
- const a = r.createServer(), c = new w({
960
+ const a = r.createServer(), c = new b({
942
961
  server: a,
943
962
  catalog: r.catalog,
944
963
  moduleLoaders: r.moduleLoaders,
@@ -963,16 +982,16 @@ async function Se(r) {
963
982
  }
964
983
  };
965
984
  }
966
- function ne(r) {
967
- ae(r), le(r), ce(r), de(r);
968
- }
969
985
  function ae(r) {
986
+ le(r), ce(r), de(r), he(r);
987
+ }
988
+ function le(r) {
970
989
  if (!r || typeof r != "object")
971
990
  throw new Error(
972
991
  "Permission configuration is required for createPermissionBasedMcpServer"
973
992
  );
974
993
  }
975
- function le(r) {
994
+ function ce(r) {
976
995
  if (!r.source)
977
996
  throw new Error('Permission source must be either "headers" or "config"');
978
997
  if (r.source !== "headers" && r.source !== "config")
@@ -980,19 +999,19 @@ function le(r) {
980
999
  `Invalid permission source: "${r.source}". Must be either "headers" or "config"`
981
1000
  );
982
1001
  }
983
- function ce(r) {
1002
+ function de(r) {
984
1003
  if (r.source === "config" && !r.staticMap && !r.resolver)
985
1004
  throw new Error(
986
1005
  "Config-based permissions require at least one of: staticMap or resolver function"
987
1006
  );
988
1007
  }
989
- function de(r) {
1008
+ function he(r) {
990
1009
  if (r.staticMap !== void 0) {
991
1010
  if (typeof r.staticMap != "object" || r.staticMap === null)
992
1011
  throw new Error(
993
1012
  "staticMap must be an object mapping client IDs to toolset arrays"
994
1013
  );
995
- he(r.staticMap);
1014
+ ue(r.staticMap);
996
1015
  }
997
1016
  if (r.resolver !== void 0 && typeof r.resolver != "function")
998
1017
  throw new Error(
@@ -1003,21 +1022,21 @@ function de(r) {
1003
1022
  if (r.headerName !== void 0 && (typeof r.headerName != "string" || r.headerName.length === 0))
1004
1023
  throw new Error("headerName must be a non-empty string");
1005
1024
  }
1006
- function he(r) {
1025
+ function ue(r) {
1007
1026
  for (const [e, s] of Object.entries(r))
1008
1027
  if (!Array.isArray(s))
1009
1028
  throw new Error(
1010
1029
  `staticMap value for client "${e}" must be an array of toolset names`
1011
1030
  );
1012
1031
  }
1013
- var g, F, _, B, H, W;
1014
- class ue {
1032
+ var g, F, _, B, H, Y;
1033
+ class fe {
1015
1034
  /**
1016
1035
  * Creates a new PermissionResolver instance.
1017
1036
  * @param config - The permission configuration defining how permissions are resolved
1018
1037
  */
1019
1038
  constructor(e) {
1020
- T(this, g);
1039
+ w(this, g);
1021
1040
  this.config = e, this.cache = /* @__PURE__ */ new Map(), this.normalizedHeaderName = (e.headerName || "mcp-toolset-permissions").toLowerCase();
1022
1041
  }
1023
1042
  /**
@@ -1119,7 +1138,7 @@ B = function(e) {
1119
1138
  return s;
1120
1139
  }
1121
1140
  if (this.config.staticMap) {
1122
- const s = f(this, g, W).call(this, e);
1141
+ const s = f(this, g, Y).call(this, e);
1123
1142
  if (s !== null)
1124
1143
  return s;
1125
1144
  }
@@ -1150,11 +1169,11 @@ H = function(e) {
1150
1169
  * @returns Array of toolset names if found, null if client not in map
1151
1170
  * @private
1152
1171
  */
1153
- W = function(e) {
1172
+ Y = function(e) {
1154
1173
  const s = this.config.staticMap[e];
1155
1174
  return s !== void 0 ? Array.isArray(s) ? s : [] : null;
1156
1175
  };
1157
- function fe(r, e) {
1176
+ function me(r, e) {
1158
1177
  return async (s) => {
1159
1178
  const t = e.resolvePermissions(
1160
1179
  s.clientId,
@@ -1179,8 +1198,8 @@ function fe(r, e) {
1179
1198
  };
1180
1199
  };
1181
1200
  }
1182
- var h, Y, U, q, J, G, K, Q, X, E, Z;
1183
- class me {
1201
+ var h, W, U, q, J, G, K, Z, Q, E, X;
1202
+ class ge {
1184
1203
  /**
1185
1204
  * Creates a new PermissionAwareFastifyTransport instance.
1186
1205
  * @param defaultManager - Default tool manager for status endpoints
@@ -1189,10 +1208,10 @@ class me {
1189
1208
  * @param configSchema - Optional JSON schema for configuration discovery
1190
1209
  */
1191
1210
  constructor(e, s, t = {}, o) {
1192
- T(this, h);
1193
- this.app = null, this.clientCache = new O({
1211
+ w(this, h);
1212
+ this.app = null, this.clientCache = new z({
1194
1213
  onEvict: (i, n) => {
1195
- f(this, h, Y).call(this, n);
1214
+ f(this, h, W).call(this, n);
1196
1215
  }
1197
1216
  }), this.defaultManager = e, this.createPermissionAwareBundle = s, this.options = {
1198
1217
  host: t.host ?? "0.0.0.0",
@@ -1213,7 +1232,7 @@ class me {
1213
1232
  const e = this.options.app ?? N({ logger: this.options.logger });
1214
1233
  this.options.cors && await e.register(j, { origin: !0 });
1215
1234
  const s = f(this, h, U).call(this, this.options.basePath);
1216
- f(this, h, q).call(this, e, s), f(this, h, J).call(this, e, s), f(this, h, G).call(this, e, s), f(this, h, K).call(this, e, s), f(this, h, Q).call(this, e, s), f(this, h, X).call(this, e, s), this.options.customEndpoints && this.options.customEndpoints.length > 0 && V(e, s, this.options.customEndpoints, {
1235
+ f(this, h, q).call(this, e, s), f(this, h, J).call(this, e, s), f(this, h, G).call(this, e, s), f(this, h, K).call(this, e, s), f(this, h, Z).call(this, e, s), f(this, h, Q).call(this, e, s), this.options.customEndpoints && this.options.customEndpoints.length > 0 && V(e, s, this.options.customEndpoints, {
1217
1236
  contextExtractor: async (t) => {
1218
1237
  const o = f(this, h, E).call(this, t);
1219
1238
  try {
@@ -1247,7 +1266,7 @@ h = new WeakSet(), /**
1247
1266
  * @param bundle - The client bundle to clean up
1248
1267
  * @private
1249
1268
  */
1250
- Y = function(e) {
1269
+ W = function(e) {
1251
1270
  for (const [s, t] of e.sessions.entries())
1252
1271
  try {
1253
1272
  typeof t.close == "function" && t.close().catch((o) => {
@@ -1329,14 +1348,14 @@ K = function(e, s) {
1329
1348
  return console.error(
1330
1349
  `Failed to create permission-aware bundle for client ${i.clientId}:`,
1331
1350
  d
1332
- ), o.code(403), f(this, h, Z).call(this, "Access denied");
1351
+ ), o.code(403), f(this, h, X).call(this, "Access denied");
1333
1352
  }
1334
1353
  const a = t.headers["mcp-session-id"];
1335
1354
  let c;
1336
1355
  if (a && l.sessions.get(a))
1337
1356
  c = l.sessions.get(a);
1338
1357
  else if (!a && D(t.body)) {
1339
- const d = y();
1358
+ const d = v();
1340
1359
  c = new k({
1341
1360
  sessionIdGenerator: () => d,
1342
1361
  onsessioninitialized: (u) => {
@@ -1374,7 +1393,7 @@ K = function(e, s) {
1374
1393
  * @param base - Base path for routes
1375
1394
  * @private
1376
1395
  */
1377
- Q = function(e, s) {
1396
+ Z = function(e, s) {
1378
1397
  e.get(`${s}/mcp`, async (t, o) => {
1379
1398
  const i = t.headers["mcp-client-id"]?.trim(), n = i && i.length > 0 ? i : "";
1380
1399
  if (!n)
@@ -1394,7 +1413,7 @@ Q = function(e, s) {
1394
1413
  * @param base - Base path for routes
1395
1414
  * @private
1396
1415
  */
1397
- X = function(e, s) {
1416
+ Q = function(e, s) {
1398
1417
  e.delete(
1399
1418
  `${s}/mcp`,
1400
1419
  async (t, o) => {
@@ -1435,7 +1454,7 @@ X = function(e, s) {
1435
1454
  * @private
1436
1455
  */
1437
1456
  E = function(e) {
1438
- const s = e.headers["mcp-client-id"]?.trim(), t = s && s.length > 0 ? s : `anon-${y()}`, o = {};
1457
+ const s = e.headers["mcp-client-id"]?.trim(), t = s && s.length > 0 ? s : `anon-${v()}`, o = {};
1439
1458
  for (const [i, n] of Object.entries(e.headers))
1440
1459
  typeof n == "string" && (o[i] = n);
1441
1460
  return { clientId: t, headers: o };
@@ -1447,7 +1466,7 @@ E = function(e) {
1447
1466
  * @returns JSON-RPC error response object
1448
1467
  * @private
1449
1468
  */
1450
- Z = function(e = "Access denied", s = -32e3) {
1469
+ X = function(e = "Access denied", s = -32e3) {
1451
1470
  return {
1452
1471
  jsonrpc: "2.0",
1453
1472
  error: {
@@ -1457,7 +1476,7 @@ Z = function(e = "Access denied", s = -32e3) {
1457
1476
  id: null
1458
1477
  };
1459
1478
  };
1460
- function ge(r) {
1479
+ function pe(r) {
1461
1480
  if (!r) return;
1462
1481
  const e = {
1463
1482
  namespaceToolsWithSetKey: r.namespaceToolsWithSetKey
@@ -1472,12 +1491,12 @@ function ge(r) {
1472
1491
  "Permission-based servers: exposurePolicy.onLimitExceeded is ignored. No toolset limits are enforced."
1473
1492
  ), e;
1474
1493
  }
1475
- async function Ee(r) {
1494
+ async function Ce(r) {
1476
1495
  if (!r.permissions)
1477
1496
  throw new Error(
1478
1497
  "Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
1479
1498
  );
1480
- if (ne(r.permissions), r.startup)
1499
+ if (ae(r.permissions), r.startup)
1481
1500
  throw new Error(
1482
1501
  "Permission-based servers determine toolsets from client permissions. The 'startup' option is not allowed. Remove it from your configuration."
1483
1502
  );
@@ -1485,9 +1504,9 @@ async function Ee(r) {
1485
1504
  throw new Error(
1486
1505
  "createPermissionBasedMcpServer: `createServer` (factory) is required"
1487
1506
  );
1488
- const e = ge(
1507
+ const e = pe(
1489
1508
  r.exposurePolicy
1490
- ), s = new ue(r.permissions), t = r.createServer(), o = new w({
1509
+ ), s = new fe(r.permissions), t = r.createServer(), o = new b({
1491
1510
  server: t,
1492
1511
  catalog: r.catalog,
1493
1512
  moduleLoaders: r.moduleLoaders,
@@ -1497,9 +1516,9 @@ async function Ee(r) {
1497
1516
  // No notifications in STATIC mode
1498
1517
  startup: { mode: "STATIC", toolsets: [] },
1499
1518
  registerMetaTools: !1
1500
- }), i = fe(
1519
+ }), i = me(
1501
1520
  (l) => {
1502
- const a = r.createServer(), c = new w({
1521
+ const a = r.createServer(), c = new b({
1503
1522
  server: a,
1504
1523
  catalog: r.catalog,
1505
1524
  moduleLoaders: r.moduleLoaders,
@@ -1515,7 +1534,7 @@ async function Ee(r) {
1515
1534
  return { server: a, orchestrator: c };
1516
1535
  },
1517
1536
  s
1518
- ), n = new me(
1537
+ ), n = new ge(
1519
1538
  o.getManager(),
1520
1539
  i,
1521
1540
  r.http,
@@ -1535,16 +1554,16 @@ async function Ee(r) {
1535
1554
  }
1536
1555
  };
1537
1556
  }
1538
- function Ce(r) {
1557
+ function Ie(r) {
1539
1558
  return r;
1540
1559
  }
1541
1560
  function xe(r) {
1542
1561
  return r;
1543
1562
  }
1544
1563
  export {
1545
- Se as createMcpServer,
1546
- Ee as createPermissionBasedMcpServer,
1547
- Ce as defineEndpoint,
1564
+ Ee as createMcpServer,
1565
+ Ce as createPermissionBasedMcpServer,
1566
+ Ie as defineEndpoint,
1548
1567
  xe as definePermissionAwareEndpoint
1549
1568
  };
1550
1569
  //# sourceMappingURL=index.js.map