@mcp-ts/sdk 2.4.5 → 2.5.1

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 (86) 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 +9 -0
  28. package/dist/client/index.js.map +1 -1
  29. package/dist/client/index.mjs +9 -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 +56 -37
  34. package/dist/client/react.js.map +1 -1
  35. package/dist/client/react.mjs +56 -37
  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 +9 -0
  40. package/dist/client/vue.js.map +1 -1
  41. package/dist/client/vue.mjs +9 -0
  42. package/dist/client/vue.mjs.map +1 -1
  43. package/dist/{index-CZk2gu2E.d.ts → index-BEdyuz1M.d.ts} +6 -1
  44. package/dist/{index-CkM3p0eE.d.mts → index-C99wE2Zf.d.mts} +6 -1
  45. package/dist/index.d.mts +4 -4
  46. package/dist/index.d.ts +4 -4
  47. package/dist/index.js +380 -82
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +376 -82
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/{multi-session-client-QOOPdEVp.d.ts → multi-session-client-CclWRxTD.d.mts} +14 -2
  52. package/dist/{multi-session-client-ChQrBZhF.d.mts → multi-session-client-DzvZD6Tt.d.ts} +14 -2
  53. package/dist/server/index.d.mts +18 -4
  54. package/dist/server/index.d.ts +18 -4
  55. package/dist/server/index.js +371 -82
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/index.mjs +367 -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-CGs3IDOJ.d.mts → tool-router-C9ECn3FX.d.mts} +1 -1
  64. package/dist/{tool-router-DQ-HrD7W.d.ts → tool-router-CUSSk6lT.d.ts} +1 -1
  65. package/dist/{types-Bf-7GOLW.d.mts → types-CbFbPkfQ.d.mts} +38 -3
  66. package/dist/{types-Bf-7GOLW.d.ts → types-CbFbPkfQ.d.ts} +38 -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 +2 -2
  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 +21 -0
  75. package/src/client/react/oauth-popup.tsx +17 -24
  76. package/src/client/react/use-mcp.ts +69 -15
  77. package/src/server/handlers/sse-handler.ts +202 -9
  78. package/src/server/mcp/multi-session-client.ts +7 -3
  79. package/src/server/mcp/oauth-client.ts +25 -10
  80. package/src/server/mcp/tool-policy-gateway.ts +90 -0
  81. package/src/server/storage/neon-backend.ts +65 -60
  82. package/src/server/storage/session-lifecycle.ts +5 -0
  83. package/src/server/storage/supabase-backend.ts +27 -15
  84. package/src/server/storage/tool-policy.ts +89 -0
  85. package/src/server/storage/types.ts +11 -0
  86. package/src/shared/types.ts +52 -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.
@@ -3397,12 +3540,21 @@ var SSEConnectionManager = class {
3397
3540
  case "connect":
3398
3541
  result = await this.connect(request.params);
3399
3542
  break;
3543
+ case "reconnect":
3544
+ result = await this.reconnect(request.params);
3545
+ break;
3400
3546
  case "disconnect":
3401
3547
  result = await this.disconnect(request.params);
3402
3548
  break;
3403
3549
  case "listTools":
3404
3550
  result = await this.listTools(request.params);
3405
3551
  break;
3552
+ case "setToolPolicy":
3553
+ result = await this.setToolPolicy(request.params);
3554
+ break;
3555
+ case "getToolPolicy":
3556
+ result = await this.getToolPolicy(request.params);
3557
+ break;
3406
3558
  case "callTool":
3407
3559
  result = await this.callTool(request.params);
3408
3560
  break;
@@ -3459,7 +3611,8 @@ var SSEConnectionManager = class {
3459
3611
  transport: s.transportType,
3460
3612
  createdAt: s.createdAt,
3461
3613
  updatedAt: s.updatedAt ?? s.createdAt,
3462
- status: s.status ?? "pending"
3614
+ status: s.status ?? "pending",
3615
+ toolPolicy: s.toolPolicy
3463
3616
  }))
3464
3617
  };
3465
3618
  }
@@ -3507,7 +3660,7 @@ var SSEConnectionManager = class {
3507
3660
  this.sendEvent(event);
3508
3661
  });
3509
3662
  await client.connect();
3510
- await client.listTools();
3663
+ await this.listPolicyFilteredTools(sessionId);
3511
3664
  return {
3512
3665
  sessionId,
3513
3666
  success: true
@@ -3532,6 +3685,66 @@ var SSEConnectionManager = class {
3532
3685
  throw error;
3533
3686
  }
3534
3687
  }
