@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
@@ -158,6 +158,59 @@ var SOFTWARE_VERSION = "2.3.4";
158
158
  var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
159
159
  var MCP_CLIENT_VERSION = "2.0";
160
160
 
161
+ // src/server/storage/tool-policy.ts
162
+ function createToolId(serverId, toolName) {
163
+ return `${serverId}::${toolName}`;
164
+ }
165
+ function normalizeToolIds(input) {
166
+ if (!Array.isArray(input)) return [];
167
+ return Array.from(new Set(
168
+ input.filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean)
169
+ ));
170
+ }
171
+ function normalizeToolPolicy(input, now = Date.now()) {
172
+ if (!input || typeof input !== "object") {
173
+ return void 0;
174
+ }
175
+ const mode = input.mode === "allowlist" || input.mode === "denylist" ? input.mode : "all";
176
+ const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : now;
177
+ if (mode === "all") {
178
+ return { mode: "all", toolIds: [], updatedAt };
179
+ }
180
+ return { mode, toolIds: normalizeToolIds(input.toolIds), updatedAt };
181
+ }
182
+ function normalizeToolPolicyForUpdate(input, now = Date.now()) {
183
+ return normalizeToolPolicy(input, now) ?? { mode: "all", toolIds: [], updatedAt: now };
184
+ }
185
+ function isToolAllowed(policy, toolName, serverId) {
186
+ if (!policy || policy.mode === "all") return true;
187
+ if (!serverId) return false;
188
+ const toolId = createToolId(serverId, toolName);
189
+ if (policy.mode === "allowlist") {
190
+ return policy.toolIds.includes(toolId);
191
+ }
192
+ return !policy.toolIds.includes(toolId);
193
+ }
194
+ function assertToolAllowed(policy, toolName, serverId) {
195
+ if (isToolAllowed(policy, toolName, serverId)) return;
196
+ throw new Error(`Tool "${toolName}" is not allowed for this MCP session`);
197
+ }
198
+ function filterToolsByPolicy(tools, policy, serverId) {
199
+ if (!policy || policy.mode === "all") return tools;
200
+ return tools.filter((tool) => isToolAllowed(policy, tool.name, serverId));
201
+ }
202
+ function validateToolPolicyAgainstTools(policy, tools, serverId) {
203
+ if (policy.mode === "all") return;
204
+ if (!serverId) {
205
+ throw new Error("Cannot validate MCP tool policy without a serverId");
206
+ }
207
+ const availableIds = new Set(tools.map((tool) => createToolId(serverId, tool.name)));
208
+ const unknownIds = policy.toolIds.filter((id) => !availableIds.has(id));
209
+ if (unknownIds.length > 0) {
210
+ throw new Error(`Unknown tool id(s) for this MCP session: ${unknownIds.join(", ")}`);
211
+ }
212
+ }
213
+
161
214
  // src/server/storage/session-lifecycle.ts
162
215
  function resolveSessionExpiresAt(status = "pending", referenceTime = Date.now()) {
163
216
  return status === "active" ? null : referenceTime + STATE_EXPIRATION_MS;
@@ -174,7 +227,8 @@ function normalizeNewSession(session, now = Date.now()) {
174
227
  status,
175
228
  createdAt,
176
229
  updatedAt,
177
- expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt)
230
+ expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt),
231
+ toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
178
232
  };
179
233
  }
