@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.
Files changed (85) hide show
  1. package/dist/adapters/agui-adapter.d.mts +3 -3
  2. package/dist/adapters/agui-adapter.d.ts +3 -3
  3. package/dist/adapters/agui-adapter.js.map +1 -1
  4. package/dist/adapters/agui-adapter.mjs.map +1 -1
  5. package/dist/adapters/agui-middleware.d.mts +3 -3
  6. package/dist/adapters/agui-middleware.d.ts +3 -3
  7. package/dist/adapters/agui-middleware.js.map +1 -1
  8. package/dist/adapters/agui-middleware.mjs.map +1 -1
  9. package/dist/adapters/ai-adapter.d.mts +3 -3
  10. package/dist/adapters/ai-adapter.d.ts +3 -3
  11. package/dist/adapters/ai-adapter.js.map +1 -1
  12. package/dist/adapters/ai-adapter.mjs.map +1 -1
  13. package/dist/adapters/langchain-adapter.d.mts +3 -3
  14. package/dist/adapters/langchain-adapter.d.ts +3 -3
  15. package/dist/adapters/langchain-adapter.js +2 -2
  16. package/dist/adapters/langchain-adapter.js.map +1 -1
  17. package/dist/adapters/langchain-adapter.mjs +2 -2
  18. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  19. package/dist/adapters/mastra-adapter.d.mts +2 -2
  20. package/dist/adapters/mastra-adapter.d.ts +2 -2
  21. package/dist/adapters/mastra-adapter.js +2 -2
  22. package/dist/adapters/mastra-adapter.js.map +1 -1
  23. package/dist/adapters/mastra-adapter.mjs +2 -2
  24. package/dist/adapters/mastra-adapter.mjs.map +1 -1
  25. package/dist/client/index.d.mts +2 -2
  26. package/dist/client/index.d.ts +2 -2
  27. package/dist/client/index.js +6 -0
  28. package/dist/client/index.js.map +1 -1
  29. package/dist/client/index.mjs +6 -0
  30. package/dist/client/index.mjs.map +1 -1
  31. package/dist/client/react.d.mts +15 -4
  32. package/dist/client/react.d.ts +15 -4
  33. package/dist/client/react.js +34 -0
  34. package/dist/client/react.js.map +1 -1
  35. package/dist/client/react.mjs +34 -0
  36. package/dist/client/react.mjs.map +1 -1
  37. package/dist/client/vue.d.mts +4 -4
  38. package/dist/client/vue.d.ts +4 -4
  39. package/dist/client/vue.js +6 -0
  40. package/dist/client/vue.js.map +1 -1
  41. package/dist/client/vue.mjs +6 -0
  42. package/dist/client/vue.mjs.map +1 -1
  43. package/dist/{index-CkM3p0eE.d.mts → index-CTURVnom.d.mts} +5 -1
  44. package/dist/{index-CZk2gu2E.d.ts → index-sVcqrhf7.d.ts} +5 -1
  45. package/dist/index.d.mts +4 -4
  46. package/dist/index.d.ts +4 -4
  47. package/dist/index.js +314 -82
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +310 -82
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/{multi-session-client-ChQrBZhF.d.mts → multi-session-client-CSPSHHla.d.ts} +14 -2
  52. package/dist/{multi-session-client-QOOPdEVp.d.ts → multi-session-client-DMZGVABI.d.mts} +14 -2
  53. package/dist/server/index.d.mts +13 -4
  54. package/dist/server/index.d.ts +13 -4
  55. package/dist/server/index.js +308 -82
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/index.mjs +304 -82
  58. package/dist/server/index.mjs.map +1 -1
  59. package/dist/shared/index.d.mts +4 -4
  60. package/dist/shared/index.d.ts +4 -4
  61. package/dist/shared/index.js.map +1 -1
  62. package/dist/shared/index.mjs.map +1 -1
  63. package/dist/{tool-router-DQ-HrD7W.d.ts → tool-router-CS9l0w4a.d.ts} +1 -1
  64. package/dist/{tool-router-CGs3IDOJ.d.mts → tool-router-CVLBaCwH.d.mts} +1 -1
  65. package/dist/{types-Bf-7GOLW.d.mts → types-DK_NGWd4.d.mts} +37 -3
  66. package/dist/{types-Bf-7GOLW.d.ts → types-DK_NGWd4.d.ts} +37 -3
  67. package/migrations/neon/20260513010000_install_mcp_sessions.sql +1 -0
  68. package/migrations/supabase/20260330195700_install_mcp_sessions.sql +1 -0
  69. package/package.json +1 -1
  70. package/src/adapters/agui-adapter.ts +6 -2
  71. package/src/adapters/ai-adapter.ts +3 -1
  72. package/src/adapters/langchain-adapter.ts +5 -3
  73. package/src/adapters/mastra-adapter.ts +5 -3
  74. package/src/client/core/sse-client.ts +17 -0
  75. package/src/client/react/use-mcp.ts +63 -0
  76. package/src/server/handlers/sse-handler.ts +116 -9
  77. package/src/server/mcp/multi-session-client.ts +7 -3
  78. package/src/server/mcp/oauth-client.ts +25 -10
  79. package/src/server/mcp/tool-policy-gateway.ts +90 -0
  80. package/src/server/storage/neon-backend.ts +65 -60
  81. package/src/server/storage/session-lifecycle.ts +5 -0
  82. package/src/server/storage/supabase-backend.ts +27 -15
  83. package/src/server/storage/tool-policy.ts +89 -0
  84. package/src/server/storage/types.ts +11 -0
  85. 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 { error } = await this.supabase.from("mcp_sessions").insert({
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 expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
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
- session_id,
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
- server_id = $1,
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 request = {
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 client.listTools();
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 client = await this.getOrCreateClient(sessionId);
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 client.listTools();
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 client.listTools();
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
  }