@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.mjs CHANGED
@@ -162,6 +162,59 @@ var SOFTWARE_VERSION = "2.3.4";
162
162
  var MCP_CLIENT_NAME = "mcp-ts-oauth-client";
163
163
  var MCP_CLIENT_VERSION = "2.0";
164
164
 
165
+ // src/server/storage/tool-policy.ts
166
+ function createToolId(serverId, toolName) {
167
+ return `${serverId}::${toolName}`;
168
+ }
169
+ function normalizeToolIds(input) {
170
+ if (!Array.isArray(input)) return [];
171
+ return Array.from(new Set(
172
+ input.filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean)
173
+ ));
174
+ }
175
+ function normalizeToolPolicy(input, now = Date.now()) {
176
+ if (!input || typeof input !== "object") {
177
+ return void 0;
178
+ }
179
+ const mode = input.mode === "allowlist" || input.mode === "denylist" ? input.mode : "all";
180
+ const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : now;
181
+ if (mode === "all") {
182
+ return { mode: "all", toolIds: [], updatedAt };
183
+ }
184
+ return { mode, toolIds: normalizeToolIds(input.toolIds), updatedAt };
185
+ }
186
+ function normalizeToolPolicyForUpdate(input, now = Date.now()) {
187
+ return normalizeToolPolicy(input, now) ?? { mode: "all", toolIds: [], updatedAt: now };
188
+ }
189
+ function isToolAllowed(policy, toolName, serverId) {
190
+ if (!policy || policy.mode === "all") return true;
191
+ if (!serverId) return false;
192
+ const toolId = createToolId(serverId, toolName);
193
+ if (policy.mode === "allowlist") {
194
+ return policy.toolIds.includes(toolId);
195
+ }
196
+ return !policy.toolIds.includes(toolId);
197
+ }
198
+ function assertToolAllowed(policy, toolName, serverId) {
199
+ if (isToolAllowed(policy, toolName, serverId)) return;
200
+ throw new Error(`Tool "${toolName}" is not allowed for this MCP session`);
201
+ }
202
+ function filterToolsByPolicy(tools, policy, serverId) {
203
+ if (!policy || policy.mode === "all") return tools;
204
+ return tools.filter((tool) => isToolAllowed(policy, tool.name, serverId));
205
+ }
206
+ function validateToolPolicyAgainstTools(policy, tools, serverId) {
207
+ if (policy.mode === "all") return;
208
+ if (!serverId) {
209
+ throw new Error("Cannot validate MCP tool policy without a serverId");
210
+ }
211
+ const availableIds = new Set(tools.map((tool) => createToolId(serverId, tool.name)));
212
+ const unknownIds = policy.toolIds.filter((id) => !availableIds.has(id));
213
+ if (unknownIds.length > 0) {
214
+ throw new Error(`Unknown tool id(s) for this MCP session: ${unknownIds.join(", ")}`);
215
+ }
216
+ }
217
+
165
218
  // src/server/storage/session-lifecycle.ts
166
219
  function resolveSessionExpiresAt(status = "pending", referenceTime = Date.now()) {
167
220
  return status === "active" ? null : referenceTime + STATE_EXPIRATION_MS;
@@ -178,7 +231,8 @@ function normalizeNewSession(session, now = Date.now()) {
178
231
  status,
179
232
  createdAt,
180
233
  updatedAt,
181
- expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt)
234
+ expiresAt: resolveSessionExpiresAt(status, status === "active" ? updatedAt : createdAt),
235
+ toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
182
236
  };
183
237
  }
