@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/server/index.js
CHANGED
|
@@ -209,6 +209,60 @@ var SOFTWARE_VERSION = "2.3.4";
|
|
|
209
209
|
var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
|
|
210
210
|
var MCP_CLIENT_VERSION = "2.0";
|
|
211
211
|
|
|
212
|
+
// src/server/storage/tool-policy.ts
|
|
213
|
+
init_cjs_shims();
|
|
214
|
+
function createToolId(serverId, toolName) {
|
|
215
|
+
return `${serverId}::${toolName}`;
|
|
216
|
+
}
|
|
217
|
+
function normalizeToolIds(input) {
|
|
218
|
+
if (!Array.isArray(input)) return [];
|
|
219
|
+
return Array.from(new Set(
|
|
220
|
+
input.filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean)
|
|
221
|
+
));
|
|
222
|
+
}
|
|
223
|
+
function normalizeToolPolicy(input, now = Date.now()) {
|
|
224
|
+
if (!input || typeof input !== "object") {
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
const mode = input.mode === "allowlist" || input.mode === "denylist" ? input.mode : "all";
|
|
228
|
+
const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : now;
|
|
229
|
+
if (mode === "all") {
|
|
230
|
+
return { mode: "all", toolIds: [], updatedAt };
|
|
231
|
+
}
|
|
232
|
+
return { mode, toolIds: normalizeToolIds(input.toolIds), updatedAt };
|
|
233
|
+
}
|
|
234
|
+
function normalizeToolPolicyForUpdate(input, now = Date.now()) {
|
|
235
|
+
return normalizeToolPolicy(input, now) ?? { mode: "all", toolIds: [], updatedAt: now };
|
|
236
|
+
}
|
|
237
|
+
function isToolAllowed(policy, toolName, serverId) {
|
|
238
|
+
if (!policy || policy.mode === "all") return true;
|
|
239
|
+
if (!serverId) return false;
|
|
240
|
+
const toolId = createToolId(serverId, toolName);
|
|
241
|
+
if (policy.mode === "allowlist") {
|
|
242
|
+
return policy.toolIds.includes(toolId);
|
|
243
|
+
}
|
|
244
|
+
return !policy.toolIds.includes(toolId);
|
|
245
|
+
}
|
|
246
|
+
function assertToolAllowed(policy, toolName, serverId) {
|
|
247
|
+
if (isToolAllowed(policy, toolName, serverId)) return;
|
|
248
|
+
throw new Error(`Tool "${toolName}" is not allowed for this MCP session`);
|
|
249
|
+
}
|
|
250
|
+
function filterToolsByPolicy(tools, policy, serverId) {
|
|
251
|
+
if (!policy || policy.mode === "all") return tools;
|
|
252
|
+
return tools.filter((tool) => isToolAllowed(policy, tool.name, serverId));
|
|
253
|
+
}
|
|
254
|
+
function validateToolPolicyAgainstTools(policy, tools, serverId) {
|
|
255
|
+
if (policy.mode === "all") return;
|
|
256
|
+
if (!serverId) {
|
|
257
|
+
throw new Error("Cannot validate MCP tool policy without a serverId");
|
|
258
|
+
}
|
|
259
|
+
const availableIds = new Set(tools.map((tool) => createToolId(serverId, tool.name)));
|
|
260
|
+
const unknownIds = policy.toolIds.filter((id) => !availableIds.has(id));
|
|
261
|
+
if (unknownIds.length > 0) {
|
|
262
|
+
throw new Error(`Unknown tool id(s) for this MCP session: ${unknownIds.join(", ")}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
212
266
|
// src/server/storage/session-lifecycle.ts
|
|
213
267
|
function resolveSessionExpiresAt(status = "pending", referenceTime = Date.now()) {
|
|
214
268
|
return status === "active" ? null : referenceTime + STATE_EXPIRATION_MS;
|
|
@@ -225,7 +279,8 @@ function normalizeNewSession(session, now = Date.now()) {
|
|
|
225
279
|
status,
|
|
226
280
|
createdAt,
|
|
227
281
|
updatedAt,
|
|
228
|
-
expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt)
|
|
282
|
+
expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt),
|
|
283
|
+
toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
|
|
229
284
|
};
|
|
230
285
|
}
|
|
231
286
|
function mergeSessionUpdate(current, data, now = Date.now()) {
|
|
@@ -239,7 +294,8 @@ function mergeSessionUpdate(current, data, now = Date.now()) {
|
|
|
239
294
|
return {
|
|
240
295
|
...updated,
|
|
241
296
|
status,
|
|
242
|
-
expiresAt: resolveSessionExpiresAt(status, updatedAt)
|
|
297
|
+
expiresAt: resolveSessionExpiresAt(status, updatedAt),
|
|
298
|
+
toolPolicy: normalizeToolPolicy(updated.toolPolicy, updatedAt)
|
|
243
299
|
};
|
|
244
300
|
}
|
|
245
301
|
function normalizeStoredSession(session) {
|
|
@@ -252,7 +308,8 @@ function normalizeStoredSession(session) {
|
|
|
252
308
|
status,
|
|
253
309
|
createdAt,
|
|
254
310
|
updatedAt,
|
|
255
|
-
expiresAt
|
|
311
|
+
expiresAt,
|
|
312
|
+
toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
|
|
256
313
|
};
|
|
257
314
|
}
|
|
258
315
|
function isSessionExpired(session, now = Date.now()) {
|
|
@@ -1123,7 +1180,8 @@ var SupabaseStorageBackend = class {
|
|
|
1123
1180
|
userId: row.user_id,
|
|
1124
1181
|
headers: decryptObject(row.headers),
|
|
1125
1182
|
authUrl: row.auth_url,
|
|
1126
|
-
status: row.status ?? "pending"
|
|
1183
|
+
status: row.status ?? "pending",
|
|
1184
|
+
toolPolicy: normalizeToolPolicy(row.tool_policy)
|
|
1127
1185
|
};
|
|
1128
1186
|
}
|
|
1129
1187
|
mapRowToCredentials(row, userId, sessionId) {
|
|
@@ -1147,7 +1205,7 @@ var SupabaseStorageBackend = class {
|
|
|
1147
1205
|
const createdAt = new Date(session.createdAt || Date.now()).toISOString();
|
|
1148
1206
|
const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
|
|
1149
1207
|
const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
|
|
1150
|
-
const
|
|
1208
|
+
const insertData = {
|
|
1151
1209
|
session_id: sessionId,
|
|
1152
1210
|
user_id: userId,
|
|
1153
1211
|
server_id: session.serverId,
|
|
@@ -1161,7 +1219,12 @@ var SupabaseStorageBackend = class {
|
|
|
1161
1219
|
auth_url: session.authUrl ?? null,
|
|
1162
1220
|
status,
|
|
1163
1221
|
expires_at: expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1164
|
-
}
|
|
1222
|
+
};
|
|
1223
|
+
const toolPolicy = normalizeToolPolicy(session.toolPolicy);
|
|
1224
|
+
if (toolPolicy) {
|
|
1225
|
+
insertData.tool_policy = toolPolicy;
|
|
1226
|
+
}
|
|
1227
|
+
const { error } = await this.supabase.from("mcp_sessions").insert(insertData);
|
|
1165
1228
|
if (error) {
|
|
1166
1229
|
if (error.code === "23505") {
|
|
1167
1230
|
throw new Error(`Session ${sessionId} already exists`);
|
|
@@ -1186,6 +1249,7 @@ var SupabaseStorageBackend = class {
|
|
|
1186
1249
|
}
|
|
1187
1250
|
if ("headers" in data) updateData.headers = encryptObject(data.headers);
|
|
1188
1251
|
if ("authUrl" in data) updateData.auth_url = data.authUrl ?? null;
|
|
1252
|
+
if ("toolPolicy" in data) updateData.tool_policy = normalizeToolPolicy(data.toolPolicy);
|
|
1189
1253
|
const shouldUpdateSession = Object.keys(updateData).some((key) => key !== "updated_at");
|
|
1190
1254
|
let updatedRows = null;
|
|
1191
1255
|
if (shouldUpdateSession) {
|
|
@@ -1359,7 +1423,8 @@ var NeonStorageBackend = class {
|
|
|
1359
1423
|
userId: row.user_id,
|
|
1360
1424
|
headers: decryptObject(row.headers),
|
|
1361
1425
|
authUrl: row.auth_url ?? void 0,
|
|
1362
|
-
status: row.status ?? "pending"
|
|
1426
|
+
status: row.status ?? "pending",
|
|
1427
|
+
toolPolicy: normalizeToolPolicy(row.tool_policy)
|
|
1363
1428
|
};
|
|
1364
1429
|
}
|
|
1365
1430
|
mapRowToCredentials(row, userId, sessionId) {
|
|
@@ -1382,42 +1447,48 @@ var NeonStorageBackend = class {
|
|
|
1382
1447
|
const status = session.status ?? "pending";
|
|
1383
1448
|
const createdAt = new Date(session.createdAt || Date.now()).toISOString();
|
|
1384
1449
|
const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
|
|
1385
|
-
const
|
|
1450
|
+
const createdAtMs = new Date(createdAt).getTime();
|
|
1451
|
+
const expiresAt = resolveSessionExpiresAt(status, createdAtMs);
|
|
1452
|
+
const toolPolicy = normalizeToolPolicy(session.toolPolicy, createdAtMs);
|
|
1453
|
+
const columns = [
|
|
1454
|
+
"session_id",
|
|
1455
|
+
"user_id",
|
|
1456
|
+
"server_id",
|
|
1457
|
+
"server_name",
|
|
1458
|
+
"server_url",
|
|
1459
|
+
"transport_type",
|
|
1460
|
+
"callback_url",
|
|
1461
|
+
"created_at",
|
|
1462
|
+
"updated_at",
|
|
1463
|
+
"headers",
|
|
1464
|
+
"auth_url",
|
|
1465
|
+
"status",
|
|
1466
|
+
"expires_at"
|
|
1467
|
+
];
|
|
1468
|
+
const values = [
|
|
1469
|
+
sessionId,
|
|
1470
|
+
userId,
|
|
1471
|
+
session.serverId,
|
|
1472
|
+
session.serverName,
|
|
1473
|
+
session.serverUrl,
|
|
1474
|
+
session.transportType,
|
|
1475
|
+
session.callbackUrl,
|
|
1476
|
+
createdAt,
|
|
1477
|
+
updatedAt,
|
|
1478
|
+
encryptObject(session.headers),
|
|
1479
|
+
session.authUrl ?? null,
|
|
1480
|
+
status,
|
|
1481
|
+
expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1482
|
+
];
|
|
1483
|
+
if (toolPolicy) {
|
|
1484
|
+
columns.push("tool_policy");
|
|
1485
|
+
values.push(toolPolicy);
|
|
1486
|
+
}
|
|
1487
|
+
const placeholders = values.map((_, i) => `$${i + 1}`);
|
|
1386
1488
|
try {
|
|
1387
1489
|
await this.sql.query(
|
|
1388
|
-
`INSERT INTO ${this.tableName} (
|
|
1389
|
-
|
|
1390
|
-
user_id,
|
|
1391
|
-
server_id,
|
|
1392
|
-
server_name,
|
|
1393
|
-
server_url,
|
|
1394
|
-
transport_type,
|
|
1395
|
-
callback_url,
|
|
1396
|
-
created_at,
|
|
1397
|
-
updated_at,
|
|
1398
|
-
headers,
|
|
1399
|
-
auth_url,
|
|
1400
|
-
status,
|
|
1401
|
-
expires_at
|
|
1402
|
-
) VALUES (
|
|
1403
|
-
$1, $2, $3, $4, $5, $6, $7, $8,
|
|
1404
|
-
$9, $10, $11, $12, $13
|
|
1405
|
-
)`,
|
|
1406
|
-
[
|
|
1407
|
-
sessionId,
|
|
1408
|
-
userId,
|
|
1409
|
-
session.serverId,
|
|
1410
|
-
session.serverName,
|
|
1411
|
-
session.serverUrl,
|
|
1412
|
-
session.transportType,
|
|
1413
|
-
session.callbackUrl,
|
|
1414
|
-
createdAt,
|
|
1415
|
-
updatedAt,
|
|
1416
|
-
encryptObject(session.headers),
|
|
1417
|
-
session.authUrl ?? null,
|
|
1418
|
-
status,
|
|
1419
|
-
expiresAt === null ? null : new Date(expiresAt).toISOString()
|
|
1420
|
-
]
|
|
1490
|
+
`INSERT INTO ${this.tableName} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`,
|
|
1491
|
+
values
|
|
1421
1492
|
);
|
|
1422
1493
|
} catch (error) {
|
|
1423
1494
|
if (error.code === "23505") {
|
|
@@ -1434,36 +1505,36 @@ var NeonStorageBackend = class {
|
|
|
1434
1505
|
const updatedSession = { ...currentSession, ...data };
|
|
1435
1506
|
const status = updatedSession.status ?? "pending";
|
|
1436
1507
|
const expiresAt = resolveSessionExpiresAt(status);
|
|
1437
|
-
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;
|
|
1508
|
+
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;
|
|
1438
1509
|
if (shouldUpdateSession) {
|
|
1510
|
+
const setClauses = [];
|
|
1511
|
+
const values = [];
|
|
1512
|
+
let paramIndex = 1;
|
|
1513
|
+
const addSet = (column, value) => {
|
|
1514
|
+
setClauses.push(`${column} = $${paramIndex++}`);
|
|
1515
|
+
values.push(value);
|
|
1516
|
+
};
|
|
1517
|
+
addSet("server_id", updatedSession.serverId);
|
|
1518
|
+
addSet("server_name", updatedSession.serverName);
|
|
1519
|
+
addSet("server_url", updatedSession.serverUrl);
|
|
1520
|
+
addSet("transport_type", updatedSession.transportType);
|
|
1521
|
+
addSet("callback_url", updatedSession.callbackUrl);
|
|
1522
|
+
addSet("status", status);
|
|
1523
|
+
addSet("headers", encryptObject(updatedSession.headers));
|
|
1524
|
+
addSet("auth_url", updatedSession.authUrl ?? null);
|
|
1525
|
+
addSet("expires_at", expiresAt === null ? null : new Date(expiresAt).toISOString());
|
|
1526
|
+
if ("toolPolicy" in data) {
|
|
1527
|
+
const policyUpdatedAt = updatedSession.updatedAt ?? Date.now();
|
|
1528
|
+
const toolPolicy = normalizeToolPolicy(updatedSession.toolPolicy, policyUpdatedAt) ?? { mode: "all", toolIds: [], updatedAt: policyUpdatedAt };
|
|
1529
|
+
addSet("tool_policy", toolPolicy);
|
|
1530
|
+
}
|
|
1531
|
+
setClauses.push("updated_at = now()");
|
|
1439
1532
|
const updatedRows = await this.sql.query(
|
|
1440
1533
|
`UPDATE ${this.tableName}
|
|
1441
|
-
SET
|
|
1442
|
-
|
|
1443
|
-
server_name = $2,
|
|
1444
|
-
server_url = $3,
|
|
1445
|
-
transport_type = $4,
|
|
1446
|
-
callback_url = $5,
|
|
1447
|
-
status = $6,
|
|
1448
|
-
headers = $7,
|
|
1449
|
-
auth_url = $8,
|
|
1450
|
-
expires_at = $9,
|
|
1451
|
-
updated_at = now()
|
|
1452
|
-
WHERE user_id = $10 AND session_id = $11
|
|
1534
|
+
SET ${setClauses.join(", ")}
|
|
1535
|
+
WHERE user_id = $${paramIndex++} AND session_id = $${paramIndex++}
|
|
1453
1536
|
RETURNING id`,
|
|
1454
|
-
[
|
|
1455
|
-
updatedSession.serverId,
|
|
1456
|
-
updatedSession.serverName,
|
|
1457
|
-
updatedSession.serverUrl,
|
|
1458
|
-
updatedSession.transportType,
|
|
1459
|
-
updatedSession.callbackUrl,
|
|
1460
|
-
status,
|
|
1461
|
-
encryptObject(updatedSession.headers),
|
|
1462
|
-
updatedSession.authUrl ?? null,
|
|
1463
|
-
expiresAt === null ? null : new Date(expiresAt).toISOString(),
|
|
1464
|
-
userId,
|
|
1465
|
-
sessionId
|
|
1466
|
-
]
|
|
1537
|
+
[...values, userId, sessionId]
|
|
1467
1538
|
);
|
|
1468
1539
|
if (updatedRows.length === 0) {
|
|
1469
1540
|
throw new Error(`Session ${sessionId} not found for userId ${userId}`);
|
|
@@ -2682,6 +2753,19 @@ var MCPClient = class {
|
|
|
2682
2753
|
throw lastError;
|
|
2683
2754
|
}
|
|
2684
2755
|
}
|
|
2756
|
+
/**
|
|
2757
|
+
* Lists all available tools from the connected MCP server without emitting discovery events.
|
|
2758
|
+
* Gateways use this to apply policy before publishing tools to agents or UI state.
|
|
2759
|
+
*/
|
|
2760
|
+
async fetchTools() {
|
|
2761
|
+
const request = {
|
|
2762
|
+
method: "tools/list",
|
|
2763
|
+
params: {}
|
|
2764
|
+
};
|
|
2765
|
+
return await this.withRetry(
|
|
2766
|
+
() => this.client.request(request, types_js.ListToolsResultSchema)
|
|
2767
|
+
);
|
|
2768
|
+
}
|
|
2685
2769
|
/**
|
|
2686
2770
|
* Lists all available tools from the connected MCP server
|
|
2687
2771
|
* @returns List of tools with their schemas and descriptions
|
|
@@ -2690,13 +2774,7 @@ var MCPClient = class {
|
|
|
2690
2774
|
async listTools() {
|
|
2691
2775
|
this.emitStateChange("DISCOVERING");
|
|
2692
2776
|
try {
|
|
2693
|
-
const
|
|
2694
|
-
method: "tools/list",
|
|
2695
|
-
params: {}
|
|
2696
|
-
};
|
|
2697
|
-
const result = await this.withRetry(
|
|
2698
|
-
() => this.client.request(request, types_js.ListToolsResultSchema)
|
|
2699
|
-
);
|
|
2777
|
+
const result = await this.fetchTools();
|
|
2700
2778
|
if (this.serverId) {
|
|
2701
2779
|
this._onConnectionEvent.fire({
|
|
2702
2780
|
type: "tools_discovered",
|
|
@@ -3025,6 +3103,69 @@ var MCPClient = class {
|
|
|
3025
3103
|
|
|
3026
3104
|
// src/server/mcp/multi-session-client.ts
|
|
3027
3105
|
init_cjs_shims();
|
|
3106
|
+
|
|
3107
|
+
// src/server/mcp/tool-policy-gateway.ts
|
|
3108
|
+
init_cjs_shims();
|
|
3109
|
+
var ToolPolicyGateway = class {
|
|
3110
|
+
constructor(userId, sessionId, client) {
|
|
3111
|
+
this.userId = userId;
|
|
3112
|
+
this.sessionId = sessionId;
|
|
3113
|
+
this.client = client;
|
|
3114
|
+
}
|
|
3115
|
+
isConnected() {
|
|
3116
|
+
return this.client.isConnected();
|
|
3117
|
+
}
|
|
3118
|
+
getServerId() {
|
|
3119
|
+
return this.client.getServerId?.();
|
|
3120
|
+
}
|
|
3121
|
+
getServerName() {
|
|
3122
|
+
return this.client.getServerName?.();
|
|
3123
|
+
}
|
|
3124
|
+
getServerUrl() {
|
|
3125
|
+
return this.client.getServerUrl?.();
|
|
3126
|
+
}
|
|
3127
|
+
getSessionId() {
|
|
3128
|
+
return this.client.getSessionId?.() ?? this.sessionId;
|
|
3129
|
+
}
|
|
3130
|
+
async listTools() {
|
|
3131
|
+
const session = await this.getSession();
|
|
3132
|
+
const result = await this.client.fetchTools();
|
|
3133
|
+
const tools = this.filterTools(session, result.tools);
|
|
3134
|
+
return {
|
|
3135
|
+
...result,
|
|
3136
|
+
tools
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
async listAllTools() {
|
|
3140
|
+
return await this.client.fetchTools();
|
|
3141
|
+
}
|
|
3142
|
+
async callTool(name, args) {
|
|
3143
|
+
const session = await this.getSession();
|
|
3144
|
+
this.assertAllowed(session, name);
|
|
3145
|
+
return await this.client.callTool(name, args);
|
|
3146
|
+
}
|
|
3147
|
+
filterTools(session, tools) {
|
|
3148
|
+
return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
|
|
3149
|
+
}
|
|
3150
|
+
assertAllowed(session, toolName) {
|
|
3151
|
+
assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
|
|
3152
|
+
}
|
|
3153
|
+
async getSession() {
|
|
3154
|
+
const session = await sessions.get(this.userId, this.sessionId);
|
|
3155
|
+
if (!session) {
|
|
3156
|
+
throw new Error("Session not found");
|
|
3157
|
+
}
|
|
3158
|
+
return session;
|
|
3159
|
+
}
|
|
3160
|
+
getPolicyServerId(session) {
|
|
3161
|
+
return this.client.getServerId?.() ?? session.serverId;
|
|
3162
|
+
}
|
|
3163
|
+
};
|
|
3164
|
+
function createToolPolicyGateway(userId, sessionId, client) {
|
|
3165
|
+
return new ToolPolicyGateway(userId, sessionId, client);
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// src/server/mcp/multi-session-client.ts
|
|
3028
3169
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
3029
3170
|
var DEFAULT_MAX_RETRIES = 2;
|
|
3030
3171
|
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
@@ -3089,7 +3230,9 @@ var MultiSessionClient = class {
|
|
|
3089
3230
|
* or to route a tool call to the right client by `serverId`.
|
|
3090
3231
|
*/
|
|
3091
3232
|
getClients() {
|
|
3092
|
-
return this.clients
|
|
3233
|
+
return this.clients.map(
|
|
3234
|
+
(client) => createToolPolicyGateway(this.userId, client.getSessionId(), client)
|
|
3235
|
+
);
|
|
3093
3236
|
}
|
|
3094
3237
|
/**
|
|
3095
3238
|
* Removes and disconnects a single session by ID.
|
|
@@ -3328,6 +3471,12 @@ var SSEConnectionManager = class {
|
|
|
3328
3471
|
case "listTools":
|
|
3329
3472
|
result = await this.listTools(request.params);
|
|
3330
3473
|
break;
|
|
3474
|
+
case "setToolPolicy":
|
|
3475
|
+
result = await this.setToolPolicy(request.params);
|
|
3476
|
+
break;
|
|
3477
|
+
case "getToolPolicy":
|
|
3478
|
+
result = await this.getToolPolicy(request.params);
|
|
3479
|
+
break;
|
|
3331
3480
|
case "callTool":
|
|
3332
3481
|
result = await this.callTool(request.params);
|
|
3333
3482
|
break;
|
|
@@ -3384,7 +3533,8 @@ var SSEConnectionManager = class {
|
|
|
3384
3533
|
transport: s.transportType,
|
|
3385
3534
|
createdAt: s.createdAt,
|
|
3386
3535
|
updatedAt: s.updatedAt ?? s.createdAt,
|
|
3387
|
-
status: s.status ?? "pending"
|
|
3536
|
+
status: s.status ?? "pending",
|
|
3537
|
+
toolPolicy: s.toolPolicy
|
|
3388
3538
|
}))
|
|
3389
3539
|
};
|
|
3390
3540
|
}
|
|
@@ -3432,7 +3582,7 @@ var SSEConnectionManager = class {
|
|
|
3432
3582
|
this.sendEvent(event);
|
|
3433
3583
|
});
|
|
3434
3584
|
await client.connect();
|
|
3435
|
-
await
|
|
3585
|
+
await this.listPolicyFilteredTools(sessionId);
|
|
3436
3586
|
return {
|
|
3437
3587
|
sessionId,
|
|
3438
3588
|
success: true
|
|
@@ -3503,22 +3653,98 @@ var SSEConnectionManager = class {
|
|
|
3503
3653
|
this.clients.set(sessionId, client);
|
|
3504
3654
|
return client;
|
|
3505
3655
|
}
|
|
3656
|
+
async listPolicyFilteredTools(sessionId) {
|
|
3657
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3658
|
+
if (!session) {
|
|
3659
|
+
throw new Error("Session not found");
|
|
3660
|
+
}
|
|
3661
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3662
|
+
const gateway = createToolPolicyGateway(this.userId, sessionId, client);
|
|
3663
|
+
const result = await gateway.listTools();
|
|
3664
|
+
this.emitConnectionEvent({
|
|
3665
|
+
type: "tools_discovered",
|
|
3666
|
+
sessionId,
|
|
3667
|
+
serverId: session.serverId ?? "unknown",
|
|
3668
|
+
toolCount: result.tools.length,
|
|
3669
|
+
tools: result.tools,
|
|
3670
|
+
timestamp: Date.now()
|
|
3671
|
+
});
|
|
3672
|
+
return { session, result };
|
|
3673
|
+
}
|
|
3506
3674
|
/**
|
|
3507
3675
|
* List tools from a session
|
|
3508
3676
|
*/
|
|
3509
3677
|
async listTools(params) {
|
|
3510
3678
|
const { sessionId } = params;
|
|
3511
|
-
const
|
|
3512
|
-
const result = await client.listTools();
|
|
3679
|
+
const { result } = await this.listPolicyFilteredTools(sessionId);
|
|
3513
3680
|
return { tools: result.tools };
|
|
3514
3681
|
}
|
|
3682
|
+
/**
|
|
3683
|
+
* Get all raw tools with effective access state for management UI.
|
|
3684
|
+
*/
|
|
3685
|
+
async getToolPolicy(params) {
|
|
3686
|
+
const { sessionId } = params;
|
|
3687
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3688
|
+
if (!session) {
|
|
3689
|
+
throw new Error("Session not found");
|
|
3690
|
+
}
|
|
3691
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3692
|
+
const allTools = await client.fetchTools();
|
|
3693
|
+
const toolPolicy = session.toolPolicy ?? {
|
|
3694
|
+
mode: "all",
|
|
3695
|
+
toolIds: [],
|
|
3696
|
+
updatedAt: session.updatedAt ?? session.createdAt
|
|
3697
|
+
};
|
|
3698
|
+
const serverId = session.serverId ?? "unknown";
|
|
3699
|
+
const tools = allTools.tools.map((tool) => ({
|
|
3700
|
+
...tool,
|
|
3701
|
+
toolId: createToolId(serverId, tool.name),
|
|
3702
|
+
allowed: isToolAllowed(toolPolicy, tool.name, session.serverId)
|
|
3703
|
+
}));
|
|
3704
|
+
return {
|
|
3705
|
+
toolPolicy,
|
|
3706
|
+
tools,
|
|
3707
|
+
toolCount: tools.length,
|
|
3708
|
+
allowedToolCount: tools.filter((tool) => tool.allowed).length
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
/**
|
|
3712
|
+
* Update per-session tool access policy.
|
|
3713
|
+
*/
|
|
3714
|
+
async setToolPolicy(params) {
|
|
3715
|
+
const { sessionId } = params;
|
|
3716
|
+
const session = await sessions.get(this.userId, sessionId);
|
|
3717
|
+
if (!session) {
|
|
3718
|
+
throw new Error("Session not found");
|
|
3719
|
+
}
|
|
3720
|
+
const client = await this.getOrCreateClient(sessionId);
|
|
3721
|
+
const allTools = await client.fetchTools();
|
|
3722
|
+
const toolPolicy = normalizeToolPolicyForUpdate(params.toolPolicy);
|
|
3723
|
+
validateToolPolicyAgainstTools(toolPolicy, allTools.tools, session.serverId);
|
|
3724
|
+
await sessions.update(this.userId, sessionId, { toolPolicy });
|
|
3725
|
+
const filteredTools = createToolPolicyGateway(this.userId, sessionId, client).filterTools({ ...session, toolPolicy }, allTools.tools);
|
|
3726
|
+
this.emitConnectionEvent({
|
|
3727
|
+
type: "tools_discovered",
|
|
3728
|
+
sessionId,
|
|
3729
|
+
serverId: session.serverId ?? "unknown",
|
|
3730
|
+
toolCount: filteredTools.length,
|
|
3731
|
+
tools: filteredTools,
|
|
3732
|
+
timestamp: Date.now()
|
|
3733
|
+
});
|
|
3734
|
+
return {
|
|
3735
|
+
success: true,
|
|
3736
|
+
toolPolicy,
|
|
3737
|
+
tools: filteredTools,
|
|
3738
|
+
toolCount: filteredTools.length
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3515
3741
|
/**
|
|
3516
3742
|
* Call a tool on the MCP server
|
|
3517
3743
|
*/
|
|
3518
3744
|
async callTool(params) {
|
|
3519
3745
|
const { sessionId, toolName, toolArgs } = params;
|
|
3520
3746
|
const client = await this.getOrCreateClient(sessionId);
|
|
3521
|
-
const result = await client.callTool(toolName, toolArgs);
|
|
3747
|
+
const result = await createToolPolicyGateway(this.userId, sessionId, client).callTool(toolName, toolArgs);
|
|
3522
3748
|
const meta = result._meta || {};
|
|
3523
3749
|
return {
|
|
3524
3750
|
...result,
|
|
@@ -3568,7 +3794,7 @@ var SSEConnectionManager = class {
|
|
|
3568
3794
|
client.onObservabilityEvent((event) => this.sendEvent(event));
|
|
3569
3795
|
await client.connect();
|
|
3570
3796
|
this.clients.set(sessionId, client);
|
|
3571
|
-
const tools = await
|
|
3797
|
+
const { result: tools } = await this.listPolicyFilteredTools(sessionId);
|
|
3572
3798
|
return { success: true, toolCount: tools.tools.length };
|
|
3573
3799
|
} catch (error) {
|
|
3574
3800
|
this.emitConnectionEvent({
|
|
@@ -3616,7 +3842,7 @@ var SSEConnectionManager = class {
|
|
|
3616
3842
|
client.onConnectionEvent((event) => this.emitConnectionEvent(event));
|
|
3617
3843
|
await client.finishAuth(code, oauthState);
|
|
3618
3844
|
this.clients.set(sessionId, client);
|
|
3619
|
-
const tools = await
|
|
3845
|
+
const { result: tools } = await this.listPolicyFilteredTools(sessionId);
|
|
3620
3846
|
return { success: true, toolCount: tools.tools.length };
|
|
3621
3847
|
} catch (error) {
|
|
3622
3848
|
this.emitConnectionEvent({
|