toolception 0.6.2 → 0.6.3
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,15 +1,15 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ve = Object.defineProperty;
|
|
2
2
|
var H = (r) => {
|
|
3
3
|
throw TypeError(r);
|
|
4
4
|
};
|
|
5
|
-
var
|
|
6
|
-
var d = (r, e, t) =>
|
|
5
|
+
var be = (r, e, t) => e in r ? ve(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
6
|
+
var d = (r, e, t) => be(r, typeof e != "symbol" ? e + "" : e, t), we = (r, e, t) => e.has(r) || H("Cannot " + t);
|
|
7
7
|
var x = (r, e, t) => e.has(r) ? H("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, t);
|
|
8
|
-
var m = (r, e, t) => (
|
|
8
|
+
var m = (r, e, t) => (we(r, e, "access private method"), t);
|
|
9
9
|
import { z as g } from "zod";
|
|
10
10
|
import U from "fastify";
|
|
11
11
|
import J from "@fastify/cors";
|
|
12
|
-
import { randomUUID as S, createHash as
|
|
12
|
+
import { randomUUID as S, createHash as Te } from "node:crypto";
|
|
13
13
|
import { StreamableHTTPServerTransport as Q } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
14
14
|
import { isInitializeRequest as G } from "@modelcontextprotocol/sdk/types.js";
|
|
15
15
|
const V = {
|
|
@@ -520,9 +520,9 @@ class N {
|
|
|
520
520
|
return this.enableToolsets(e);
|
|
521
521
|
}
|
|
522
522
|
}
|
|
523
|
-
const
|
|
524
|
-
function
|
|
525
|
-
(s?.mode ?? "DYNAMIC") === "DYNAMIC" && (t.addForToolset(
|
|
523
|
+
const w = "_meta";
|
|
524
|
+
function xe(r, e, t, s) {
|
|
525
|
+
(s?.mode ?? "DYNAMIC") === "DYNAMIC" && (t.addForToolset(w, "enable_toolset"), r.tool(
|
|
526
526
|
"enable_toolset",
|
|
527
527
|
"Enable a toolset by name",
|
|
528
528
|
{ name: g.string().describe("Toolset name") },
|
|
@@ -533,7 +533,7 @@ function Te(r, e, t, s) {
|
|
|
533
533
|
content: [{ type: "text", text: JSON.stringify(i) }]
|
|
534
534
|
};
|
|
535
535
|
}
|
|
536
|
-
), t.addForToolset(
|
|
536
|
+
), t.addForToolset(w, "disable_toolset"), r.tool(
|
|
537
537
|
"disable_toolset",
|
|
538
538
|
"Disable a toolset by name (state only)",
|
|
539
539
|
{ name: g.string().describe("Toolset name") },
|
|
@@ -544,7 +544,7 @@ function Te(r, e, t, s) {
|
|
|
544
544
|
content: [{ type: "text", text: JSON.stringify(i) }]
|
|
545
545
|
};
|
|
546
546
|
}
|
|
547
|
-
), t.addForToolset(
|
|
547
|
+
), t.addForToolset(w, "list_toolsets"), r.tool(
|
|
548
548
|
"list_toolsets",
|
|
549
549
|
"List available toolsets with active status and definitions",
|
|
550
550
|
{},
|
|
@@ -570,7 +570,7 @@ function Te(r, e, t, s) {
|
|
|
570
570
|
]
|
|
571
571
|
};
|
|
572
572
|
}
|
|
573
|
-
), t.addForToolset(
|
|
573
|
+
), t.addForToolset(w, "describe_toolset"), r.tool(
|
|
574
574
|
"describe_toolset",
|
|
575
575
|
"Describe a toolset with definition, active status and tools",
|
|
576
576
|
{ name: g.string().describe("Toolset name") },
|
|
@@ -601,7 +601,7 @@ function Te(r, e, t, s) {
|
|
|
601
601
|
content: [{ type: "text", text: JSON.stringify(l) }]
|
|
602
602
|
};
|
|
603
603
|
}
|
|
604
|
-
)), t.addForToolset(
|
|
604
|
+
)), t.addForToolset(w, "list_tools"), r.tool(
|
|
605
605
|
"list_tools",
|
|
606
606
|
"List currently registered tool names (best effort)",
|
|
607
607
|
{},
|
|
@@ -631,7 +631,7 @@ class A {
|
|
|
631
631
|
const o = C.builder().namespaceWithToolset(
|
|
632
632
|
e.exposurePolicy?.namespaceToolsWithSetKey ?? !0
|
|
633
633
|
).build(), n = N.builder().server(e.server).resolver(this.resolver).context(e.context).toolRegistry(o);
|
|
634
|
-
e.notifyToolsListChanged && n.onToolsListChanged(e.notifyToolsListChanged), e.exposurePolicy && n.exposurePolicy(e.exposurePolicy), this.manager = n.build(), e.registerMetaTools !== !1 &&
|
|
634
|
+
e.notifyToolsListChanged && n.onToolsListChanged(e.notifyToolsListChanged), e.exposurePolicy && n.exposurePolicy(e.exposurePolicy), this.manager = n.build(), e.registerMetaTools !== !1 && xe(e.server, this.manager, o, { mode: this.mode });
|
|
635
635
|
const i = s.toolsets;
|
|
636
636
|
this.initPromise = this.initializeToolsets(i);
|
|
637
637
|
}
|
|
@@ -746,10 +746,10 @@ class A {
|
|
|
746
746
|
return this.manager;
|
|
747
747
|
}
|
|
748
748
|
}
|
|
749
|
-
var
|
|
749
|
+
var T, M;
|
|
750
750
|
const D = class D {
|
|
751
751
|
constructor(e = {}) {
|
|
752
|
-
x(this,
|
|
752
|
+
x(this, T);
|
|
753
753
|
d(this, "storage", /* @__PURE__ */ new Map());
|
|
754
754
|
d(this, "maxSize");
|
|
755
755
|
d(this, "ttlMs");
|
|
@@ -803,7 +803,7 @@ const D = class D {
|
|
|
803
803
|
*/
|
|
804
804
|
delete(e) {
|
|
805
805
|
const t = this.storage.get(e);
|
|
806
|
-
t && (this.storage.delete(e), m(this,
|
|
806
|
+
t && (this.storage.delete(e), m(this, T, M).call(this, e, t.resource));
|
|
807
807
|
}
|
|
808
808
|
/**
|
|
809
809
|
* @param clearEntries - If true, also removes all entries and calls onEvict for each
|
|
@@ -815,7 +815,7 @@ const D = class D {
|
|
|
815
815
|
const e = Array.from(this.storage.entries());
|
|
816
816
|
this.storage.clear();
|
|
817
817
|
for (const [t, s] of e)
|
|
818
|
-
m(this,
|
|
818
|
+
m(this, T, M).call(this, t, s.resource);
|
|
819
819
|
}
|
|
820
820
|
evictLeastRecentlyUsed() {
|
|
821
821
|
const e = this.storage.keys().next().value;
|
|
@@ -829,7 +829,7 @@ const D = class D {
|
|
|
829
829
|
this.delete(s);
|
|
830
830
|
}
|
|
831
831
|
};
|
|
832
|
-
|
|
832
|
+
T = new WeakSet(), /**
|
|
833
833
|
* @param key - The key being evicted
|
|
834
834
|
* @param resource - The resource being evicted
|
|
835
835
|
*/
|
|
@@ -845,10 +845,10 @@ M = function(e, t) {
|
|
|
845
845
|
}
|
|
846
846
|
};
|
|
847
847
|
let E = D;
|
|
848
|
-
function
|
|
848
|
+
function rt(r) {
|
|
849
849
|
return r;
|
|
850
850
|
}
|
|
851
|
-
function
|
|
851
|
+
function ot(r) {
|
|
852
852
|
return r;
|
|
853
853
|
}
|
|
854
854
|
function Z(r, e, t, s) {
|
|
@@ -936,7 +936,7 @@ function P(r, e, t) {
|
|
|
936
936
|
}
|
|
937
937
|
};
|
|
938
938
|
}
|
|
939
|
-
const
|
|
939
|
+
const Se = g.string({ message: "Missing required mcp-client-id header" }).trim().min(1, "mcp-client-id header must not be empty");
|
|
940
940
|
class O {
|
|
941
941
|
constructor(e, t, s = {}, o, n, i) {
|
|
942
942
|
d(this, "options");
|
|
@@ -1061,7 +1061,7 @@ class O {
|
|
|
1061
1061
|
e.post(
|
|
1062
1062
|
`${t}/mcp`,
|
|
1063
1063
|
async (s, o) => {
|
|
1064
|
-
const n =
|
|
1064
|
+
const n = Se.safeParse(
|
|
1065
1065
|
s.headers["mcp-client-id"]
|
|
1066
1066
|
);
|
|
1067
1067
|
if (!n.success)
|
|
@@ -1088,13 +1088,16 @@ class O {
|
|
|
1088
1088
|
if (u && c.sessions.get(u))
|
|
1089
1089
|
f = c.sessions.get(u);
|
|
1090
1090
|
else if (!u && G(s.body)) {
|
|
1091
|
+
await this.drainExistingSessions(c.sessions), await this.disconnectServer(c.server);
|
|
1091
1092
|
const v = S();
|
|
1092
1093
|
f = new Q({
|
|
1093
1094
|
sessionIdGenerator: () => v,
|
|
1094
1095
|
onsessioninitialized: (b) => {
|
|
1095
1096
|
c.sessions.set(b, f);
|
|
1096
1097
|
}
|
|
1097
|
-
})
|
|
1098
|
+
}), f.onclose = () => {
|
|
1099
|
+
f?.sessionId && c.sessions.delete(f.sessionId);
|
|
1100
|
+
};
|
|
1098
1101
|
try {
|
|
1099
1102
|
await c.server.connect(f);
|
|
1100
1103
|
} catch {
|
|
@@ -1104,20 +1107,13 @@ class O {
|
|
|
1104
1107
|
id: null
|
|
1105
1108
|
};
|
|
1106
1109
|
}
|
|
1107
|
-
f.onclose = () => {
|
|
1108
|
-
f?.sessionId && c.sessions.delete(f.sessionId);
|
|
1109
|
-
};
|
|
1110
1110
|
} else
|
|
1111
1111
|
return o.code(400), {
|
|
1112
1112
|
jsonrpc: "2.0",
|
|
1113
1113
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
1114
1114
|
id: null
|
|
1115
1115
|
};
|
|
1116
|
-
return await f.handleRequest(
|
|
1117
|
-
s.raw,
|
|
1118
|
-
o.raw,
|
|
1119
|
-
s.body
|
|
1120
|
-
), o;
|
|
1116
|
+
return await f.handleRequest(s.raw, o.raw, s.body), o;
|
|
1121
1117
|
}
|
|
1122
1118
|
);
|
|
1123
1119
|
}
|
|
@@ -1166,11 +1162,8 @@ class O {
|
|
|
1166
1162
|
id: null
|
|
1167
1163
|
};
|
|
1168
1164
|
try {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
await u.close();
|
|
1172
|
-
} catch {
|
|
1173
|
-
}
|
|
1165
|
+
await u.close();
|
|
1166
|
+
} catch {
|
|
1174
1167
|
} finally {
|
|
1175
1168
|
u?.sessionId ? c.sessions.delete(u.sessionId) : c.sessions.delete(a);
|
|
1176
1169
|
}
|
|
@@ -1181,18 +1174,53 @@ class O {
|
|
|
1181
1174
|
async stop() {
|
|
1182
1175
|
this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
|
|
1183
1176
|
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Closes all active sessions and clears the session map so the server is
|
|
1179
|
+
* free to accept a new connection. Required for SDK 1.26+, which throws
|
|
1180
|
+
* "Already connected" when `connect()` is called while a transport is
|
|
1181
|
+
* still attached. Handles unclean client disconnects followed by re-init.
|
|
1182
|
+
*
|
|
1183
|
+
* The map is cleared before closing so that `onclose` handlers fired by
|
|
1184
|
+
* `transport.close()` do not attempt double-deletion.
|
|
1185
|
+
*
|
|
1186
|
+
* @param sessions - The client's active session map to drain
|
|
1187
|
+
*/
|
|
1188
|
+
async drainExistingSessions(e) {
|
|
1189
|
+
if (e.size === 0) return;
|
|
1190
|
+
const t = Array.from(e.values());
|
|
1191
|
+
e.clear();
|
|
1192
|
+
for (const s of t)
|
|
1193
|
+
try {
|
|
1194
|
+
await s.close();
|
|
1195
|
+
} catch {
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Disconnects the server from its current transport so that `Protocol._transport`
|
|
1200
|
+
* is cleared before a new connection is established.
|
|
1201
|
+
*
|
|
1202
|
+
* `drainExistingSessions` handles same-bundle reconnects (sessions in the map).
|
|
1203
|
+
* This method handles the STATIC-mode cross-client case: a different client's
|
|
1204
|
+
* bundle has an empty sessions map, but the shared server is still attached to
|
|
1205
|
+
* the previous client's transport because `StreamableHTTPClientTransport.close()`
|
|
1206
|
+
* does not send DELETE—it only aborts connections.
|
|
1207
|
+
*
|
|
1208
|
+
* @param server - The MCP server to disconnect from its current transport
|
|
1209
|
+
*/
|
|
1210
|
+
async disconnectServer(e) {
|
|
1211
|
+
try {
|
|
1212
|
+
await e.close();
|
|
1213
|
+
} catch {
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1184
1216
|
/**
|
|
1185
1217
|
* @param bundle - The client bundle to clean up
|
|
1186
1218
|
*/
|
|
1187
1219
|
cleanupBundle(e) {
|
|
1188
1220
|
for (const [t, s] of e.sessions.entries())
|
|
1189
|
-
|
|
1190
|
-
typeof s.close == "function" && s.close().catch((o) => {
|
|
1191
|
-
console.warn(`Error closing session ${t}:`, o);
|
|
1192
|
-
});
|
|
1193
|
-
} catch (o) {
|
|
1221
|
+
s.close().catch((o) => {
|
|
1194
1222
|
console.warn(`Error closing session ${t}:`, o);
|
|
1195
|
-
}
|
|
1223
|
+
});
|
|
1196
1224
|
e.sessions.clear();
|
|
1197
1225
|
}
|
|
1198
1226
|
/**
|
|
@@ -1391,25 +1419,25 @@ class k {
|
|
|
1391
1419
|
for (const n of t)
|
|
1392
1420
|
s[n] = e[n];
|
|
1393
1421
|
const o = JSON.stringify(s);
|
|
1394
|
-
return
|
|
1422
|
+
return Te("sha256").update(o).digest("hex").slice(0, 16);
|
|
1395
1423
|
}
|
|
1396
1424
|
}
|
|
1397
1425
|
function X(r) {
|
|
1398
|
-
|
|
1426
|
+
Ee(r), Ce(r), Ae(r), Pe(r), Me(r);
|
|
1399
1427
|
}
|
|
1400
|
-
function
|
|
1428
|
+
function Ee(r) {
|
|
1401
1429
|
if (!r || typeof r != "object")
|
|
1402
1430
|
throw new Error(
|
|
1403
1431
|
"Session context configuration must be an object"
|
|
1404
1432
|
);
|
|
1405
1433
|
}
|
|
1406
|
-
function
|
|
1434
|
+
function Ce(r) {
|
|
1407
1435
|
if (r.enabled !== void 0 && typeof r.enabled != "boolean")
|
|
1408
1436
|
throw new Error(
|
|
1409
1437
|
`enabled must be a boolean, got ${typeof r.enabled}`
|
|
1410
1438
|
);
|
|
1411
1439
|
}
|
|
1412
|
-
function
|
|
1440
|
+
function Ae(r) {
|
|
1413
1441
|
if (r.queryParam !== void 0) {
|
|
1414
1442
|
if (typeof r.queryParam != "object" || r.queryParam === null)
|
|
1415
1443
|
throw new Error("queryParam must be an object");
|
|
@@ -1432,25 +1460,25 @@ function Ee(r) {
|
|
|
1432
1460
|
}
|
|
1433
1461
|
}
|
|
1434
1462
|
}
|
|
1435
|
-
function
|
|
1463
|
+
function Pe(r) {
|
|
1436
1464
|
if (r.contextResolver !== void 0 && typeof r.contextResolver != "function")
|
|
1437
1465
|
throw new Error(
|
|
1438
1466
|
"contextResolver must be a function: (request, baseContext, parsedQueryConfig?) => unknown"
|
|
1439
1467
|
);
|
|
1440
1468
|
}
|
|
1441
|
-
function
|
|
1469
|
+
function Me(r) {
|
|
1442
1470
|
if (r.merge !== void 0 && r.merge !== "shallow" && r.merge !== "deep")
|
|
1443
1471
|
throw new Error(
|
|
1444
1472
|
`Invalid merge strategy: "${r.merge}". Must be "shallow" or "deep"`
|
|
1445
1473
|
);
|
|
1446
1474
|
}
|
|
1447
|
-
const
|
|
1475
|
+
const Ie = g.object({
|
|
1448
1476
|
mode: g.enum(["DYNAMIC", "STATIC"]).optional(),
|
|
1449
1477
|
toolsets: g.union([g.array(g.string()), g.literal("ALL")]).optional()
|
|
1450
1478
|
}).strict();
|
|
1451
|
-
function
|
|
1479
|
+
function $e(r) {
|
|
1452
1480
|
try {
|
|
1453
|
-
|
|
1481
|
+
Ie.parse(r);
|
|
1454
1482
|
} catch (e) {
|
|
1455
1483
|
if (e instanceof g.ZodError) {
|
|
1456
1484
|
const t = e.format();
|
|
@@ -1464,7 +1492,7 @@ Hint: Common mistake - use "toolsets" not "initialToolsets"`
|
|
|
1464
1492
|
throw e;
|
|
1465
1493
|
}
|
|
1466
1494
|
}
|
|
1467
|
-
function
|
|
1495
|
+
function Re() {
|
|
1468
1496
|
const r = (t) => typeof t?.server?.notification == "function", e = (t) => typeof t?.notifyToolsListChanged == "function";
|
|
1469
1497
|
return async (t) => {
|
|
1470
1498
|
try {
|
|
@@ -1482,12 +1510,12 @@ function Ie() {
|
|
|
1482
1510
|
}
|
|
1483
1511
|
};
|
|
1484
1512
|
}
|
|
1485
|
-
function
|
|
1513
|
+
function Le(r, e) {
|
|
1486
1514
|
return r !== void 0 ? r : e === "DYNAMIC";
|
|
1487
1515
|
}
|
|
1488
|
-
async function
|
|
1489
|
-
|
|
1490
|
-
const e = r.startup?.mode ?? "DYNAMIC", t =
|
|
1516
|
+
async function nt(r) {
|
|
1517
|
+
je(r);
|
|
1518
|
+
const e = r.startup?.mode ?? "DYNAMIC", t = Le(r.registerMetaTools, e), s = Ne(r, e), o = Re(), n = r.createServer(), i = ee(
|
|
1491
1519
|
n,
|
|
1492
1520
|
r,
|
|
1493
1521
|
e,
|
|
@@ -1495,14 +1523,14 @@ async function rt(r) {
|
|
|
1495
1523
|
o
|
|
1496
1524
|
);
|
|
1497
1525
|
e === "STATIC" && await i.ensureReady();
|
|
1498
|
-
const a =
|
|
1526
|
+
const a = Oe(
|
|
1499
1527
|
r,
|
|
1500
1528
|
e,
|
|
1501
1529
|
n,
|
|
1502
1530
|
i,
|
|
1503
1531
|
t,
|
|
1504
1532
|
o
|
|
1505
|
-
), l =
|
|
1533
|
+
), l = ke(
|
|
1506
1534
|
r,
|
|
1507
1535
|
i.getManager(),
|
|
1508
1536
|
a,
|
|
@@ -1514,11 +1542,11 @@ async function rt(r) {
|
|
|
1514
1542
|
close: () => l.stop()
|
|
1515
1543
|
};
|
|
1516
1544
|
}
|
|
1517
|
-
function
|
|
1518
|
-
if (r.startup &&
|
|
1545
|
+
function je(r) {
|
|
1546
|
+
if (r.startup && $e(r.startup), typeof r.createServer != "function")
|
|
1519
1547
|
throw new Error("createMcpServer: `createServer` (factory) is required");
|
|
1520
1548
|
}
|
|
1521
|
-
function
|
|
1549
|
+
function Ne(r, e) {
|
|
1522
1550
|
if (!r.sessionContext) return;
|
|
1523
1551
|
X(r.sessionContext);
|
|
1524
1552
|
const t = k.builder().enabled(r.sessionContext.enabled ?? !0).queryParam(r.sessionContext.queryParam).contextResolver(r.sessionContext.contextResolver).merge(r.sessionContext.merge ?? "shallow").build();
|
|
@@ -1530,7 +1558,7 @@ function ee(r, e, t, s, o, n) {
|
|
|
1530
1558
|
const i = A.builder().server(r).catalog(e.catalog).moduleLoaders(e.moduleLoaders ?? {}).context(n !== void 0 ? n : e.context).notifyToolsListChanged(async () => o(r)).registerMetaTools(s);
|
|
1531
1559
|
return e.exposurePolicy && i.exposurePolicy(e.exposurePolicy), e.startup && i.startup(e.startup), i.build();
|
|
1532
1560
|
}
|
|
1533
|
-
function
|
|
1561
|
+
function Oe(r, e, t, s, o, n) {
|
|
1534
1562
|
return (i) => {
|
|
1535
1563
|
if (e === "STATIC")
|
|
1536
1564
|
return { server: t, orchestrator: s };
|
|
@@ -1545,20 +1573,20 @@ function je(r, e, t, s, o, n) {
|
|
|
1545
1573
|
return { server: l, orchestrator: c };
|
|
1546
1574
|
};
|
|
1547
1575
|
}
|
|
1548
|
-
function
|
|
1576
|
+
function ke(r, e, t, s) {
|
|
1549
1577
|
const o = O.builder().defaultManager(e).createBundle(t).host(r.http?.host ?? "0.0.0.0").port(r.http?.port ?? 3e3).basePath(r.http?.basePath ?? "/").cors(r.http?.cors ?? !0).logger(r.http?.logger ?? !1);
|
|
1550
1578
|
return r.http?.app && o.app(r.http.app), r.http?.customEndpoints && o.customEndpoints(r.http.customEndpoints), r.configSchema && o.configSchema(r.configSchema), s && o.sessionContextResolver(s), r.context !== void 0 && o.baseContext(r.context), o.build();
|
|
1551
1579
|
}
|
|
1552
|
-
function
|
|
1553
|
-
|
|
1580
|
+
function De(r) {
|
|
1581
|
+
ze(r), qe(r), Fe(r), _e(r);
|
|
1554
1582
|
}
|
|
1555
|
-
function
|
|
1583
|
+
function ze(r) {
|
|
1556
1584
|
if (!r || typeof r != "object")
|
|
1557
1585
|
throw new Error(
|
|
1558
1586
|
"Permission configuration is required for createPermissionBasedMcpServer"
|
|
1559
1587
|
);
|
|
1560
1588
|
}
|
|
1561
|
-
function
|
|
1589
|
+
function qe(r) {
|
|
1562
1590
|
if (!r.source)
|
|
1563
1591
|
throw new Error('Permission source must be either "headers" or "config"');
|
|
1564
1592
|
if (r.source !== "headers" && r.source !== "config")
|
|
@@ -1566,19 +1594,19 @@ function De(r) {
|
|
|
1566
1594
|
`Invalid permission source: "${r.source}". Must be either "headers" or "config"`
|
|
1567
1595
|
);
|
|
1568
1596
|
}
|
|
1569
|
-
function
|
|
1597
|
+
function Fe(r) {
|
|
1570
1598
|
if (r.source === "config" && !r.staticMap && !r.resolver)
|
|
1571
1599
|
throw new Error(
|
|
1572
1600
|
"Config-based permissions require at least one of: staticMap or resolver function"
|
|
1573
1601
|
);
|
|
1574
1602
|
}
|
|
1575
|
-
function
|
|
1603
|
+
function _e(r) {
|
|
1576
1604
|
if (r.staticMap !== void 0) {
|
|
1577
1605
|
if (typeof r.staticMap != "object" || r.staticMap === null)
|
|
1578
1606
|
throw new Error(
|
|
1579
1607
|
"staticMap must be an object mapping client IDs to toolset arrays"
|
|
1580
1608
|
);
|
|
1581
|
-
|
|
1609
|
+
Ke(r.staticMap);
|
|
1582
1610
|
}
|
|
1583
1611
|
if (r.resolver !== void 0 && typeof r.resolver != "function")
|
|
1584
1612
|
throw new Error(
|
|
@@ -1589,14 +1617,14 @@ function qe(r) {
|
|
|
1589
1617
|
if (r.headerName !== void 0 && (typeof r.headerName != "string" || r.headerName.length === 0))
|
|
1590
1618
|
throw new Error("headerName must be a non-empty string");
|
|
1591
1619
|
}
|
|
1592
|
-
function
|
|
1620
|
+
function Ke(r) {
|
|
1593
1621
|
for (const [e, t] of Object.entries(r))
|
|
1594
1622
|
if (!Array.isArray(t))
|
|
1595
1623
|
throw new Error(
|
|
1596
1624
|
`staticMap value for client "${e}" must be an array of toolset names`
|
|
1597
1625
|
);
|
|
1598
1626
|
}
|
|
1599
|
-
function
|
|
1627
|
+
function Be(r, e) {
|
|
1600
1628
|
return async (t) => {
|
|
1601
1629
|
const s = e.resolvePermissions(
|
|
1602
1630
|
t.clientId,
|
|
@@ -1621,7 +1649,7 @@ function _e(r, e) {
|
|
|
1621
1649
|
};
|
|
1622
1650
|
};
|
|
1623
1651
|
}
|
|
1624
|
-
function
|
|
1652
|
+
function He(r) {
|
|
1625
1653
|
if (!r) return;
|
|
1626
1654
|
const e = {
|
|
1627
1655
|
namespaceToolsWithSetKey: r.namespaceToolsWithSetKey
|
|
@@ -1770,8 +1798,8 @@ ne = function(e) {
|
|
|
1770
1798
|
return t !== void 0 ? Array.isArray(t) ? t : [] : null;
|
|
1771
1799
|
};
|
|
1772
1800
|
let I = z;
|
|
1773
|
-
const
|
|
1774
|
-
var h, ie, ae, le, ce, de, ue, he, fe, R,
|
|
1801
|
+
const Ve = g.string({ message: "Missing required mcp-client-id header" }).trim().min(1, "mcp-client-id header must not be empty");
|
|
1802
|
+
var h, ie, ae, le, ce, de, ue, he, fe, me, pe, R, ge;
|
|
1775
1803
|
const q = class q {
|
|
1776
1804
|
constructor(e, t, s = {}, o) {
|
|
1777
1805
|
x(this, h);
|
|
@@ -1783,7 +1811,7 @@ const q = class q {
|
|
|
1783
1811
|
// Per-client server bundles and per-client session transports
|
|
1784
1812
|
d(this, "clientCache", new E({
|
|
1785
1813
|
onEvict: (e, t) => {
|
|
1786
|
-
m(this, h,
|
|
1814
|
+
m(this, h, le).call(this, t);
|
|
1787
1815
|
}
|
|
1788
1816
|
}));
|
|
1789
1817
|
this.defaultManager = e, this.createPermissionAwareBundle = t, this.options = {
|
|
@@ -1841,8 +1869,8 @@ const q = class q {
|
|
|
1841
1869
|
if (this.app) return;
|
|
1842
1870
|
const e = this.options.app ?? U({ logger: this.options.logger });
|
|
1843
1871
|
this.options.cors && await e.register(J, { origin: !0 });
|
|
1844
|
-
const t = m(this, h,
|
|
1845
|
-
m(this, h,
|
|
1872
|
+
const t = m(this, h, ce).call(this, this.options.basePath);
|
|
1873
|
+
m(this, h, de).call(this, e, t), m(this, h, ue).call(this, e, t), m(this, h, he).call(this, e, t), m(this, h, fe).call(this, e, t), m(this, h, me).call(this, e, t), m(this, h, pe).call(this, e, t), this.options.customEndpoints && this.options.customEndpoints.length > 0 && Z(e, t, this.options.customEndpoints, {
|
|
1846
1874
|
contextExtractor: async (s) => {
|
|
1847
1875
|
const o = m(this, h, R).call(this, s);
|
|
1848
1876
|
try {
|
|
@@ -1866,42 +1894,52 @@ const q = class q {
|
|
|
1866
1894
|
this.app && (this.clientCache.stop(!0), this.options.app || await this.app.close(), this.app = null);
|
|
1867
1895
|
}
|
|
1868
1896
|
};
|
|
1869
|
-
h = new WeakSet(),
|
|
1897
|
+
h = new WeakSet(), ie = async function(e) {
|
|
1898
|
+
if (e.size === 0) return;
|
|
1899
|
+
const t = Array.from(e.values());
|
|
1900
|
+
e.clear();
|
|
1901
|
+
for (const s of t)
|
|
1902
|
+
try {
|
|
1903
|
+
await s.close();
|
|
1904
|
+
} catch {
|
|
1905
|
+
}
|
|
1906
|
+
}, ae = async function(e) {
|
|
1907
|
+
try {
|
|
1908
|
+
await e.close();
|
|
1909
|
+
} catch {
|
|
1910
|
+
}
|
|
1911
|
+
}, /**
|
|
1870
1912
|
* @param bundle - The client bundle to clean up
|
|
1871
1913
|
*/
|
|
1872
|
-
|
|
1914
|
+
le = function(e) {
|
|
1873
1915
|
for (const [t, s] of e.sessions.entries())
|
|
1874
|
-
|
|
1875
|
-
typeof s.close == "function" && s.close().catch((o) => {
|
|
1876
|
-
console.warn(`Error closing session ${t}:`, o);
|
|
1877
|
-
});
|
|
1878
|
-
} catch (o) {
|
|
1916
|
+
s.close().catch((o) => {
|
|
1879
1917
|
console.warn(`Error closing session ${t}:`, o);
|
|
1880
|
-
}
|
|
1918
|
+
});
|
|
1881
1919
|
e.sessions.clear();
|
|
1882
1920
|
}, /**
|
|
1883
1921
|
* @param basePath - The base path to normalize
|
|
1884
1922
|
* @returns Normalized base path without trailing slash
|
|
1885
1923
|
*/
|
|
1886
|
-
|
|
1924
|
+
ce = function(e) {
|
|
1887
1925
|
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
1888
1926
|
}, /**
|
|
1889
1927
|
* @param app - Fastify instance
|
|
1890
1928
|
* @param base - Base path for routes
|
|
1891
1929
|
*/
|
|
1892
|
-
|
|
1930
|
+
de = function(e, t) {
|
|
1893
1931
|
e.get(`${t}/healthz`, async () => ({ ok: !0 }));
|
|
1894
1932
|
}, /**
|
|
1895
1933
|
* @param app - Fastify instance
|
|
1896
1934
|
* @param base - Base path for routes
|
|
1897
1935
|
*/
|
|
1898
|
-
|
|
1936
|
+
ue = function(e, t) {
|
|
1899
1937
|
e.get(`${t}/tools`, async () => this.defaultManager.getStatus());
|
|
1900
1938
|
}, /**
|
|
1901
1939
|
* @param app - Fastify instance
|
|
1902
1940
|
* @param base - Base path for routes
|
|
1903
1941
|
*/
|
|
1904
|
-
|
|
1942
|
+
he = function(e, t) {
|
|
1905
1943
|
e.get(`${t}/.well-known/mcp-config`, async (s, o) => (o.header("Content-Type", "application/schema+json; charset=utf-8"), this.configSchema ?? {
|
|
1906
1944
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1907
1945
|
title: "MCP Session Configuration",
|
|
@@ -1916,11 +1954,11 @@ de = function(e, t) {
|
|
|
1916
1954
|
* @param app - Fastify instance
|
|
1917
1955
|
* @param base - Base path for routes
|
|
1918
1956
|
*/
|
|
1919
|
-
|
|
1957
|
+
fe = function(e, t) {
|
|
1920
1958
|
e.post(
|
|
1921
1959
|
`${t}/mcp`,
|
|
1922
1960
|
async (s, o) => {
|
|
1923
|
-
const n =
|
|
1961
|
+
const n = Ve.safeParse(
|
|
1924
1962
|
s.headers["mcp-client-id"]
|
|
1925
1963
|
);
|
|
1926
1964
|
if (!n.success)
|
|
@@ -1949,20 +1987,23 @@ ue = function(e, t) {
|
|
|
1949
1987
|
return console.error(
|
|
1950
1988
|
`Failed to create permission-aware bundle for client ${i.clientId}:`,
|
|
1951
1989
|
u
|
|
1952
|
-
), o.code(403), m(this, h,
|
|
1990
|
+
), o.code(403), m(this, h, ge).call(this, "Access denied");
|
|
1953
1991
|
}
|
|
1954
1992
|
const l = s.headers["mcp-session-id"];
|
|
1955
1993
|
let c;
|
|
1956
1994
|
if (l && a.sessions.get(l))
|
|
1957
1995
|
c = a.sessions.get(l);
|
|
1958
1996
|
else if (!l && G(s.body)) {
|
|
1997
|
+
await m(this, h, ie).call(this, a.sessions), await m(this, h, ae).call(this, a.server);
|
|
1959
1998
|
const u = S();
|
|
1960
1999
|
c = new Q({
|
|
1961
2000
|
sessionIdGenerator: () => u,
|
|
1962
2001
|
onsessioninitialized: (f) => {
|
|
1963
2002
|
a.sessions.set(f, c);
|
|
1964
2003
|
}
|
|
1965
|
-
})
|
|
2004
|
+
}), c.onclose = () => {
|
|
2005
|
+
c?.sessionId && a.sessions.delete(c.sessionId);
|
|
2006
|
+
};
|
|
1966
2007
|
try {
|
|
1967
2008
|
await a.server.connect(c);
|
|
1968
2009
|
} catch {
|
|
@@ -1972,27 +2013,20 @@ ue = function(e, t) {
|
|
|
1972
2013
|
id: null
|
|
1973
2014
|
};
|
|
1974
2015
|
}
|
|
1975
|
-
c.onclose = () => {
|
|
1976
|
-
c?.sessionId && a.sessions.delete(c.sessionId);
|
|
1977
|
-
};
|
|
1978
2016
|
} else
|
|
1979
2017
|
return o.code(400), {
|
|
1980
2018
|
jsonrpc: "2.0",
|
|
1981
2019
|
error: { code: -32e3, message: "Session not found or expired" },
|
|
1982
2020
|
id: null
|
|
1983
2021
|
};
|
|
1984
|
-
return await c.handleRequest(
|
|
1985
|
-
s.raw,
|
|
1986
|
-
o.raw,
|
|
1987
|
-
s.body
|
|
1988
|
-
), o;
|
|
2022
|
+
return await c.handleRequest(s.raw, o.raw, s.body), o;
|
|
1989
2023
|
}
|
|
1990
2024
|
);
|
|
1991
2025
|
}, /**
|
|
1992
2026
|
* @param app - Fastify instance
|
|
1993
2027
|
* @param base - Base path for routes
|
|
1994
2028
|
*/
|
|
1995
|
-
|
|
2029
|
+
me = function(e, t) {
|
|
1996
2030
|
e.get(`${t}/mcp`, async (s, o) => {
|
|
1997
2031
|
const n = s.headers["mcp-client-id"]?.trim(), i = n && n.length > 0 ? n : "";
|
|
1998
2032
|
if (!i)
|
|
@@ -2010,7 +2044,7 @@ he = function(e, t) {
|
|
|
2010
2044
|
* @param app - Fastify instance
|
|
2011
2045
|
* @param base - Base path for routes
|
|
2012
2046
|
*/
|
|
2013
|
-
|
|
2047
|
+
pe = function(e, t) {
|
|
2014
2048
|
e.delete(
|
|
2015
2049
|
`${t}/mcp`,
|
|
2016
2050
|
async (s, o) => {
|
|
@@ -2032,11 +2066,8 @@ fe = function(e, t) {
|
|
|
2032
2066
|
id: null
|
|
2033
2067
|
};
|
|
2034
2068
|
try {
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
await c.close();
|
|
2038
|
-
} catch {
|
|
2039
|
-
}
|
|
2069
|
+
await c.close();
|
|
2070
|
+
} catch {
|
|
2040
2071
|
} finally {
|
|
2041
2072
|
c?.sessionId ? l.sessions.delete(c.sessionId) : l.sessions.delete(a);
|
|
2042
2073
|
}
|
|
@@ -2057,7 +2088,7 @@ R = function(e) {
|
|
|
2057
2088
|
* @param code - JSON-RPC error code
|
|
2058
2089
|
* @returns JSON-RPC error response object
|
|
2059
2090
|
*/
|
|
2060
|
-
|
|
2091
|
+
ge = function(e = "Access denied", t = -32e3) {
|
|
2061
2092
|
return {
|
|
2062
2093
|
jsonrpc: "2.0",
|
|
2063
2094
|
error: {
|
|
@@ -2068,12 +2099,12 @@ me = function(e = "Access denied", t = -32e3) {
|
|
|
2068
2099
|
};
|
|
2069
2100
|
};
|
|
2070
2101
|
let $ = q;
|
|
2071
|
-
async function
|
|
2072
|
-
|
|
2073
|
-
const e =
|
|
2074
|
-
|
|
2102
|
+
async function it(r) {
|
|
2103
|
+
We(r);
|
|
2104
|
+
const e = He(r.exposurePolicy), t = Ye(r), s = r.createServer(), o = ye(s, r, e), n = Be(
|
|
2105
|
+
Ue(r, e),
|
|
2075
2106
|
t
|
|
2076
|
-
), i =
|
|
2107
|
+
), i = Je(r, o.getManager(), n);
|
|
2077
2108
|
return {
|
|
2078
2109
|
server: s,
|
|
2079
2110
|
start: () => i.start(),
|
|
@@ -2086,12 +2117,12 @@ async function ot(r) {
|
|
|
2086
2117
|
}
|
|
2087
2118
|
};
|
|
2088
2119
|
}
|
|
2089
|
-
function
|
|
2120
|
+
function We(r) {
|
|
2090
2121
|
if (!r.permissions)
|
|
2091
2122
|
throw new Error(
|
|
2092
2123
|
"Permission configuration is required for createPermissionBasedMcpServer. Please provide a 'permissions' field in the options."
|
|
2093
2124
|
);
|
|
2094
|
-
if (
|
|
2125
|
+
if (De(r.permissions), r.sessionContext && (X(r.sessionContext), console.warn(
|
|
2095
2126
|
"Session context support for permission-based servers is limited. The base context will be used for module loaders."
|
|
2096
2127
|
)), r.startup)
|
|
2097
2128
|
throw new Error(
|
|
@@ -2102,29 +2133,29 @@ function He(r) {
|
|
|
2102
2133
|
"createPermissionBasedMcpServer: `createServer` (factory) is required"
|
|
2103
2134
|
);
|
|
2104
2135
|
}
|
|
2105
|
-
function
|
|
2136
|
+
function Ye(r) {
|
|
2106
2137
|
const e = I.builder().source(r.permissions.source).headerName(r.permissions.headerName ?? "mcp-toolset-permissions").staticMap(r.permissions.staticMap ?? {}).defaultPermissions(r.permissions.defaultPermissions ?? []);
|
|
2107
2138
|
return r.permissions.resolver && e.resolver(r.permissions.resolver), e.build();
|
|
2108
2139
|
}
|
|
2109
|
-
function
|
|
2140
|
+
function ye(r, e, t) {
|
|
2110
2141
|
const s = A.builder().server(r).catalog(e.catalog).moduleLoaders(e.moduleLoaders ?? {}).context(e.context).startup({ mode: "STATIC", toolsets: [] }).registerMetaTools(!1);
|
|
2111
2142
|
return t && s.exposurePolicy(t), s.build();
|
|
2112
2143
|
}
|
|
2113
|
-
function
|
|
2144
|
+
function Ue(r, e) {
|
|
2114
2145
|
return (t) => {
|
|
2115
|
-
const s = r.createServer(), o =
|
|
2146
|
+
const s = r.createServer(), o = ye(s, r, e);
|
|
2116
2147
|
return { server: s, orchestrator: o };
|
|
2117
2148
|
};
|
|
2118
2149
|
}
|
|
2119
|
-
function
|
|
2150
|
+
function Je(r, e, t) {
|
|
2120
2151
|
const s = $.builder().defaultManager(e).createPermissionAwareBundle(t).host(r.http?.host ?? "0.0.0.0").port(r.http?.port ?? 3e3).basePath(r.http?.basePath ?? "/").cors(r.http?.cors ?? !0).logger(r.http?.logger ?? !1);
|
|
2121
2152
|
return r.http?.app && s.app(r.http.app), r.http?.customEndpoints && s.customEndpoints(r.http.customEndpoints), r.configSchema && s.configSchema(r.configSchema), s.build();
|
|
2122
2153
|
}
|
|
2123
2154
|
export {
|
|
2124
2155
|
k as SessionContextResolver,
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2156
|
+
nt as createMcpServer,
|
|
2157
|
+
it as createPermissionBasedMcpServer,
|
|
2158
|
+
rt as defineEndpoint,
|
|
2159
|
+
ot as definePermissionAwareEndpoint
|
|
2129
2160
|
};
|
|
2130
2161
|
//# sourceMappingURL=index.js.map
|