3688
+ /**
3689
+ * Reconnect to an MCP server — tears down the active client transport/connection
3690
+ * and creates a fresh connection while reusing the existing session credentials in a single RPC call.
3691
+ */
3692
+ async reconnect(params) {
3693
+ const { serverId: rawServerId, serverName, serverUrl, callbackUrl, transportType } = params;
3694
+ const headers = normalizeHeaders(params.headers);
3695
+ const serverId = rawServerId && rawServerId.length <= 12 ? rawServerId : generateServerId();
3696
+ const existingSessions = await sessions.list(this.userId);
3697
+ const duplicate = existingSessions.find(
3698
+ (s) => s.serverId === serverId || s.serverUrl === serverUrl
3699
+ );
3700
+ const sessionId = duplicate ? duplicate.sessionId : await sessions.generateSessionId();
3701
+ if (duplicate) {
3702
+ const existingClient = this.clients.get(duplicate.sessionId);
3703
+ if (existingClient) {
3704
+ await existingClient.disconnect();
3705
+ this.clients.delete(duplicate.sessionId);
3706
+ }
3707
+ }
3708
+ try {
3709
+ const clientMetadata = await this.getResolvedClientMetadata();
3710
+ const client = new MCPClient({
3711
+ userId: this.userId,
3712
+ sessionId,
3713
+ serverId,
3714
+ serverName,
3715
+ serverUrl,
3716
+ callbackUrl,
3717
+ transportType,
3718
+ headers,
3719
+ ...clientMetadata
3720
+ });
3721
+ this.clients.set(sessionId, client);
3722
+ client.onConnectionEvent((event) => {
3723
+ this.emitConnectionEvent(event);
3724
+ });
3725
+ client.onObservabilityEvent((event) => {
3726
+ this.sendEvent(event);
3727
+ });
3728
+ await client.connect();
3729
+ await this.listPolicyFilteredTools(sessionId);
3730
+ return { sessionId, success: true };
3731
+ } catch (error) {
3732
+ if (error instanceof UnauthorizedError) {
3733
+ this.clients.delete(sessionId);
3734
+ return { sessionId, success: true };
3735
+ }
3736
+ this.emitConnectionEvent({
3737
+ type: "error",
3738
+ sessionId,
3739
+ serverId,
3740
+ error: error instanceof Error ? error.message : "Connection failed",
3741
+ errorType: "connection",
3742
+ timestamp: Date.now()
3743
+ });
3744
+ this.clients.delete(sessionId);
3745
+ throw error;
3746
+ }
3747
+ }
3535
3748
  /**
3536
3749
  * Disconnect from an MCP server
3537
3750
  */
@@ -3578,22 +3791,98 @@ var SSEConnectionManager = class {
3578
3791
  this.clients.set(sessionId, client);
3579
3792
  return client;
3580
3793
  }
3794
+ async listPolicyFilteredTools(sessionId) {
3795
+ const session = await sessions.get(this.userId, sessionId);
3796
+ if (!session) {
3797
+ throw new Error("Session not found");
3798
+ }
3799
+ const client = await this.getOrCreateClient(sessionId);
3800
+ const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3801
+ const result = await gateway.listTools();
3802
+ this.emitConnectionEvent({
3803
+ type: "tools_discovered",
3804
+ sessionId,
3805
+ serverId: session.serverId ?? "unknown",
3806
+ toolCount: result.tools.length,
3807
+ tools: result.tools,
3808
+ timestamp: Date.now()
3809
+ });
3810
+ return { session, result };
3811
+ }
3581
3812
  /**
3582
3813
  * List tools from a session
3583
3814
  */
3584
3815
  async listTools(params) {
3585
3816
  const { sessionId } = params;
3586
- const client = await this.getOrCreateClient(sessionId);
3587
- const result = await client.listTools();
3817
+ const { result } = await this.listPolicyFilteredTools(sessionId);
3588
3818
  return { tools: result.tools };
3589
3819
  }
