@mcp-ts/sdk 2.4.5 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/agui-adapter.d.mts +3 -3
- package/dist/adapters/agui-adapter.d.ts +3 -3
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +3 -3
- package/dist/adapters/agui-middleware.d.ts +3 -3
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +3 -3
- package/dist/adapters/ai-adapter.d.ts +3 -3
- package/dist/adapters/ai-adapter.js.map +1 -1
- package/dist/adapters/ai-adapter.mjs.map +1 -1
- package/dist/adapters/langchain-adapter.d.mts +3 -3
- package/dist/adapters/langchain-adapter.d.ts +3 -3
- package/dist/adapters/langchain-adapter.js +2 -2
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs +2 -2
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/adapters/mastra-adapter.d.mts +2 -2
- package/dist/adapters/mastra-adapter.d.ts +2 -2
- package/dist/adapters/mastra-adapter.js +2 -2
- package/dist/adapters/mastra-adapter.js.map +1 -1
- package/dist/adapters/mastra-adapter.mjs +2 -2
- package/dist/adapters/mastra-adapter.mjs.map +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +6 -0
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +15 -4
- package/dist/client/react.d.ts +15 -4
- package/dist/client/react.js +34 -0
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +34 -0
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +4 -4
- package/dist/client/vue.d.ts +4 -4
- package/dist/client/vue.js +6 -0
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +6 -0
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{index-CkM3p0eE.d.mts → index-CTURVnom.d.mts} +5 -1
- package/dist/{index-CZk2gu2E.d.ts → index-sVcqrhf7.d.ts} +5 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +314 -82
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +310 -82
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-ChQrBZhF.d.mts → multi-session-client-CSPSHHla.d.ts} +14 -2
- package/dist/{multi-session-client-QOOPdEVp.d.ts → multi-session-client-DMZGVABI.d.mts} +14 -2
- package/dist/server/index.d.mts +13 -4
- package/dist/server/index.d.ts +13 -4
- package/dist/server/index.js +308 -82
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +304 -82
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +4 -4
- package/dist/shared/index.d.ts +4 -4
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{tool-router-DQ-HrD7W.d.ts → tool-router-CS9l0w4a.d.ts} +1 -1
- package/dist/{tool-router-CGs3IDOJ.d.mts → tool-router-CVLBaCwH.d.mts} +1 -1
- package/dist/{types-Bf-7GOLW.d.mts → types-DK_NGWd4.d.mts} +37 -3
- package/dist/{types-Bf-7GOLW.d.ts → types-DK_NGWd4.d.ts} +37 -3
- package/migrations/neon/20260513010000_install_mcp_sessions.sql +1 -0
- package/migrations/supabase/20260330195700_install_mcp_sessions.sql +1 -0
- package/package.json +1 -1
- package/src/adapters/agui-adapter.ts +6 -2
- package/src/adapters/ai-adapter.ts +3 -1
- package/src/adapters/langchain-adapter.ts +5 -3
- package/src/adapters/mastra-adapter.ts +5 -3
- package/src/client/core/sse-client.ts +17 -0
- package/src/client/react/use-mcp.ts +63 -0
- package/src/server/handlers/sse-handler.ts +116 -9
- package/src/server/mcp/multi-session-client.ts +7 -3
- package/src/server/mcp/oauth-client.ts +25 -10
- package/src/server/mcp/tool-policy-gateway.ts +90 -0
- package/src/server/storage/neon-backend.ts +65 -60
- package/src/server/storage/session-lifecycle.ts +5 -0
- package/src/server/storage/supabase-backend.ts +27 -15
- package/src/server/storage/tool-policy.ts +89 -0
- package/src/server/storage/types.ts +11 -0
- package/src/shared/types.ts +48 -1
package/dist/index.js
CHANGED
|
@@ -216,6 +216,60 @@ var SOFTWARE_VERSION = "2.3.4";
|
|
|
216
216
|
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
217
217
|
var MCP_CLIENT_VERSION = "2.0";
|
|
218
218
|
|
|
219
|
+
// src/server/storage/tool-policy.ts
|
|
220
|
+
init_cjs_shims();
|
|
221
|
+
function createToolId(serverId, toolName) {
|
|
222
|
+
return `${serverId}::${toolName}`;
|
|
223
|
+
}
|
|
224
|
+
function normalizeToolIds(input) {
|
|
225
|
+
if (!Array.isArray(input)) return [];
|
|
226
|
+
return Array.from(new Set(
|
|
227
|
+
input.filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean)
|
|
228
|
+
));
|
|
229
|
+
}
|
|
230
|
+
function normalizeToolPolicy(input, now = Date.now()) {
|
|
231
|
+
if (!input || typeof input !== "object") {
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
const mode = input.mode === "allowlist" || input.mode === "denylist" ? input.mode : "all";
|
|
235
|
+
const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : now;
|
|
236
|
+
if (mode === "all") {
|
|
237
|
+
return { mode: "all", toolIds: [], updatedAt };
|
|
238
|
+
}
|
|
239
|
+
return { mode, toolIds: normalizeToolIds(input.toolIds), updatedAt };
|
|
240
|
+
}
|
|
241
|
+
function normalizeToolPolicyForUpdate(input, now = Date.now()) {
|
|
242
|
+
return normalizeToolPolicy(input, now) ?? { mode: "all", toolIds: [], updatedAt: now };
|
|
243
|
+
}
|
|
244
|
+
function isToolAllowed(policy, toolName, serverId) {
|
|
245
|
+
if (!policy || policy.mode === "all") return true;
|
|
246
|
+
if (!serverId) return false;
|
|
247
|
+
const toolId = createToolId(serverId, toolName);
|
|
248
|
+
if (policy.mode === "allowlist") {
|
|
249
|
+
return policy.toolIds.includes(toolId);
|
|
250
|
+
}
|
|
251
|
+
return !policy.toolIds.includes(toolId);
|
|
252
|
+
}
|
|
253
|
+
function assertToolAllowed(policy, toolName, serverId) {
|
|
254
|
+
if (isToolAllowed(policy, toolName, serverId)) return;
|
|
255
|
+
throw new Error(`Tool "${toolName}" is not allowed for this MCP session`);
|
|
256
|
+
}
|
|
257
|
+
function filterToolsByPolicy(tools, policy, serverId) {
|
|
258
|
+
if (!policy || policy.mode === "all") return tools;
|
|
259
|
+
return tools.filter((tool) => isToolAllowed(policy, tool.name, serverId));
|
|
260
|
+
}
|
|
261
|
+
function validateToolPolicyAgainstTools(policy, tools, serverId) {
|
|
262
|
+
if (policy.mode === "all") return;
|
|
263
|
+
if (!serverId) {
|
|
264
|
+
throw new Error("Cannot validate MCP tool policy without a serverId");
|
|
265
|
+
}
|
|
266
|
+
const availableIds = new Set(tools.map((tool) => createToolId(serverId, tool.name)));
|
|
267
|
+
const unknownIds = policy.toolIds.filter((id) => !availableIds.has(id));
|
|
268
|
+
if (unknownIds.length > 0) {
|
|
269
|
+
throw new Error(`Unknown tool id(s) for this MCP session: ${unknownIds.join(", ")}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
219
273
|
// src/server/storage/session-lifecycle.ts
|
|
220
274
|
function resolveSessionExpiresAt(status = "pending", referenceTime = Date.now()) {
|
|
221
275
|
return status === "active" ? null : referenceTime + STATE_EXPIRATION_MS;
|
|
@@ -232,7 +286,8 @@ function normalizeNewSession(session, now = Date.now()) {
|
|
|
232
286
|
status,
|
|
233
287
|
createdAt,
|
|
234
288
|
updatedAt,
|
|
235
|
-
expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt)
|
|
289
|
+
expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt),
|
|
290
|
+
toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
|
|
236
291
|
};
|
|
237
292
|
}
|
|
238
293
|
function mergeSessionUpdate(current, data, now = Date.now()) {
|
|
@@ -246,7 +301,8 @@ function mergeSessionUpdate(current, data, now = Date.now()) {
|
|
|
246
301
|
return {
|
|
247
302
|
...updated,
|
|
248
303
|
status,
|
|
249
|
-
expiresAt: resolveSessionExpiresAt(status, updatedAt)
|
|
304
|
+
expiresAt: resolveSessionExpiresAt(status, updatedAt),
|
|
305
|
+
toolPolicy: normalizeToolPolicy(updated.toolPolicy, updatedAt)
|
|
250
306
|
};
|
|
251
307
|
}
|
|
252
308
|
function normalizeStoredSession(session) {
|
|
@@ -259,7 +315,8 @@ function normalizeStoredSession(session) {
|
|
|
259
315
|
status,
|
|
260
316
|
createdAt,
|
|
261
317
|
updatedAt,
|
|
262
|
-
expiresAt
|
|
318
|
+
expiresAt,
|
|
319
|
+
toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
|
|
263
320
|
};
|
|
264
321
|
}
|
|
265
322
|
function isSessionExpired(session, now = Date.now()) {
|
|
@@ -1130,7 +1187,8 @@ var SupabaseStorageBackend = class {
|
|
|
1130
1187
|
userId: row.user_id,
|
|
1131
1188
|
headers: decryptObject(row.headers),
|
|
1132
1189
|
authUrl: row.auth_url,
|
|
1133
|
-
status: row.status ?? "pending"
|
|
1190
|
+
status: row.status ?? "pending",
|
|
1191
|
+
toolPolicy: normalizeToolPolicy(row.tool_policy)
|
|
1134
1192
|
};
|
|
1135
1193
|
}
|
|
1136
1194
|
mapRowToCredentials(row, userId, sessionId) {
|
|
@@ -1154,7 +1212,7 @@ var SupabaseStorageBackend = class {
|
|
|
1154
1212
|
const createdAt = new Date(session.createdAt || Date.now()).toISOString();
|
|
1155
1213
|
const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
|
|
1156
1214
|
const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
|
|
1157
|
-
const
|
|
1215
|
+
const insertData = {
|
|
1158
1216
|
session_id: sessionId,
|
|
1159
1217
|
user_id: userId,
|
|
1160
1218
|
server_id: session.serverId,
|
|
@@ -1168,7 +1226,12 @@ var SupabaseStorageBackend = class {
|
|
|
1168
1226
|
auth_url: session.authUrl ?? null,
|
|
1169
1227
|
status,
|
|
1170
1228
|
expires_at: expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1171
|
-
}
|
|
1229
|
+
};
|
|
1230
|
+
const toolPolicy = normalizeToolPolicy(session.toolPolicy);
|
|
1231
|
+
if (toolPolicy) {
|
|
1232
|
+
insertData.tool_policy = toolPolicy;
|
|
1233
|
+
}
|
|
1234
|
+
const { error } = await this.supabase.from("mcp_sessions").insert(insertData);
|
|
1172
1235
|
if (error) {
|
|
1173
1236
|
if (error.code === "23505") {
|
|
1174
1237
|
throw new Error(`Session ${sessionId} already exists`);
|
|
@@ -1193,6 +1256,7 @@ var SupabaseStorageBackend = class {
|
|
|
1193
1256
|
}
|
|
1194
1257
|
if ("headers" in data) updateData.headers = encryptObject(data.headers);
|
|
1195
1258
|
if ("authUrl" in data) updateData.auth_url = data.authUrl ?? null;
|
|
1259
|
+
if ("toolPolicy" in data) updateData.tool_policy = normalizeToolPolicy(data.toolPolicy);
|
|
1196
1260
|
const shouldUpdateSession = Object.keys(updateData).some((key) => key !== "updated_at");
|
|
1197
1261
|
let updatedRows = null;
|
|
1198
1262
|
if (shouldUpdateSession) {
|
|
@@ -1366,7 +1430,8 @@ var NeonStorageBackend = class {
|
|
|
1366
1430
|
userId: row.user_id,
|
|
1367
1431
|
headers: decryptObject(row.headers),
|
|
1368
1432
|
authUrl: row.auth_url ?? void 0,
|
|
1369
|
-
status: row.status ?? "pending"
|
|
1433
|
+
status: row.status ?? "pending",
|
|
1434
|
+
toolPolicy: normalizeToolPolicy(row.tool_policy)
|
|
1370
1435
|
};
|
|
1371
1436
|
}
|
|
1372
1437
|
mapRowToCredentials(row, userId, sessionId) {
|
|
@@ -1389,42 +1454,48 @@ var NeonStorageBackend = class {
|
|
|
1389
1454
|
const status = session.status ?? "pending";
|
|
1390
1455
|
const createdAt = new Date(session.createdAt || Date.now()).toISOString();
|
|
1391
1456
|
const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
|
|
1392
|
-
const
|
|
1457
|
+
const createdAtMs = new Date(createdAt).getTime();
|
|
1458
|
+
const expiresAt = resolveSessionExpiresAt(status, createdAtMs);
|
|
1459
|
+
const toolPolicy = normalizeToolPolicy(session.toolPolicy, createdAtMs);
|
|
1460
|
+
const columns = [
|
|
1461
|
+
"session_id",
|
|
1462
|
+
"user_id",
|
|
1463
|
+
"server_id",
|
|
1464
|
+
"server_name",
|
|
1465
|
+
"server_url",
|
|
1466
|
+
"transport_type",
|
|
1467
|
+
"callback_url",
|
|
1468
|
+
"created_at",
|
|
1469
|
+
"updated_at",
|
|
1470
|
+
"headers",
|
|
1471
|
+
"auth_url",
|
|
1472
|
+
"status",
|
|
1473
|
+
"expires_at"
|
|
1474
|
+
];
|
|
1475
|
+
const values = [
|
|
1476
|
+
sessionId,
|
|
1477
|
+
userId,
|
|
1478
|
+
session.serverId,
|
|
1479
|
+
session.serverName,
|
|
1480
|
+
session.serverUrl,
|
|
1481
|
+
session.transportType,
|
|
1482
|
+
session.callbackUrl,
|
|
1483
|
+
createdAt,
|
|
1484
|
+
updatedAt,
|
|
1485
|
+
encryptObject(session.headers),
|
|
1486
|
+
session.authUrl ?? null,
|
|
1487
|
+
status,
|
|
1488
|
+
expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1489
|
+
];
|
|
1490
|
+
if (toolPolicy) {
|
|
1491
|
+
columns.push("tool_policy");
|
|
1492
|
+
values.push(toolPolicy);
|
|
1493
|
+
}
|
|
1494
|
+
const placeholders = values.map((_, i) => `$${i + 1}`);
|
|
1393
1495
|
try {
|
|
1394
1496
|
await this.sql.query(
|
|
1395
|
-
`INSERT INTO ${this.tableName} (
|
|
1396
|
-
|
|
1397
|
-
user_id,
|
|
1398
|
-
server_id,
|
|
1399
|
-
server_name,
|
|
1400
|
-
server_url,
|
|
1401
|
-
transport_type,
|
|
1402
|
-
callback_url,
|
|
1403
|
-
created_at,
|
|
1404
|
-
updated_at,
|
|
1405
|
-
headers,
|
|
1406
|
-
auth_url,
|
|
1407
|
-
status,
|
|
1408
|
-
expires_at
|
|
1409
|
-
) VALUES (
|
|
1410
|
-
$1, $2, $3, $4, $5, $6, $7, $8,
|
|
1411
|
-
$9, $10, $11, $12, $13
|
|
1412
|
-
)`,
|
|
1413
|
-
[
|
|
1414
|
-
sessionId,
|
|
1415
|
-
userId,
|
|
1416
|
-
session.serverId,
|
|
1417
|
-
session.serverName,
|
|
1418
|
-
session.serverUrl,
|
|
1419
|
-
session.transportType,
|
|
1420
|
-
session.callbackUrl,
|
|
1421
|
-
createdAt,
|
|
1422
|
-
updatedAt,
|
|
1423
|
-
encryptObject(session.headers),
|
|
1424
|
-
session.authUrl ?? null,
|
|
1425
|
-
status,
|
|
1426
|
-
expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1427
|
-
]
|
|
1497
|
+
`INSERT INTO ${this.tableName} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`,
|
|
1498
|
+
values
|
|
1428
1499
|
);
|
|
1429
1500
|
} catch (error) {
|
|
1430
1501
|
if (error.code === "23505") {
|
|
@@ -1441,36 +1512,36 @@ var NeonStorageBackend = class {
|
|
|
1441
1512
|
const updatedSession = { ...currentSession, ...data };
|
|
1442
1513
|
const status = updatedSession.status ?? "pending";
|
|
1443
1514
|
const expiresAt = resolveSessionExpiresAt(status);
|
|
1444
|
-
const shouldUpdateSession = "serverId" in data || "serverName" in data || "serverUrl" in data || "transportType" in data || "callbackUrl" in data || "status" in data || "headers" in data || "authUrl" in data;
|
|
1515
|
+
const shouldUpdateSession = "serverId" in data || "serverName" in data || "serverUrl" in data || "transportType" in data || "callbackUrl" in data || "status" in data || "headers" in data || "authUrl" in data || "toolPolicy" in data;
|
|
1445
1516
|
if (shouldUpdateSession) {
|
|
1517
|
+
const setClauses = [];
|
|
1518
|
+
const values = [];
|
|
1519
|
+
let paramIndex = 1;
|
|
1520
|
+
const addSet = (column, value) => {
|
|
1521
|
+
setClauses.push(`${column} = $${paramIndex++}`);
|
|
1522
|
+
values.push(value);
|
|
1523
|
+
};
|
|
1524
|
+
addSet("server_id", updatedSession.serverId);
|
|
1525
|
+
addSet("server_name", updatedSession.serverName);
|
|
1526
|
+
addSet("server_url", updatedSession.serverUrl);
|
|
1527
|
+
addSet("transport_type", updatedSession.transportType);
|
|
1528
|
+
addSet("callback_url", updatedSession.callbackUrl);
|
|
1529
|
+
addSet("status", status);
|
|
1530
|
+
addSet("headers", encryptObject(updatedSession.headers));
|
|
1531
|
+
addSet("auth_url", updatedSession.authUrl ?? null);
|
|
1532
|
+
addSet("expires_at", expiresAt === null ? null : new Date(expiresAt).toISOString());
|
|
1533
|
+
if ("toolPolicy" in data) {
|
|
1534
|
+
const policyUpdatedAt = updatedSession.updatedAt ?? Date.now();
|
|
1535
|
+
const toolPolicy = normalizeToolPolicy(updatedSession.toolPolicy, policyUpdatedAt) ?? { mode: "all", toolIds: [], updatedAt: policyUpdatedAt };
|
|
1536
|
+
addSet("tool_policy", toolPolicy);
|
|
1537
|
+
}
|
|
1538
|
+
setClauses.push("updated_at = now()");
|
|
1446
1539
|
const updatedRows = await this.sql.query(
|
|
1447
1540
|
`UPDATE ${this.tableName}
|
|
1448
|
-
SET
|
|
1449
|
-
|
|
1450
|
-
server_name = $2,
|
|
1451
|
-
server_url = $3,
|
|
1452
|
-
transport_type = $4,
|
|
1453
|
-
callback_url = $5,
|
|
1454
|
-
status = $6,
|
|
1455
|
-
headers = $7,
|
|
1456
|
-
auth_url = $8,
|
|
1457
|
-
expires_at = $9,
|
|
1458
|
-
updated_at = now()
|
|
1459
|
-
WHERE user_id = $10 AND session_id = $11
|
|
1541
|
+
SET ${setClauses.join(", ")}
|
|
1542
|
+
WHERE user_id = $${paramIndex++} AND session_id = $${paramIndex++}
|
|
1460
1543
|
RETURNING id`,
|
|
1461
|
-
[
|
|
1462
|
-
updatedSession.serverId,
|
|
1463
|
-
updatedSession.serverName,
|
|
1464
|
-
updatedSession.serverUrl,
|
|
1465
|
-
updatedSession.transportType,
|
|
1466
|
-
updatedSession.callbackUrl,
|
|
1467
|
-
status,
|
|
1468
|
-
encryptObject(updatedSession.headers),
|
|
1469
|
-
updatedSession.authUrl ?? null,
|
|
1470
|
-
expiresAt === null ? null : new Date(expiresAt).toISOString(),
|
|
1471
|
-
userId,
|
|
1472
|
-
sessionId
|
|
1473
|
-
]
|
|
1544
|
+
[...values, userId, sessionId]
|
|
1474
1545
|
);
|
|
1475
1546
|
if (updatedRows.length === 0) {
|
|
1476
1547
|
throw new Error(`Session ${sessionId} not found for userId ${userId}`);
|
|
@@ -2757,6 +2828,19 @@ var MCPClient = class {
|
|
|
2757
2828
|
throw lastError;
|
|
2758
2829
|
}
|
|
2759
2830
|
}
|
|
2831
|
+
/**
|
|
2832
|
+
* Lists all available tools from the connected MCP server without emitting discovery events.
|
|
2833
|
+
* Gateways use this to apply policy before publishing tools to agents or UI state.
|
|
2834
|
+
*/
|
|
2835
|
+
async fetchTools() {
|
|
2836
|
+
const request = {
|
|
2837
|
+
method: "tools/list",
|
|
2838
|
+
params: {}
|
|
2839
|
+
};
|
|
2840
|
+
return await this.withRetry(
|
|
2841
|
+
() => this.client.request(request, types_js.ListToolsResultSchema)
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2760
2844
|
/**
|
|
2761
2845
|
* Lists all available tools from the connected MCP server
|
|
2762
2846
|
* @returns List of tools with their schemas and descriptions
|
|
@@ -2765,13 +2849,7 @@ var MCPClient = class {
|
|
|
2765
2849
|
async listTools() {
|
|
2766
2850
|
this.emitStateChange("DISCOVERING");
|
|
2767
2851
|
try {
|
|
2768
|
-
const
|
|
2769
|
-
method: "tools/list",
|
|
2770
|
-
params: {}
|
|
2771
|
-
};
|
|
2772
|
-
const result = await this.withRetry(
|
|
2773
|
-
() => this.client.request(request, types_js.ListToolsResultSchema)
|
|
2774
|
-
);
|
|
2852
|
+
const result = await this.fetchTools();
|
|
2775
2853
|
if (this.serverId) {
|
|
2776
2854
|
this._onConnectionEvent.fire({
|
|
2777
2855
|
type: "tools_discovered",
|
|
@@ -3100,6 +3178,69 @@ var MCPClient = class {
|
|
|
3100
3178
|
|
|
3101
3179
|
// src/server/mcp/multi-session-client.ts
|
|
3102
3180
|
init_cjs_shims();
|
|
3181
|
+
|
|
3182
|
+
// src/server/mcp/tool-policy-gateway.ts
|
|
3183
|
+
init_cjs_shims();
|
|
3184
|
+
var ToolPolicyGateway = class {
|
|
3185
|
+
constructor(userId, sessionId, client) {
|
|
3186
|
+
this.userId = userId;
|
|
3187
|
+
this.sessionId = sessionId;
|
|
3188
|
+
this.client = client;
|
|
3189
|
+
}
|
|
3190
|
+
isConnected() {
|
|
3191
|
+
return this.client.isConnected();
|
|
3192
|
+
}
|
|
3193
|
+
getServerId() {
|
|
3194
|
+
return this.client.getServerId?.();
|
|
3195
|
+
}
|
|
3196
|
+
getServerName() {
|
|
3197
|
+
return this.client.getServerName?.();
|
|
3198
|
+
}
|
|
3199
|
+
getServerUrl() {
|
|
3200
|
+
return this.client.getServerUrl?.();
|
|
3201
|
+
}
|
|
3202
|
+
getSessionId() {
|
|
3203
|
+
return this.client.getSessionId?.() ?? this.sessionId;
|
|
3204
|
+
}
|
|
3205
|
+
async listTools() {
|
|
3206
|
+
const session = await this.getSession();
|
|
3207
|
+
const result = await this.client.fetchTools();
|
|
3208
|
+
const tools = this.filterTools(session, result.tools);
|
|
3209
|
+
return {
|
|
3210
|
+
...result,
|
|
3211
|
+
tools
|
|
3212
|
+
};
|
|
3213
|
+
}
|
|
3214
|
+
async listAllTools() {
|
|
3215
|
+
return await this.client.fetchTools();
|
|
3216
|
+
}
|
|
3217
|
+
async callTool(name, args) {
|
|
3218
|
+
const session = await this.getSession();
|
|
3219
|
+
this.assertAllowed(session, name);
|
|
3220
|
+
return await this.client.callTool(name, args);
|
|
3221
|
+
}
|
|
3222
|
+
filterTools(session, tools) {
|
|
3223
|
+
return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
|
|
3224
|
+
}
|
|
3225
|
+
assertAllowed(session, toolName) {
|
|
3226
|
+
assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
|
|
3227
|
+
}
|
|
3228
|
+
async getSession() {
|
|
3229
|
+
const session = await sessions.get(this.userId, this.sessionId);
|
|
3230
|
+
if (!session) {
|
|
3231
|
+
throw new Error("Session not found");
|
|
3232
|
+
}
|
|
3233
|
+
return session;
|
|
3234
|
+
}
|
|
3235
|
+
getPolicyServerId(session) {
|
|
3236
|
+
return this.client.getServerId?.() ?? session.serverId;
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
function createToolPolicyGateway(userId, sessionId, client) {
|
|
3240
|
+
return new ToolPolicyGateway(userId, sessionId, client);
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
// src/server/mcp/multi-session-client.ts
|
|
3103
3244
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
3104
3245
|
var DEFAULT_MAX_RETRIES = 2;
|
|
3105
3246
|
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
@@ -3164,7 +3305,9 @@ var MultiSessionClient = class {
|
|
|
3164
3305
|
* or to route a tool call to the right client by `serverId`.
|
|
3165
3306
|
*/
|
|
3166
3307
|
getClients() {
|
|
3167
|
-
return this.clients
|
|
3308
|
+
return this.clients.map(
|
|
3309
|
+
(client) => createToolPolicyGateway(this.userId, client.getSessionId(), client)
|
|
3310
|
+
);
|
|
3168
3311
|
}
|
|
3169
3312
|
/**
|
|
3170
3313
|
* Removes and disconnects a single session by ID.
|
|
@@ -3403,6 +3546,12 @@ var SSEConnectionManager = class {
|
|
|
3403
3546
|
case "listTools":
|
|
3404
3547
|
result = await this.listTools(request.params);
|
|
3405
3548
|
break;
|
|
3549
|
+
case "setToolPolicy":
|
|
3550
|
+
result = await this.setToolPolicy(request.params);
|
|
3551
|
+
break;
|
|
3552
|
+
case "getToolPolicy":
|
|
3553
|
+
result = await this.getToolPolicy(request.params);
|
|
3554
|
+
break;
|
|
3406
3555
|
case "callTool":
|
|
3407
3556
|
result = await this.callTool(request.params);
|
|
3408
3557
|
break;
|
|
@@ -3459,7 +3608,8 @@ var SSEConnectionManager = class {
|
|
|
3459
3608
|
transport: s.transportType,
|
|
3460
3609
|
createdAt: s.createdAt,
|
|
3461
3610
|
updatedAt: s.updatedAt ?? s.createdAt,
|
|
3462
|
-
status: s.status ?? "pending"
|
|
3611
|
+
status: s.status ?? "pending",
|
|
3612
|
+
toolPolicy: s.toolPolicy
|
|
3463
3613
|
}))
|
|
3464
3614
|
};
|
|
3465
3615
|
}
|
|
@@ -3507,7 +3657,7 @@ var SSEConnectionManager = class {
|
|
|
3507
3657
|
this.sendEvent(event);
|
|
3508
3658
|
});
|
|
3509
3659
|
await client.connect();
|
|
3510
|
-
await
|
|
3660
|
+
await this.listPolicyFilteredTools(sessionId);
|
|
3511
3661
|
return {
|
|
3512
3662
|
sessionId,
|
|
3513
3663
|
success: true
|
|
@@ -3578,22 +3728,98 @@ var SSEConnectionManager = class {
|
|
|
3578
3728
|
this.clients.set(sessionId, client);
|
|
3579
3729
|
return client;
|
|
3580
3730
|
}
|
|
3731
|
+
async listPolicyFilteredTools(sessionId) {
|
|
3732
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3733
|
+
if (!session) {
|
|
3734
|
+
throw new Error("Session not found");
|
|
3735
|
+
}
|
|
3736
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3737
|
+
const gateway = createToolPolicyGateway(this.userId, sessionId, client);
|
|
3738
|
+
const result = await gateway.listTools();
|
|
3739
|
+
this.emitConnectionEvent({
|
|
3740
|
+
type: "tools_discovered",
|
|
3741
|
+
sessionId,
|
|
3742
|
+
serverId: session.serverId ?? "unknown",
|
|
3743
|
+
toolCount: result.tools.length,
|
|
3744
|
+
tools: result.tools,
|
|
3745
|
+
timestamp: Date.now()
|
|
3746
|
+
});
|
|
3747
|
+
return { session, result };
|
|
3748
|
+
}
|
|
3581
3749
|
/**
|
|
3582
3750
|
* List tools from a session
|
|
3583
3751
|
*/
|
|
3584
3752
|
async listTools(params) {
|
|
3585
3753
|
const { sessionId } = params;
|
|
3586
|
-
const
|
|
3587
|
-
const result = await client.listTools();
|
|
3754
|
+
const { result } = await this.listPolicyFilteredTools(sessionId);
|
|
3588
3755
|
return { tools: result.tools };
|
|
3589
3756
|
}
|
|
3757
|
+
/**
|
|
3758
|
+
* Get all raw tools with effective access state for management UI.
|
|
3759
|
+
*/
|
|
3760
|
+
async getToolPolicy(params) {
|
|
3761
|
+
const { sessionId } = params;
|
|
3762
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3763
|
+
if (!session) {
|
|
3764
|
+
throw new Error("Session not found");
|
|
3765
|
+
}
|
|
3766
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3767
|
+
const allTools = await client.fetchTools();
|
|
3768
|
+
const toolPolicy = session.toolPolicy ?? {
|
|
3769
|
+
mode: "all",
|
|
3770
|
+
toolIds: [],
|
|
3771
|
+
updatedAt: session.updatedAt ?? session.createdAt
|
|
3772
|
+
};
|
|
3773
|
+
const serverId = session.serverId ?? "unknown";
|
|
3774
|
+
const tools = allTools.tools.map((tool) => ({
|
|
3775
|
+
...tool,
|
|
3776
|
+
toolId: createToolId(serverId, tool.name),
|
|
3777
|
+
allowed: isToolAllowed(toolPolicy, tool.name, session.serverId)
|
|
3778
|
+
}));
|
|
3779
|
+
return {
|
|
3780
|
+
toolPolicy,
|
|
3781
|
+
tools,
|
|
3782
|
+
toolCount: tools.length,
|
|
3783
|
+
allowedToolCount: tools.filter((tool) => tool.allowed).length
|
|
3784
|
+
};
|
|
3785
|
+
}
|
|
3786
|
+
/**
|
|
3787
|
+
* Update per-session tool access policy.
|
|
3788
|
+
*/
|
|
3789
|
+
async setToolPolicy(params) {
|
|
3790
|
+
const { sessionId } = params;
|
|
3791
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3792
|
+
if (!session) {
|
|
3793
|
+
throw new Error("Session not found");
|
|
3794
|
+
}
|
|
3795
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3796
|
+
const allTools = await client.fetchTools();
|
|
3797
|
+
const toolPolicy = normalizeToolPolicyForUpdate(params.toolPolicy);
|
|
3798
|
+
validateToolPolicyAgainstTools(toolPolicy, allTools.tools, session.serverId);
|
|
3799
|
+
await sessions.update(this.userId, sessionId, { toolPolicy });
|
|
3800
|
+
const filteredTools = createToolPolicyGateway(this.userId, sessionId, client).filterTools({ ...session, toolPolicy }, allTools.tools);
|
|
3801
|
+
this.emitConnectionEvent({
|
|
3802
|
+
type: "tools_discovered",
|
|
3803
|
+
sessionId,
|
|
3804
|
+
serverId: session.serverId ?? "unknown",
|
|
3805
|
+
toolCount: filteredTools.length,
|
|
3806
|
+
tools: filteredTools,
|
|
3807
|
+
timestamp: Date.now()
|
|
3808
|
+
});
|
|
3809
|
+
return {
|
|
3810
|
+
success: true,
|
|
3811
|
+
toolPolicy,
|
|
3812
|
+
tools: filteredTools,
|
|
3813
|
+
toolCount: filteredTools.length
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
3590
3816
|
/**
|
|
3591
3817
|
* Call a tool on the MCP server
|
|
3592
3818
|
*/
|
|
3593
3819
|
async callTool(params) {
|
|
3594
3820
|
const { sessionId, toolName, toolArgs } = params;
|
|
3595
3821
|
const client = await this.getOrCreateClient(sessionId);
|
|
3596
|
-
const result = await client.callTool(toolName, toolArgs);
|
|
3822
|
+
const result = await createToolPolicyGateway(this.userId, sessionId, client).callTool(toolName, toolArgs);
|
|
3597
3823
|
const meta = result._meta || {};
|
|
3598
3824
|
return {
|
|
3599
3825
|
...result,
|
|
@@ -3643,7 +3869,7 @@ var SSEConnectionManager = class {
|
|
|
3643
3869
|
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
3644
3870
|
await client.connect();
|
|
3645
3871
|
this.clients.set(sessionId, client);
|
|
3646
|
-
const tools = await
|
|
3872
|
+
const { result: tools } = await this.listPolicyFilteredTools(sessionId);
|
|
3647
3873
|
return { success: true, toolCount: tools.tools.length };
|
|
3648
3874
|
} catch (error) {
|
|
3649
3875
|
this.emitConnectionEvent({
|
|
@@ -3691,7 +3917,7 @@ var SSEConnectionManager = class {
|
|
|
3691
3917
|
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
3692
3918
|
await client.finishAuth(code, oauthState);
|
|
3693
3919
|
this.clients.set(sessionId, client);
|
|
3694
|
-
const tools = await
|
|
3920
|
+
const { result: tools } = await this.listPolicyFilteredTools(sessionId);
|
|
3695
3921
|
return { success: true, toolCount: tools.tools.length };
|
|
3696
3922
|
} catch (error) {
|
|
3697
3923
|
this.emitConnectionEvent({
|
|
@@ -3990,6 +4216,12 @@ var SSEClient = class {
|
|
|
3990
4216
|
async disconnectFromServer(sessionId) {
|
|
3991
4217
|
return this.sendRequest("disconnect", { sessionId });
|
|
3992
4218
|
}
|
|
4219
|
+
async setToolPolicy(sessionId, toolPolicy) {
|
|
4220
|
+
return this.sendRequest("setToolPolicy", { sessionId, toolPolicy });
|
|
4221
|
+
}
|
|
4222
|
+
async getToolPolicy(sessionId) {
|
|
4223
|
+
return this.sendRequest("getToolPolicy", { sessionId });
|
|
4224
|
+
}
|
|
3993
4225
|
async listTools(sessionId) {
|
|
3994
4226
|
return this.sendRequest("listTools", { sessionId });
|
|
3995
4227
|
}
|