184
238
  function mergeSessionUpdate(current, data, now = Date.now()) {
@@ -192,7 +246,8 @@ function mergeSessionUpdate(current, data, now = Date.now()) {
192
246
  return {
193
247
  ...updated,
194
248
  status,
195
- expiresAt: resolveSessionExpiresAt(status, updatedAt)
249
+ expiresAt: resolveSessionExpiresAt(status, updatedAt),
250
+ toolPolicy: normalizeToolPolicy(updated.toolPolicy, updatedAt)
196
251
  };
197
252
  }
198
253
  function normalizeStoredSession(session) {
@@ -205,7 +260,8 @@ function normalizeStoredSession(session) {
205
260
  status,
206
261
  createdAt,
207
262
  updatedAt,
208
- expiresAt
263
+ expiresAt,
264
+ toolPolicy: normalizeToolPolicy(session.toolPolicy, updatedAt)
209
265
  };
210
266
  }
211
267
  function isSessionExpired(session, now = Date.now()) {
@@ -1063,7 +1119,8 @@ var SupabaseStorageBackend = class {
1063
1119
  userId: row.user_id,
1064
1120
  headers: decryptObject(row.headers),
1065
1121
  authUrl: row.auth_url,
1066
- status: row.status ?? "pending"
1122
+ status: row.status ?? "pending",
1123
+ toolPolicy: normalizeToolPolicy(row.tool_policy)
1067
1124
  };
1068
1125
  }
1069
1126
  mapRowToCredentials(row, userId, sessionId) {
@@ -1087,7 +1144,7 @@ var SupabaseStorageBackend = class {
1087
1144
  const createdAt = new Date(session.createdAt || Date.now()).toISOString();
1088
1145
  const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
1089
1146
  const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
1090
- const { error } = await this.supabase.from("mcp_sessions").insert({
1147
+ const insertData = {
1091
1148
  session_id: sessionId,
1092
1149
  user_id: userId,
1093
1150
  server_id: session.serverId,
@@ -1101,7 +1158,12 @@ var SupabaseStorageBackend = class {
1101
1158
  auth_url: session.authUrl ?? null,
1102
1159
  status,
1103
1160
  expires_at: expiresAt === null ? null : new Date(expiresAt).toISOString()
1104
- });
1161
+ };
1162
+ const toolPolicy = normalizeToolPolicy(session.toolPolicy);
1163
+ if (toolPolicy) {
1164
+ insertData.tool_policy = toolPolicy;
1165
+ }
1166
+ const { error } = await this.supabase.from("mcp_sessions").insert(insertData);
1105
1167
  if (error) {
1106
1168
  if (error.code === "23505") {
1107
1169
  throw new Error(`Session ${sessionId} already exists`);
@@ -1126,6 +1188,7 @@ var SupabaseStorageBackend = class {
1126
1188
  }
1127
1189
  if ("headers" in data) updateData.headers = encryptObject(data.headers);
1128
1190
  if ("authUrl" in data) updateData.auth_url = data.authUrl ?? null;
1191
+ if ("toolPolicy" in data) updateData.tool_policy = normalizeToolPolicy(data.toolPolicy);
1129
1192
  const shouldUpdateSession = Object.keys(updateData).some((key) => key !== "updated_at");
1130
1193
  let updatedRows = null;
1131
1194
  if (shouldUpdateSession) {
@@ -1298,7 +1361,8 @@ var NeonStorageBackend = class {
1298
1361
  userId: row.user_id,
1299
1362
  headers: decryptObject(row.headers),
1300
1363
  authUrl: row.auth_url ?? void 0,
1301
- status: row.status ?? "pending"
1364
+ status: row.status ?? "pending",
1365
+ toolPolicy: normalizeToolPolicy(row.tool_policy)
1302
1366
  };
1303
1367
  }
1304
1368
  mapRowToCredentials(row, userId, sessionId) {
@@ -1321,42 +1385,48 @@ var NeonStorageBackend = class {
1321
1385
  const status = session.status ?? "pending";
1322
1386
  const createdAt = new Date(session.createdAt || Date.now()).toISOString();
1323
1387
  const updatedAt = new Date(session.updatedAt ?? session.createdAt ?? Date.now()).toISOString();
1324
- const expiresAt = resolveSessionExpiresAt(status, new Date(createdAt).getTime());
1388
+ const createdAtMs = new Date(createdAt).getTime();
1389
+ const expiresAt = resolveSessionExpiresAt(status, createdAtMs);
1390
+ const toolPolicy = normalizeToolPolicy(session.toolPolicy, createdAtMs);
1391
+ const columns = [
1392
+ "session_id",
1393
+ "user_id",
1394
+ "server_id",
1395
+ "server_name",
1396
+ "server_url",
1397
+ "transport_type",
1398
+ "callback_url",
1399
+ "created_at",
1400
+ "updated_at",
1401
+ "headers",
1402
+ "auth_url",
1403
+ "status",
1404
+ "expires_at"
1405
+ ];
1406
+ const values = [
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
+ ];
1421
+ if (toolPolicy) {
1422
+ columns.push("tool_policy");
1423
+ values.push(toolPolicy);
1424
+ }
1425
+ const placeholders = values.map((_, i) => `$${i + 1}`);
1325
1426
  try {
1326
1427
  await this.sql.query(
1327
- `INSERT INTO ${this.tableName} (
1328
- session_id,
1329
- user_id,
1330
- server_id,
1331
- server_name,
1332
- server_url,
1333
- transport_type,
1334
- callback_url,
1335
- created_at,
1336
- updated_at,
1337
- headers,
1338
- auth_url,
1339
- status,
1340
- expires_at
1341
- ) VALUES (
1342
- $1, $2, $3, $4, $5, $6, $7, $8,
1343
- $9, $10, $11, $12, $13
1344
- )`,
1345
- [
1346
- sessionId,
1347
- userId,
1348
- session.serverId,
1349
- session.serverName,
1350
- session.serverUrl,
1351
- session.transportType,
1352
- session.callbackUrl,
1353
- createdAt,
1354
- updatedAt,
1355
- encryptObject(session.headers),
1356
- session.authUrl ?? null,
1357
- status,
1358
- expiresAt === null ? null : new Date(expiresAt).toISOString()
1359
- ]
1428
+ `INSERT INTO ${this.tableName} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`,
1429
+ values
1360
1430
  );
1361
1431
  } catch (error) {
1362
1432
  if (error.code === "23505") {
@@ -1373,36 +1443,36 @@ var NeonStorageBackend = class {
1373
1443
  const updatedSession = { ...currentSession, ...data };
1374
1444
  const status = updatedSession.status ?? "pending";
1375
1445
  const expiresAt = resolveSessionExpiresAt(status);
1376
- 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;
1446
+ 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;
1377
1447
  if (shouldUpdateSession) {
1448
+ const setClauses = [];
1449
+ const values = [];
1450
+ let paramIndex = 1;
1451
+ const addSet = (column, value) => {
1452
+ setClauses.push(`${column} = $${paramIndex++}`);
1453
+ values.push(value);
1454
+ };
1455
+ addSet("server_id", updatedSession.serverId);
1456
+ addSet("server_name", updatedSession.serverName);
1457
+ addSet("server_url", updatedSession.serverUrl);
1458
+ addSet("transport_type", updatedSession.transportType);
1459
+ addSet("callback_url", updatedSession.callbackUrl);
1460
+ addSet("status", status);
1461
+ addSet("headers", encryptObject(updatedSession.headers));
1462
+ addSet("auth_url", updatedSession.authUrl ?? null);
1463
+ addSet("expires_at", expiresAt === null ? null : new Date(expiresAt).toISOString());
1464
+ if ("toolPolicy" in data) {
1465
+ const policyUpdatedAt = updatedSession.updatedAt ?? Date.now();
1466
+ const toolPolicy = normalizeToolPolicy(updatedSession.toolPolicy, policyUpdatedAt) ?? { mode: "all", toolIds: [], updatedAt: policyUpdatedAt };
1467
+ addSet("tool_policy", toolPolicy);
1468
+ }
1469
+ setClauses.push("updated_at = now()");
1378
1470
  const updatedRows = await this.sql.query(
1379
1471
  `UPDATE ${this.tableName}
1380
- SET
1381
- server_id = $1,
1382
- server_name = $2,
1383
- server_url = $3,
1384
- transport_type = $4,
1385
- callback_url = $5,
1386
- status = $6,
1387
- headers = $7,
1388
- auth_url = $8,
1389
- expires_at = $9,
1390
- updated_at = now()
1391
- WHERE user_id = $10 AND session_id = $11
1472
+ SET ${setClauses.join(", ")}
1473
+ WHERE user_id = $${paramIndex++} AND session_id = $${paramIndex++}
1392
1474
  RETURNING id`,
1393
- [
1394
- updatedSession.serverId,
1395
- updatedSession.serverName,
1396
- updatedSession.serverUrl,
1397
- updatedSession.transportType,
1398
- updatedSession.callbackUrl,
1399
- status,
1400
- encryptObject(updatedSession.headers),
1401
- updatedSession.authUrl ?? null,
1402
- expiresAt === null ? null : new Date(expiresAt).toISOString(),
1403
- userId,
1404
- sessionId
1405
- ]
1475
+ [...values, userId, sessionId]
1406
1476
  );
1407
1477
  if (updatedRows.length === 0) {
1408
1478
  throw new Error(`Session ${sessionId} not found for userId ${userId}`);
@@ -2684,6 +2754,19 @@ var MCPClient = class {
2684
2754
  throw lastError;
2685
2755
  }
2686
2756
  }
2757
+ /**
2758
+ * Lists all available tools from the connected MCP server without emitting discovery events.
2759
+ * Gateways use this to apply policy before publishing tools to agents or UI state.
2760
+ */
2761
+ async fetchTools() {
2762
+ const request = {
2763
+ method: "tools/list",
2764
+ params: {}
2765
+ };
2766
+ return await this.withRetry(
2767
+ () => this.client.request(request, ListToolsResultSchema)
2768
+ );
2769
+ }
2687
2770
  /**
2688
2771
  * Lists all available tools from the connected MCP server
2689
2772
  * @returns List of tools with their schemas and descriptions
@@ -2692,13 +2775,7 @@ var MCPClient = class {
2692
2775
  async listTools() {
2693
2776
  this.emitStateChange("DISCOVERING");
2694
2777
  try {
2695
- const request = {
2696
- method: "tools/list",
2697
- params: {}
2698
- };
2699
- const result = await this.withRetry(
2700
- () => this.client.request(request, ListToolsResultSchema)
2701
- );
2778
+ const result = await this.fetchTools();
2702
2779
  if (this.serverId) {
2703
2780
  this._onConnectionEvent.fire({
2704
2781
  type: "tools_discovered",
@@ -3025,6 +3102,66 @@ var MCPClient = class {
3025
3102
  }
3026
3103
  };
3027
3104
 
3105
+ // src/server/mcp/tool-policy-gateway.ts
3106
+ var ToolPolicyGateway = class {
3107
+ constructor(userId, sessionId, client) {
3108
+ this.userId = userId;
3109
+ this.sessionId = sessionId;
3110
+ this.client = client;
3111
+ }
3112
+ isConnected() {
3113
+ return this.client.isConnected();
3114
+ }
3115
+ getServerId() {
3116
+ return this.client.getServerId?.();
3117
+ }
3118
+ getServerName() {
3119
+ return this.client.getServerName?.();
3120
+ }
3121
+ getServerUrl() {
3122
+ return this.client.getServerUrl?.();
3123
+ }
3124
+ getSessionId() {
3125
+ return this.client.getSessionId?.() ?? this.sessionId;
3126
+ }
3127
+ async listTools() {
3128
+ const session = await this.getSession();
3129
+ const result = await this.client.fetchTools();
3130
+ const tools = this.filterTools(session, result.tools);
3131
+ return {
3132
+ ...result,
3133
+ tools
3134
+ };
3135
+ }
3136
+ async listAllTools() {
3137
+ return await this.client.fetchTools();
3138
+ }
3139
+ async callTool(name, args) {
3140
+ const session = await this.getSession();
3141
+ this.assertAllowed(session, name);
3142
+ return await this.client.callTool(name, args);
3143
+ }
3144
+ filterTools(session, tools) {
3145
+ return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
3146
+ }
3147
+ assertAllowed(session, toolName) {
3148
+ assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
3149
+ }
3150
+ async getSession() {
3151
+ const session = await sessions.get(this.userId, this.sessionId);
3152
+ if (!session) {
3153
+ throw new Error("Session not found");
3154
+ }
3155
+ return session;
3156
+ }
3157
+ getPolicyServerId(session) {
3158
+ return this.client.getServerId?.() ?? session.serverId;
3159
+ }
3160
+ };
3161
+ function createToolPolicyGateway(userId, sessionId, client) {
3162
+ return new ToolPolicyGateway(userId, sessionId, client);
3163
+ }
3164
+
3028
3165
  // src/server/mcp/multi-session-client.ts
3029
3166
  var DEFAULT_TIMEOUT_MS = 15e3;
3030
3167
  var DEFAULT_MAX_RETRIES = 2;
@@ -3090,7 +3227,9 @@ var MultiSessionClient = class {
3090
3227
  * or to route a tool call to the right client by `serverId`.
3091
3228
  */
3092
3229
  getClients() {
3093
- return this.clients;
3230
+ return this.clients.map(
3231
+ (client) => createToolPolicyGateway(this.userId, client.getSessionId(), client)
3232
+ );
3094
3233
  }
3095
3234
  /**
3096
3235
  * Removes and disconnects a single session by ID.
@@ -3325,6 +3464,12 @@ var SSEConnectionManager = class {
3325
3464
  case "listTools":
3326
3465
  result = await this.listTools(request.params);
3327
3466
  break;
3467
+ case "setToolPolicy":
3468
+ result = await this.setToolPolicy(request.params);
3469
+ break;
3470
+ case "getToolPolicy":
3471
+ result = await this.getToolPolicy(request.params);
3472
+ break;
3328
3473
  case "callTool":
3329
3474
  result = await this.callTool(request.params);
3330
3475
  break;
@@ -3381,7 +3526,8 @@ var SSEConnectionManager = class {
3381
3526
  transport: s.transportType,
3382
3527
  createdAt: s.createdAt,
3383
3528
  updatedAt: s.updatedAt ?? s.createdAt,
3384
- status: s.status ?? "pending"
3529
+ status: s.status ?? "pending",
3530
+ toolPolicy: s.toolPolicy
3385
3531
  }))
3386
3532
  };
3387
3533
  }
@@ -3429,7 +3575,7 @@ var SSEConnectionManager = class {
3429
3575
  this.sendEvent(event);
3430
3576
  });
3431
3577
  await client.connect();
3432
- await client.listTools();
3578
+ await this.listPolicyFilteredTools(sessionId);
3433
3579
  return {
3434
3580
  sessionId,
3435
3581
  success: true
@@ -3500,22 +3646,98 @@ var SSEConnectionManager = class {
3500
3646
  this.clients.set(sessionId, client);
3501
3647
  return client;
3502
3648
  }
3649
+ async listPolicyFilteredTools(sessionId) {
3650
+ const session = await sessions.get(this.userId, sessionId);
3651
+ if (!session) {
3652
+ throw new Error("Session not found");
3653
+ }
3654
+ const client = await this.getOrCreateClient(sessionId);
3655
+ const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3656
+ const result = await gateway.listTools();
3657
+ this.emitConnectionEvent({
3658
+ type: "tools_discovered",
3659
+ sessionId,
3660
+ serverId: session.serverId ?? "unknown",
3661
+ toolCount: result.tools.length,
3662
+ tools: result.tools,
3663
+ timestamp: Date.now()
3664
+ });
3665
+ return { session, result };
3666
+ }
3503
3667
  /**
3504
3668
  * List tools from a session
3505
3669
  */
3506
3670
  async listTools(params) {
3507
3671
  const { sessionId } = params;
3508
- const client = await this.getOrCreateClient(sessionId);
3509
- const result = await client.listTools();
3672
+ const { result } = await this.listPolicyFilteredTools(sessionId);
3510
3673
  return { tools: result.tools };
3511
3674
  }
3675
+ /**
3676
+ * Get all raw tools with effective access state for management UI.
3677
+ */
3678
+ async getToolPolicy(params) {
3679
+ const { sessionId } = params;
3680
+ const session = await sessions.get(this.userId, sessionId);
3681
+ if (!session) {
3682
+ throw new Error("Session not found");
3683
+ }
3684
+ const client = await this.getOrCreateClient(sessionId);
3685
+ const allTools = await client.fetchTools();
3686
+ const toolPolicy = session.toolPolicy ?? {
3687
+ mode: "all",
3688
+ toolIds: [],
3689
+ updatedAt: session.updatedAt ?? session.createdAt
3690
+ };
3691
+ const serverId = session.serverId ?? "unknown";
3692
+ const tools = allTools.tools.map((tool) => ({
3693
+ ...tool,
3694
+ toolId: createToolId(serverId, tool.name),
3695
+ allowed: isToolAllowed(toolPolicy, tool.name, session.serverId)
3696
+ }));
3697
+ return {
3698
+ toolPolicy,
3699
+ tools,
3700
+ toolCount: tools.length,
3701
+ allowedToolCount: tools.filter((tool) => tool.allowed).length
3702
+ };
3703
+ }
3704
+ /**
3705
+ * Update per-session tool access policy.
3706
+ */
3707
+ async setToolPolicy(params) {
3708
+ const { sessionId } = params;
3709
+ const session = await sessions.get(this.userId, sessionId);
3710
+ if (!session) {
3711
+ throw new Error("Session not found");
3712
+ }
3713
+ const client = await this.getOrCreateClient(sessionId);
3714
+ const allTools = await client.fetchTools();
3715
+ const toolPolicy = normalizeToolPolicyForUpdate(params.toolPolicy);
3716
+ validateToolPolicyAgainstTools(toolPolicy, allTools.tools, session.serverId);
3717
+ await sessions.update(this.userId, sessionId, { toolPolicy });
3718
+ const filteredTools = createToolPolicyGateway(this.userId, sessionId, client).filterTools({ ...session, toolPolicy }, allTools.tools);
3719
+ this.emitConnectionEvent({
3720
+ type: "tools_discovered",
3721
+ sessionId,
3722
+ serverId: session.serverId ?? "unknown",
3723
+ toolCount: filteredTools.length,
3724
+ tools: filteredTools,
3725
+ timestamp: Date.now()
3726
+ });
3727
+ return {
3728
+ success: true,
3729
+ toolPolicy,
3730
+ tools: filteredTools,
3731
+ toolCount: filteredTools.length
3732
+ };
3733
+ }
3512
3734
  /**
3513
3735
  * Call a tool on the MCP server
3514
3736
  */
3515
3737
  async callTool(params) {
3516
3738
  const { sessionId, toolName, toolArgs } = params;
3517
3739
  const client = await this.getOrCreateClient(sessionId);
3518
- const result = await client.callTool(toolName, toolArgs);
3740
+ const result = await createToolPolicyGateway(this.userId, sessionId, client).callTool(toolName, toolArgs);
3519
3741
  const meta = result._meta || {};
3520
3742
  return {
3521
3743
  ...result,
@@ -3565,7 +3787,7 @@ var SSEConnectionManager = class {
3565
3787
  client.onObservabilityEvent((event) => this.sendEvent(event));
3566
3788
  await client.connect();
3567
3789
  this.clients.set(sessionId, client);
3568
- const tools = await client.listTools();
3790
+ const { result: tools } = await this.listPolicyFilteredTools(sessionId);
3569
3791
  return { success: true, toolCount: tools.tools.length };
3570
3792
  } catch (error) {
3571
3793
  this.emitConnectionEvent({
@@ -3613,7 +3835,7 @@ var SSEConnectionManager = class {
3613
3835
  client.onConnectionEvent((event) => this.emitConnectionEvent(event));
3614
3836
  await client.finishAuth(code, oauthState);
3615
3837
  this.clients.set(sessionId, client);
3616
- const tools = await client.listTools();
3838
+ const { result: tools } = await this.listPolicyFilteredTools(sessionId);
3617
3839
  return { success: true, toolCount: tools.tools.length };
3618
3840
  } catch (error) {
3619
3841
  this.emitConnectionEvent({
@@ -3905,6 +4127,12 @@ var SSEClient = class {
3905
4127
  async disconnectFromServer(sessionId) {
3906
4128
  return this.sendRequest("disconnect", { sessionId });
3907
4129
  }
4130
+ async setToolPolicy(sessionId, toolPolicy) {
4131
+ return this.sendRequest("setToolPolicy", { sessionId, toolPolicy });
4132
+ }
4133
+ async getToolPolicy(sessionId) {
4134
+ return this.sendRequest("getToolPolicy", { sessionId });
4135
+ }
3908
4136
  async listTools(sessionId) {
3909
4137
  return this.sendRequest("listTools", { sessionId });
3910
4138
  }