3820
+ /**
3821
+ * Get all raw tools with effective access state for management UI.
3822
+ */
3823
+ async getToolPolicy(params) {
3824
+ const { sessionId } = params;
3825
+ const session = await sessions.get(this.userId, sessionId);
3826
+ if (!session) {
3827
+ throw new Error("Session not found");
3828
+ }
3829
+ const client = await this.getOrCreateClient(sessionId);
3830
+ const allTools = await client.fetchTools();
3831
+ const toolPolicy = session.toolPolicy ?? {
3832
+ mode: "all",
3833
+ toolIds: [],
3834
+ updatedAt: session.updatedAt ?? session.createdAt
3835
+ };
3836
+ const serverId = session.serverId ?? "unknown";
3837
+ const tools = allTools.tools.map((tool) => ({
3838
+ ...tool,
3839
+ toolId: createToolId(serverId, tool.name),
3840
+ allowed: isToolAllowed(toolPolicy, tool.name, session.serverId)
3841
+ }));
3842
+ return {
3843
+ toolPolicy,
3844
+ tools,
3845
+ toolCount: tools.length,
3846
+ allowedToolCount: tools.filter((tool) => tool.allowed).length
3847
+ };
3848
+ }
3849
+ /**
3850
+ * Update per-session tool access policy.
3851
+ */
3852
+ async setToolPolicy(params) {
3853
+ const { sessionId } = params;
3854
+ const session = await sessions.get(this.userId, sessionId);
3855
+ if (!session) {
3856
+ throw new Error("Session not found");
3857
+ }
3858
+ const client = await this.getOrCreateClient(sessionId);
3859
+ const allTools = await client.fetchTools();
3860
+ const toolPolicy = normalizeToolPolicyForUpdate(params.toolPolicy);
3861
+ validateToolPolicyAgainstTools(toolPolicy, allTools.tools, session.serverId);
3862
+ await sessions.update(this.userId, sessionId, { toolPolicy });
3863
+ const filteredTools = createToolPolicyGateway(this.userId, sessionId, client).filterTools({ ...session, toolPolicy }, allTools.tools);
3864
+ this.emitConnectionEvent({
3865
+ type: "tools_discovered",
3866
+ sessionId,
3867
+ serverId: session.serverId ?? "unknown",
3868
+ toolCount: filteredTools.length,
3869
+ tools: filteredTools,
3870
+ timestamp: Date.now()
3871
+ });
3872
+ return {
3873
+ success: true,
3874
+ toolPolicy,
3875
+ tools: filteredTools,
3876
+ toolCount: filteredTools.length
3877
+ };
3878
+ }
3590
3879
  /**
3591
3880
  * Call a tool on the MCP server
3592
3881
  */
3593
3882
  async callTool(params) {
3594
3883
  const { sessionId, toolName, toolArgs } = params;
3595
3884
  const client = await this.getOrCreateClient(sessionId);
3596
- const result = await client.callTool(toolName, toolArgs);
3885
+ const result = await createToolPolicyGateway(this.userId, sessionId, client).callTool(toolName, toolArgs);
3597
3886
  const meta = result._meta || {};
3598
3887
  return {
3599
3888
  ...result,
@@ -3643,7 +3932,7 @@ var SSEConnectionManager = class {
3643
3932
  client.onObservabilityEvent((event) => this.sendEvent(event));
3644
3933
  await client.connect();
3645
3934
  this.clients.set(sessionId, client);
3646
- const tools = await client.listTools();
3935
+ const { result: tools } = await this.listPolicyFilteredTools(sessionId);
3647
3936
  return { success: true, toolCount: tools.tools.length };
3648
3937
  } catch (error) {
3649
3938
  this.emitConnectionEvent({
@@ -3691,7 +3980,7 @@ var SSEConnectionManager = class {
3691
3980
  client.onConnectionEvent((event) => this.emitConnectionEvent(event));
3692
3981
  await client.finishAuth(code, oauthState);
3693
3982
  this.clients.set(sessionId, client);
3694
- const tools = await client.listTools();
3983
+ const { result: tools } = await this.listPolicyFilteredTools(sessionId);
3695
3984
  return { success: true, toolCount: tools.tools.length };
3696
3985
  } catch (error) {
3697
3986
  this.emitConnectionEvent({
@@ -3990,6 +4279,15 @@ var SSEClient = class {
3990
4279
  async disconnectFromServer(sessionId) {
3991
4280
  return this.sendRequest("disconnect", { sessionId });
3992
4281
  }
4282
+ async reconnectToServer(params) {
4283
+ return this.sendRequest("reconnect", params);
4284
+ }
4285
+ async setToolPolicy(sessionId, toolPolicy) {
4286
+ return this.sendRequest("setToolPolicy", { sessionId, toolPolicy });
4287
+ }
4288
+ async getToolPolicy(sessionId) {
4289
+ return this.sendRequest("getToolPolicy", { sessionId });
4290
+ }
3993
4291
  async listTools(sessionId) {
3994
4292
  return this.sendRequest("listTools", { sessionId });
3995
4293
  }