180
234
  function mergeSessionUpdate(current, data, now = Date.now()) {
@@ -188,7 +242,8 @@ function mergeSessionUpdate(current, data, now = Date.now()) {
188
242
  return {
189
243
  ...updated,
190
244
  status,
191
- expiresAt: resolveSessionExpiresAt(status, updatedAt)
245
+ expiresAt: resolveSessionExpiresAt(status, updatedAt),
246
+ toolPolicy: normalizeToolPolicy(updated.toolPolicy, updatedAt)
192
247
  };
193
248
  }
194
249
  function normalizeStoredSession(session) {
@@ -201,7 +256,8 @@ function normalizeStoredSession(session) {
201
256
  status,
202
257
  createdAt,
203
258
  updatedAt,
204
- expiresAt
259
+ expiresAt,
260
+ toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
205
261
  };
206
262
  }
207
263
  function isSessionExpired(session, now = Date.now()) {
@@ -1059,7 +1115,8 @@ var SupabaseStorageBackend = class {
1059
1115
  userId: row.user_id,
1060
1116
  headers: decryptObject(row.headers),
1061
1117
  authUrl: row.auth_url,
1062
- status: row.status ?? "pending"
1118
+ status: row.status ?? "pending",
1119
+ toolPolicy: normalizeToolPolicy(row.tool_policy)
1063
1120
  };
1064
1121
  }
1065
1122
  mapRowToCredentials(row, userId, sessionId) {
@@ -1083,7 +1140,7 @@ var SupabaseStorageBackend = class {
1083
1140
  const createdAt = new Date(session.createdAt || Date.now()).toISOString();
1084
1141
  const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
1085
1142
  const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
1086
- const { error } = await this.supabase.from("mcp_sessions").insert({
1143
+ const insertData = {
1087
1144
  session_id: sessionId,
1088
1145
  user_id: userId,
1089
1146
  server_id: session.serverId,
@@ -1097,7 +1154,12 @@ var SupabaseStorageBackend = class {
1097
1154
  auth_url: session.authUrl ?? null,
1098
1155
  status,
1099
1156
  expires_at: expiresAt === null ? null : new Date(expiresAt).toISOString()
1100
- });
1157
+ };
1158
+ const toolPolicy = normalizeToolPolicy(session.toolPolicy);
1159
+ if (toolPolicy) {
1160
+ insertData.tool_policy = toolPolicy;
1161
+ }
1162
+ const { error } = await this.supabase.from("mcp_sessions").insert(insertData);
1101
1163
  if (error) {
1102
1164
  if (error.code === "23505") {
1103
1165
  throw new Error(`Session ${sessionId} already exists`);
@@ -1122,6 +1184,7 @@ var SupabaseStorageBackend = class {
1122
1184
  }
1123
1185
  if ("headers" in data) updateData.headers = encryptObject(data.headers);
1124
1186
  if ("authUrl" in data) updateData.auth_url = data.authUrl ?? null;
1187
+ if ("toolPolicy" in data) updateData.tool_policy = normalizeToolPolicy(data.toolPolicy);
1125
1188
  const shouldUpdateSession = Object.keys(updateData).some((key) => key !== "updated_at");
1126
1189
  let updatedRows = null;
1127
1190
  if (shouldUpdateSession) {
@@ -1294,7 +1357,8 @@ var NeonStorageBackend = class {
1294
1357
  userId: row.user_id,
1295
1358
  headers: decryptObject(row.headers),
1296
1359
  authUrl: row.auth_url ?? void 0,
1297
- status: row.status ?? "pending"
1360
+ status: row.status ?? "pending",
1361
+ toolPolicy: normalizeToolPolicy(row.tool_policy)
1298
1362
  };
1299
1363
  }
1300
1364
  mapRowToCredentials(row, userId, sessionId) {
@@ -1317,42 +1381,48 @@ var NeonStorageBackend = class {
1317
1381
  const status = session.status ?? "pending";
1318
1382
  const createdAt = new Date(session.createdAt || Date.now()).toISOString();
1319
1383
  const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
1320
- const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
1384
+ const createdAtMs = new Date(createdAt).getTime();
1385
+ const expiresAt = resolveSessionExpiresAt(status, createdAtMs);
1386
+ const toolPolicy = normalizeToolPolicy(session.toolPolicy, createdAtMs);
1387
+ const columns = [
1388
+ "session_id",
1389
+ "user_id",
1390
+ "server_id",
1391
+ "server_name",
1392
+ "server_url",
1393
+ "transport_type",
1394
+ "callback_url",
1395
+ "created_at",
1396
+ "updated_at",
1397
+ "headers",
1398
+ "auth_url",
1399
+ "status",
1400
+ "expires_at"
1401
+ ];
1402
+ const values = [
1403
+ sessionId,
1404
+ userId,
1405
+ session.serverId,
1406
+ session.serverName,
1407
+ session.serverUrl,
1408
+ session.transportType,
1409
+ session.callbackUrl,
1410
+ createdAt,
1411
+ updatedAt,
1412
+ encryptObject(session.headers),
1413
+ session.authUrl ?? null,
1414
+ status,
1415
+ expiresAt === null ? null : new Date(expiresAt).toISOString()
1416
+ ];
1417
+ if (toolPolicy) {
1418
+ columns.push("tool_policy");
1419
+ values.push(toolPolicy);
1420
+ }
1421
+ const placeholders = values.map((_, i) => `$${i + 1}`);
1321
1422
  try {
1322
1423
  await this.sql.query(
1323
- `INSERT INTO ${this.tableName} (
1324
- session_id,
1325
- user_id,
1326
- server_id,
1327
- server_name,
1328
- server_url,
1329
- transport_type,
1330
- callback_url,
1331
- created_at,
1332
- updated_at,
1333
- headers,
1334
- auth_url,
1335
- status,
1336
- expires_at
1337
- ) VALUES (
1338
- $1, $2, $3, $4, $5, $6, $7, $8,
1339
- $9, $10, $11, $12, $13
1340
- )`,
1341
- [
1342
- sessionId,
1343
- userId,
1344
- session.serverId,
1345
- session.serverName,
1346
- session.serverUrl,
1347
- session.transportType,
1348
- session.callbackUrl,
1349
- createdAt,
1350
- updatedAt,
1351
- encryptObject(session.headers),
1352
- session.authUrl ?? null,
1353
- status,
1354
- expiresAt === null ? null : new Date(expiresAt).toISOString()
1355
- ]
1424
+ `INSERT INTO ${this.tableName} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`,
1425
+ values
1356
1426
  );
1357
1427
  } catch (error) {
1358
1428
  if (error.code === "23505") {
@@ -1369,36 +1439,36 @@ var NeonStorageBackend = class {
1369
1439
  const updatedSession = { ...currentSession, ...data };
1370
1440
  const status = updatedSession.status ?? "pending";
1371
1441
  const expiresAt = resolveSessionExpiresAt(status);
1372
- 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;
1442
+ 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;
1373
1443
  if (shouldUpdateSession) {
1444
+ const setClauses = [];
1445
+ const values = [];
1446
+ let paramIndex = 1;
1447
+ const addSet = (column, value) => {
1448
+ setClauses.push(`${column} = $${paramIndex++}`);
1449
+ values.push(value);
1450
+ };
1451
+ addSet("server_id", updatedSession.serverId);
1452
+ addSet("server_name", updatedSession.serverName);
1453
+ addSet("server_url", updatedSession.serverUrl);
1454
+ addSet("transport_type", updatedSession.transportType);
1455
+ addSet("callback_url", updatedSession.callbackUrl);
1456
+ addSet("status", status);
1457
+ addSet("headers", encryptObject(updatedSession.headers));
1458
+ addSet("auth_url", updatedSession.authUrl ?? null);
1459
+ addSet("expires_at", expiresAt === null ? null : new Date(expiresAt).toISOString());
1460
+ if ("toolPolicy" in data) {
1461
+ const policyUpdatedAt = updatedSession.updatedAt ?? Date.now();
1462
+ const toolPolicy = normalizeToolPolicy(updatedSession.toolPolicy, policyUpdatedAt) ?? { mode: "all", toolIds: [], updatedAt: policyUpdatedAt };
1463
+ addSet("tool_policy", toolPolicy);
1464
+ }
1465
+ setClauses.push("updated_at = now()");
1374
1466
  const updatedRows = await this.sql.query(
1375
1467
  `UPDATE ${this.tableName}
1376
- SET
1377
- server_id = $1,
1378
- server_name = $2,
1379
- server_url = $3,
1380
- transport_type = $4,
1381
- callback_url = $5,
1382
- status = $6,
1383
- headers = $7,
1384
- auth_url = $8,
1385
- expires_at = $9,
1386
- updated_at = now()
1387
- WHERE user_id = $10 AND session_id = $11
1468
+ SET ${setClauses.join(", ")}
1469
+ WHERE user_id = $${paramIndex++} AND session_id = $${paramIndex++}
1388
1470
  RETURNING id`,
1389
- [
1390
- updatedSession.serverId,
1391
- updatedSession.serverName,
1392
- updatedSession.serverUrl,
1393
- updatedSession.transportType,
1394
- updatedSession.callbackUrl,
1395
- status,
1396
- encryptObject(updatedSession.headers),
1397
- updatedSession.authUrl ?? null,
1398
- expiresAt === null ? null : new Date(expiresAt).toISOString(),
1399
- userId,
1400
- sessionId
1401
- ]
1471
+ [...values, userId, sessionId]
1402
1472
  );
1403
1473
  if (updatedRows.length === 0) {
1404
1474
  throw new Error(`Session ${sessionId} not found for userId ${userId}`);
@@ -2612,6 +2682,19 @@ var MCPClient = class {
2612
2682
  throw lastError;
2613
2683
  }
2614
2684
  }
2685
+ /**
2686
+ * Lists all available tools from the connected MCP server without emitting discovery events.
2687
+ * Gateways use this to apply policy before publishing tools to agents or UI state.
2688
+ */
2689
+ async fetchTools() {
2690
+ const request = {
2691
+ method: "tools/list",
2692
+ params: {}
2693
+ };
2694
+ return await this.withRetry(
2695
+ () => this.client.request(request, ListToolsResultSchema)
2696
+ );
2697
+ }
2615
2698
  /**
2616
2699
  * Lists all available tools from the connected MCP server
2617
2700
  * @returns List of tools with their schemas and descriptions
@@ -2620,13 +2703,7 @@ var MCPClient = class {
2620
2703
  async listTools() {
2621
2704
  this.emitStateChange("DISCOVERING");
2622
2705
  try {
2623
- const request = {
2624
- method: "tools/list",
2625
- params: {}
2626
- };
2627
- const result = await this.withRetry(
2628
- () => this.client.request(request, ListToolsResultSchema)
2629
- );
2706
+ const result = await this.fetchTools();
2630
2707
  if (this.serverId) {
2631
2708
  this._onConnectionEvent.fire({
2632
2709
  type: "tools_discovered",
@@ -2953,6 +3030,66 @@ var MCPClient = class {
2953
3030
  }
2954
3031
  };
2955
3032
 
3033
+ // src/server/mcp/tool-policy-gateway.ts
3034
+ var ToolPolicyGateway = class {
3035
+ constructor(userId, sessionId, client) {
3036
+ this.userId = userId;
3037
+ this.sessionId = sessionId;
3038
+ this.client = client;
3039
+ }
3040
+ isConnected() {
3041
+ return this.client.isConnected();
3042
+ }
3043
+ getServerId() {
3044
+ return this.client.getServerId?.();
3045
+ }
3046
+ getServerName() {
3047
+ return this.client.getServerName?.();
3048
+ }
3049
+ getServerUrl() {
3050
+ return this.client.getServerUrl?.();
3051
+ }
3052
+ getSessionId() {
3053
+ return this.client.getSessionId?.() ?? this.sessionId;
3054
+ }
3055
+ async listTools() {
3056
+ const session = await this.getSession();
3057
+ const result = await this.client.fetchTools();
3058
+ const tools = this.filterTools(session, result.tools);
3059
+ return {
3060
+ ...result,
3061
+ tools
3062
+ };
3063
+ }
3064
+ async listAllTools() {
3065
+ return await this.client.fetchTools();
3066
+ }
3067
+ async callTool(name, args) {
3068
+ const session = await this.getSession();
3069
+ this.assertAllowed(session, name);
3070
+ return await this.client.callTool(name, args);
3071
+ }
3072
+ filterTools(session, tools) {
3073
+ return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
3074
+ }
3075
+ assertAllowed(session, toolName) {
3076
+ assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
3077
+ }
3078
+ async getSession() {
3079
+ const session = await sessions.get(this.userId, this.sessionId);
3080
+ if (!session) {
3081
+ throw new Error("Session not found");
3082
+ }
3083
+ return session;
3084
+ }
3085
+ getPolicyServerId(session) {
3086
+ return this.client.getServerId?.() ?? session.serverId;
3087
+ }
3088
+ };
3089
+ function createToolPolicyGateway(userId, sessionId, client) {
3090
+ return new ToolPolicyGateway(userId, sessionId, client);
3091
+ }
3092
+
2956
3093
  // src/server/mcp/multi-session-client.ts
2957
3094
  var DEFAULT_TIMEOUT_MS = 15e3;
2958
3095
  var DEFAULT_MAX_RETRIES = 2;
@@ -3018,7 +3155,9 @@ var MultiSessionClient = class {
3018
3155
  * or to route a tool call to the right client by `serverId`.
3019
3156
  */
3020
3157
  getClients() {
3021
- return this.clients;
3158
+ return this.clients.map(
3159
+ (client) => createToolPolicyGateway(this.userId, client.getSessionId(), client)
3160
+ );
3022
3161
  }
3023
3162
  /**
3024
3163
  * Removes and disconnects a single session by ID.
@@ -3253,6 +3392,12 @@ var SSEConnectionManager = class {
3253
3392
  case "listTools":
3254
3393
  result = await this.listTools(request.params);
3255
3394
  break;
3395
+ case "setToolPolicy":
3396
+ result = await this.setToolPolicy(request.params);
3397
+ break;
3398
+ case "getToolPolicy":
3399
+ result = await this.getToolPolicy(request.params);
3400
+ break;
3256
3401
  case "callTool":
3257
3402
  result = await this.callTool(request.params);
3258
3403
  break;
@@ -3309,7 +3454,8 @@ var SSEConnectionManager = class {
3309
3454
  transport: s.transportType,
3310
3455
  createdAt: s.createdAt,
3311
3456
  updatedAt: s.updatedAt ?? s.createdAt,
3312
- status: s.status ?? "pending"
3457
+ status: s.status ?? "pending",
3458
+ toolPolicy: s.toolPolicy
3313
3459
  }))
3314
3460
  };
3315
3461
  }
@@ -3357,7 +3503,7 @@ var SSEConnectionManager = class {
3357
3503
  this.sendEvent(event);
3358
3504
  });
3359
3505
  await client.connect();
3360
- await client.listTools();
3506
+ await this.listPolicyFilteredTools(sessionId);
3361
3507
  return {
3362
3508
  sessionId,
3363
3509
  success: true
@@ -3428,22 +3574,98 @@ var SSEConnectionManager = class {
3428
3574
  this.clients.set(sessionId, client);
3429
3575
  return client;
3430
3576
  }
3577
+ async listPolicyFilteredTools(sessionId) {
3578
+ const session = await sessions.get(this.userId, sessionId);
3579
+ if (!session) {
3580
+ throw new Error("Session not found");
3581
+ }
3582
+ const client = await this.getOrCreateClient(sessionId);
3583
+ const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3584
+ const result = await gateway.listTools();
3585
+ this.emitConnectionEvent({
3586
+ type: "tools_discovered",
3587
+ sessionId,
3588
+ serverId: session.serverId ?? "unknown",
3589
+ toolCount: result.tools.length,
3590
+ tools: result.tools,
3591
+ timestamp: Date.now()
3592
+ });
3593
+ return { session, result };
3594
+ }
3431
3595
  /**
3432
3596
  * List tools from a session
3433
3597
  */
3434
3598
  async listTools(params) {
3435
3599
  const { sessionId } = params;
3436
- const client = await this.getOrCreateClient(sessionId);
3437
- const result = await client.listTools();
3600
+ const { result } = await this.listPolicyFilteredTools(sessionId);
3438
3601
  return { tools: result.tools };
3439
3602
  }
3603
+ /**
3604
+ * Get all raw tools with effective access state for management UI.
3605
+ */
3606
+ async getToolPolicy(params) {
3607
+ const { sessionId } = params;
3608
+ const session = await sessions.get(this.userId, sessionId);
3609
+ if (!session) {
3610
+ throw new Error("Session not found");
3611
+ }
3612
+ const client = await this.getOrCreateClient(sessionId);
3613
+ const allTools = await client.fetchTools();
3614
+ const toolPolicy = session.toolPolicy ?? {
3615
+ mode: "all",
3616
+ toolIds: [],
3617
+ updatedAt: session.updatedAt ?? session.createdAt
3618
+ };
3619
+ const serverId = session.serverId ?? "unknown";
3620
+ const tools = allTools.tools.map((tool) => ({
3621
+ ...tool,
3622
+ toolId: createToolId(serverId, tool.name),
3623
+ allowed: isToolAllowed(toolPolicy, tool.name, session.serverId)
3624
+ }));
3625
+ return {
3626
+ toolPolicy,
3627
+ tools,
3628
+ toolCount: tools.length,
3629
+ allowedToolCount: tools.filter((tool) => tool.allowed).length
3630
+ };
3631
+ }
3632
+ /**
3633
+ * Update per-session tool access policy.
3634
+ */
3635
+ async setToolPolicy(params) {
3636
+ const { sessionId } = params;
3637
+ const session = await sessions.get(this.userId, sessionId);
3638
+ if (!session) {
3639
+ throw new Error("Session not found");
3640
+ }
3641
+ const client = await this.getOrCreateClient(sessionId);
3642
+ const allTools = await client.fetchTools();
3643
+ const toolPolicy = normalizeToolPolicyForUpdate(params.toolPolicy);
3644
+ validateToolPolicyAgainstTools(toolPolicy, allTools.tools, session.serverId);
3645
+ await sessions.update(this.userId, sessionId, { toolPolicy });
3646
+ const filteredTools = createToolPolicyGateway(this.userId, sessionId, client).filterTools({ ...session, toolPolicy }, allTools.tools);
3647
+ this.emitConnectionEvent({
3648
+ type: "tools_discovered",
3649
+ sessionId,
3650
+ serverId: session.serverId ?? "unknown",
3651
+ toolCount: filteredTools.length,
3652
+ tools: filteredTools,
3653
+ timestamp: Date.now()
3654
+ });
3655
+ return {
3656
+ success: true,
3657
+ toolPolicy,
3658
+ tools: filteredTools,
3659
+ toolCount: filteredTools.length
3660
+ };
3661
+ }
3440
3662
  /**
3441
3663
  * Call a tool on the MCP server
3442
3664
  */
3443
3665
  async callTool(params) {
3444
3666
  const { sessionId, toolName, toolArgs } = params;
3445
3667
  const client = await this.getOrCreateClient(sessionId);
3446
- const result = await client.callTool(toolName, toolArgs);
3668
+ const result = await createToolPolicyGateway(this.userId, sessionId, client).callTool(toolName, toolArgs);
3447
3669
  const meta = result._meta || {};
3448
3670
  return {
3449
3671
  ...result,
@@ -3493,7 +3715,7 @@ var SSEConnectionManager = class {
3493
3715
  client.onObservabilityEvent((event) => this.sendEvent(event));
3494
3716
  await client.connect();
3495
3717
  this.clients.set(sessionId, client);
3496
- const tools = await client.listTools();
3718
+ const { result: tools } = await this.listPolicyFilteredTools(sessionId);
3497
3719
  return { success: true, toolCount: tools.tools.length };
3498
3720
  } catch (error) {
3499
3721
  this.emitConnectionEvent({
@@ -3541,7 +3763,7 @@ var SSEConnectionManager = class {
3541
3763
  client.onConnectionEvent((event) => this.emitConnectionEvent(event));
3542
3764
  await client.finishAuth(code, oauthState);
3543
3765
  this.clients.set(sessionId, client);
3544
- const tools = await client.listTools();
3766
+ const { result: tools } = await this.listPolicyFilteredTools(sessionId);
3545
3767
  return { success: true, toolCount: tools.tools.length };
3546
3768
  } catch (error) {
3547
3769
  this.emitConnectionEvent({