@samanhappy/mcphub 1.0.28 → 1.0.30

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 (87) hide show
  1. package/README.fr.md +2 -1
  2. package/README.md +2 -1
  3. package/README.zh.md +2 -1
  4. package/dist/betterAuth.js +3 -6
  5. package/dist/betterAuth.js.map +1 -1
  6. package/dist/clients/openapi.js +194 -15
  7. package/dist/clients/openapi.js.map +1 -1
  8. package/dist/controllers/serverController.js +108 -16
  9. package/dist/controllers/serverController.js.map +1 -1
  10. package/dist/dao/ServerDao.js +3 -1
  11. package/dist/dao/ServerDao.js.map +1 -1
  12. package/dist/dao/ServerDaoDbImpl.js +3 -0
  13. package/dist/dao/ServerDaoDbImpl.js.map +1 -1
  14. package/dist/db/entities/Server.js +4 -0
  15. package/dist/db/entities/Server.js.map +1 -1
  16. package/dist/db/repositories/ServerRepository.js +1 -0
  17. package/dist/db/repositories/ServerRepository.js.map +1 -1
  18. package/dist/routes/index.js +2 -1
  19. package/dist/routes/index.js.map +1 -1
  20. package/dist/server.js +12 -9
  21. package/dist/server.js.map +1 -1
  22. package/dist/services/betterAuthConfig.js +4 -0
  23. package/dist/services/betterAuthConfig.js.map +1 -1
  24. package/dist/services/dataService.js +8 -3
  25. package/dist/services/dataService.js.map +1 -1
  26. package/dist/services/mcpOAuthProvider.js +13 -4
  27. package/dist/services/mcpOAuthProvider.js.map +1 -1
  28. package/dist/services/mcpService.js +47 -12
  29. package/dist/services/mcpService.js.map +1 -1
  30. package/dist/services/smartRoutingService.js +4 -3
  31. package/dist/services/smartRoutingService.js.map +1 -1
  32. package/dist/utils/migration.js +1 -0
  33. package/dist/utils/migration.js.map +1 -1
  34. package/dist/utils/serverConfigPersistence.js +10 -1
  35. package/dist/utils/serverConfigPersistence.js.map +1 -1
  36. package/dist/utils/serverShutdown.js +27 -0
  37. package/dist/utils/serverShutdown.js.map +1 -0
  38. package/frontend/dist/assets/{ActivityPage-D0SFqsRe.js → ActivityPage-U9om5C9X.js} +2 -2
  39. package/frontend/dist/assets/{ActivityPage-D0SFqsRe.js.map → ActivityPage-U9om5C9X.js.map} +1 -1
  40. package/frontend/dist/assets/{Dashboard-DzWkQnht.js → Dashboard-DGqpU4bd.js} +2 -2
  41. package/frontend/dist/assets/{Dashboard-DzWkQnht.js.map → Dashboard-DGqpU4bd.js.map} +1 -1
  42. package/frontend/dist/assets/{EndpointCopy-Bqjt3lGe.js → EndpointCopy-DuGmhiMq.js} +2 -2
  43. package/frontend/dist/assets/{EndpointCopy-Bqjt3lGe.js.map → EndpointCopy-DuGmhiMq.js.map} +1 -1
  44. package/frontend/dist/assets/{GroupsPage-DsmZMSK0.js → GroupsPage-Dhqy02FY.js} +2 -2
  45. package/frontend/dist/assets/{GroupsPage-DsmZMSK0.js.map → GroupsPage-Dhqy02FY.js.map} +1 -1
  46. package/frontend/dist/assets/{LoginPage-_Gr1ere-.js → LoginPage-BXRn47pC.js} +2 -2
  47. package/frontend/dist/assets/{LoginPage-_Gr1ere-.js.map → LoginPage-BXRn47pC.js.map} +1 -1
  48. package/frontend/dist/assets/{LogsPage-D2vByutC.js → LogsPage-CiO6LZEJ.js} +2 -2
  49. package/frontend/dist/assets/LogsPage-CiO6LZEJ.js.map +1 -0
  50. package/frontend/dist/assets/{MarketPage-BhyrxwBv.js → MarketPage-BEdWfj0u.js} +2 -2
  51. package/frontend/dist/assets/{MarketPage-BhyrxwBv.js.map → MarketPage-BEdWfj0u.js.map} +1 -1
  52. package/frontend/dist/assets/{PromptsPage-BLn95Icp.js → PromptsPage-BG6oWgAk.js} +2 -2
  53. package/frontend/dist/assets/{PromptsPage-BLn95Icp.js.map → PromptsPage-BG6oWgAk.js.map} +1 -1
  54. package/frontend/dist/assets/{ResourcesPage-B27GVOO6.js → ResourcesPage-BqJUKqnV.js} +2 -2
  55. package/frontend/dist/assets/{ResourcesPage-B27GVOO6.js.map → ResourcesPage-BqJUKqnV.js.map} +1 -1
  56. package/frontend/dist/assets/{ServersPage-BuoWpLAq.js → ServersPage-CHbXHEcG.js} +4 -4
  57. package/frontend/dist/assets/{ServersPage-BuoWpLAq.js.map → ServersPage-CHbXHEcG.js.map} +1 -1
  58. package/frontend/dist/assets/SettingsPage-B6yl-U9T.js +12 -0
  59. package/frontend/dist/assets/SettingsPage-B6yl-U9T.js.map +1 -0
  60. package/frontend/dist/assets/{StatusDot-BUWJw3dA.js → StatusDot-Bz7sBh0t.js} +2 -2
  61. package/frontend/dist/assets/{StatusDot-BUWJw3dA.js.map → StatusDot-Bz7sBh0t.js.map} +1 -1
  62. package/frontend/dist/assets/{ToggleGroup-BPAIOm5B.js → ToggleGroup-60voS_0U.js} +2 -2
  63. package/frontend/dist/assets/{ToggleGroup-BPAIOm5B.js.map → ToggleGroup-60voS_0U.js.map} +1 -1
  64. package/frontend/dist/assets/{UsersPage-SZrL0bKd.js → UsersPage-Dzzo9CC-.js} +2 -2
  65. package/frontend/dist/assets/{UsersPage-SZrL0bKd.js.map → UsersPage-Dzzo9CC-.js.map} +1 -1
  66. package/frontend/dist/assets/{contextCost-BxT09q-Y.js → contextCost-CIOdvJNu.js} +2 -2
  67. package/frontend/dist/assets/{contextCost-BxT09q-Y.js.map → contextCost-CIOdvJNu.js.map} +1 -1
  68. package/frontend/dist/assets/index-CGvImZ3F.css +1 -0
  69. package/frontend/dist/assets/index-Ca9HKIrV.js +3 -0
  70. package/frontend/dist/assets/index-Ca9HKIrV.js.map +1 -0
  71. package/frontend/dist/assets/{resourceService-CWKSpa0p.js → resourceService-DIIsi_li.js} +2 -2
  72. package/frontend/dist/assets/{resourceService-CWKSpa0p.js.map → resourceService-DIIsi_li.js.map} +1 -1
  73. package/frontend/dist/assets/useSettingsData-C84qj7ge.js +2 -0
  74. package/frontend/dist/assets/{useSettingsData-0sbahDtL.js.map → useSettingsData-C84qj7ge.js.map} +1 -1
  75. package/frontend/dist/assets/variableDetection-Y9ymMI80.js +16 -0
  76. package/frontend/dist/assets/variableDetection-Y9ymMI80.js.map +1 -0
  77. package/frontend/dist/index.html +2 -2
  78. package/package.json +4 -3
  79. package/frontend/dist/assets/LogsPage-D2vByutC.js.map +0 -1
  80. package/frontend/dist/assets/SettingsPage-BzCwUvx7.js +0 -12
  81. package/frontend/dist/assets/SettingsPage-BzCwUvx7.js.map +0 -1
  82. package/frontend/dist/assets/index-Bz_euXvO.js +0 -3
  83. package/frontend/dist/assets/index-Bz_euXvO.js.map +0 -1
  84. package/frontend/dist/assets/index-DEAy4k6z.css +0 -1
  85. package/frontend/dist/assets/useSettingsData-0sbahDtL.js +0 -2
  86. package/frontend/dist/assets/variableDetection-DfkBZ0gv.js +0 -16
  87. package/frontend/dist/assets/variableDetection-DfkBZ0gv.js.map +0 -1
@@ -200,7 +200,7 @@ export const initUpstreamServers = async () => {
200
200
  // Register all tools from upstream servers
201
201
  await registerAllTools(true);
202
202
  // Initialize smart routing service with references to mcpService functions
203
- initSmartRoutingService(() => serverInfos, filterToolsByConfig, filterToolsByGroup);
203
+ initSmartRoutingService(getVisibleServerInfos, filterToolsByConfig, filterToolsByGroup);
204
204
  };
205
205
  const getMcpServerDescriptor = async (group) => {
206
206
  if (!group || isSmartRoutingGroup(group)) {
@@ -288,6 +288,11 @@ const cleanupIsolatedSession = (sessionId) => {
288
288
  }
289
289
  console.log(`Cleaned up isolated clients for session: ${sessionId}`);
290
290
  }
291
+ // Drop per-session OpenAPI cookie jars so authenticated state does not
292
+ // outlive the downstream session.
293
+ for (const serverInfo of serverInfos) {
294
+ serverInfo.openApiClient?.clearSessionCookies?.(sessionId);
295
+ }
291
296
  };
292
297
  /** Helper to write an isolated session upstream client into the tracking map. */
293
298
  const setSessionIsolatedClient = (sessionId, serverName, client, transport) => {
@@ -392,16 +397,18 @@ export const broadcastResourceListChanged = () => {
392
397
  export const broadcastPromptListChanged = () => {
393
398
  broadcastListChanged('prompt', (server) => server.sendPromptListChanged());
394
399
  };
395
- export const updateServerInfoVisibility = (serverName, visibility) => {
400
+ export const updateServerInfoVisibility = (serverName, visibility, sharedWithUsers) => {
396
401
  const serverInfo = getServerByName(serverName);
397
402
  if (!serverInfo) {
398
403
  return;
399
404
  }
400
405
  serverInfo.visibility = visibility;
406
+ serverInfo.sharedWithUsers = sharedWithUsers;
401
407
  if (serverInfo.config) {
402
408
  serverInfo.config = {
403
409
  ...serverInfo.config,
404
410
  visibility,
411
+ sharedWithUsers,
405
412
  };
406
413
  }
407
414
  };
@@ -497,6 +504,12 @@ const normalizeResourceForCache = (resource) => {
497
504
  };
498
505
  // Store all server information
499
506
  let serverInfos = [];
507
+ const getVisibleServerInfos = () => {
508
+ return getDataService().filterData(serverInfos);
509
+ };
510
+ const getVisibleServerByName = (name) => {
511
+ return getVisibleServerInfos().find((serverInfo) => serverInfo.name === name);
512
+ };
500
513
  // Per-session upstream clients for isolated servers (perSessionClient: true).
501
514
  // Map<sessionId, Map<serverName, { client, transport }>>
502
515
  const sessionIsolatedClients = new Map();
@@ -1161,6 +1174,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1161
1174
  name,
1162
1175
  owner: expandedConf.owner,
1163
1176
  visibility: expandedConf.visibility,
1177
+ sharedWithUsers: expandedConf.sharedWithUsers,
1164
1178
  status: 'disconnected',
1165
1179
  error: null,
1166
1180
  tools: [],
@@ -1191,6 +1205,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1191
1205
  }),
1192
1206
  owner: expandedConf.owner,
1193
1207
  visibility: expandedConf.visibility,
1208
+ sharedWithUsers: expandedConf.sharedWithUsers,
1194
1209
  enabled: true,
1195
1210
  config: expandedConf,
1196
1211
  });
@@ -1217,6 +1232,9 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1217
1232
  (!serverName && (existingServer.status === 'connected' || hasInflightOAuthAuthorization)))) {
1218
1233
  nextServerInfos.push({
1219
1234
  ...existingServer,
1235
+ owner: expandedConf.owner,
1236
+ visibility: expandedConf.visibility,
1237
+ sharedWithUsers: expandedConf.sharedWithUsers,
1220
1238
  enabled: expandedConf.enabled === undefined ? true : expandedConf.enabled,
1221
1239
  });
1222
1240
  console.log(hasInflightOAuthAuthorization
@@ -1234,6 +1252,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1234
1252
  name,
1235
1253
  owner: expandedConf.owner,
1236
1254
  visibility: expandedConf.visibility,
1255
+ sharedWithUsers: expandedConf.sharedWithUsers,
1237
1256
  status: 'disconnected',
1238
1257
  error: 'Missing OpenAPI specification URL or schema',
1239
1258
  tools: [],
@@ -1248,6 +1267,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1248
1267
  name,
1249
1268
  owner: expandedConf.owner,
1250
1269
  visibility: expandedConf.visibility,
1270
+ sharedWithUsers: expandedConf.sharedWithUsers,
1251
1271
  status: 'connecting',
1252
1272
  error: null,
1253
1273
  tools: [],
@@ -1345,6 +1365,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1345
1365
  name,
1346
1366
  owner: expandedConf.owner,
1347
1367
  visibility: expandedConf.visibility,
1368
+ sharedWithUsers: expandedConf.sharedWithUsers,
1348
1369
  status: 'connecting',
1349
1370
  error: null,
1350
1371
  tools: [],
@@ -1525,6 +1546,7 @@ export const getServersInfo = async (page, limit, user) => {
1525
1546
  name: server.name,
1526
1547
  owner: server.owner,
1527
1548
  visibility: server.visibility,
1549
+ sharedWithUsers: server.sharedWithUsers,
1528
1550
  // Newly created servers that are enabled should appear as "connecting"
1529
1551
  // until the MCP client initialization completes. Disabled servers remain
1530
1552
  // in the "disconnected" state.
@@ -1713,7 +1735,7 @@ const filterToolsByConfig = async (serverName, tools) => {
1713
1735
  };
1714
1736
  // Get server by tool name
1715
1737
  const getServerByTool = (toolName) => {
1716
- return serverInfos.find((serverInfo) => serverInfo.enabled !== false && serverInfo.tools.some((tool) => tool.name === toolName));
1738
+ return getVisibleServerInfos().find((serverInfo) => serverInfo.enabled !== false && serverInfo.tools.some((tool) => tool.name === toolName));
1717
1739
  };
1718
1740
  // Add new server
1719
1741
  export const addServer = async (name, config) => {
@@ -1827,8 +1849,9 @@ const closeServerRuntime = (serverInfo) => {
1827
1849
  }
1828
1850
  console.log('Closed MCP server client and transport');
1829
1851
  };
1830
- // Close server client and transport
1831
- function closeServer(name) {
1852
+ // Close server client and transport (keeps the entry in serverInfos; the next
1853
+ // initializeClientsFromSettings rebuild drops it if it is no longer configured).
1854
+ export function closeServer(name) {
1832
1855
  const serverInfo = serverInfos.find((serverInfo) => serverInfo.name === name);
1833
1856
  if (serverInfo) {
1834
1857
  closeServerRuntime(serverInfo);
@@ -2337,6 +2360,18 @@ export const handleCallToolRequest = async (request, extra) => {
2337
2360
  const requestContextService = RequestContextService.getInstance();
2338
2361
  const bearerKeyContext = requestContextService.getBearerKeyContext();
2339
2362
  const sessionId = extra.sessionId || '';
2363
+ // For OpenAPI cookie-session isolation, use a real per-caller session id.
2364
+ // Direct API controllers fall back to shared synthetic ids ('api-session' /
2365
+ // 'openapi-session') when x-session-id is absent, which would leak cookies
2366
+ // across callers, so only accept an explicitly-provided session header or a
2367
+ // non-synthetic extra.sessionId. Fail-safe (undefined) otherwise.
2368
+ const isSyntheticSessionFallback = (id) => id === 'api-session' || id === 'openapi-session';
2369
+ const explicitXSessionId = extra?.headers?.['x-session-id'];
2370
+ const cookieSessionId = [
2371
+ requestContextService.getSessionId(),
2372
+ typeof explicitXSessionId === 'string' ? explicitXSessionId : undefined,
2373
+ typeof extra?.sessionId === 'string' ? extra.sessionId : undefined,
2374
+ ].find((id) => typeof id === 'string' && id.length > 0 && !isSyntheticSessionFallback(id));
2340
2375
  // Extract group and key info from request context (set by SSE/HTTP handlers)
2341
2376
  // Fallback to extra for backward compatibility (e.g., direct API calls)
2342
2377
  const group = requestContextService.getGroupContext() || extra?.group || getGroup(sessionId) || undefined;
@@ -2395,7 +2430,7 @@ export const handleCallToolRequest = async (request, extra) => {
2395
2430
  targetServerInfo = appsRouteContext.serverInfo;
2396
2431
  }
2397
2432
  else if (extra && extra.server) {
2398
- targetServerInfo = getServerByName(extra.server);
2433
+ targetServerInfo = getVisibleServerByName(extra.server);
2399
2434
  }
2400
2435
  else if (getGroupLookupName(group)) {
2401
2436
  const groupTool = await resolveToolInGroup(group, toolName, false);
@@ -2409,7 +2444,7 @@ export const handleCallToolRequest = async (request, extra) => {
2409
2444
  // Find the first server that has this tool.
2410
2445
  // On-demand servers may be sleeping (disconnected) but still advertise
2411
2446
  // their cached tool list — include them as candidates.
2412
- targetServerInfo = serverInfos.find((serverInfo) => serverInfo.enabled !== false &&
2447
+ targetServerInfo = getVisibleServerInfos().find((serverInfo) => serverInfo.enabled !== false &&
2413
2448
  (serverInfo.status === 'connected' || serverInfo.config?.startOnDemand === true) &&
2414
2449
  serverInfo.tools.some((tool) => tool.name === toolName));
2415
2450
  }
@@ -2472,7 +2507,7 @@ export const handleCallToolRequest = async (request, extra) => {
2472
2507
  }
2473
2508
  }
2474
2509
  await reserveHostedIfNeeded(targetServerInfo.name, cleanToolName);
2475
- const result = await openApiClient.callTool(cleanToolName, finalArgs, passthroughHeaders);
2510
+ const result = await openApiClient.callTool(cleanToolName, finalArgs, passthroughHeaders, false, cookieSessionId);
2476
2511
  await settleHostedIfNeeded({
2477
2512
  success: true,
2478
2513
  requestContent: finalArgs,
@@ -2638,7 +2673,7 @@ export const handleCallToolRequest = async (request, extra) => {
2638
2673
  }
2639
2674
  const finalArgs = request.params.arguments || {};
2640
2675
  await reserveHostedIfNeeded(serverInfo.name, cleanToolName);
2641
- const result = await openApiClient.callTool(cleanToolName, finalArgs, passthroughHeaders);
2676
+ const result = await openApiClient.callTool(cleanToolName, finalArgs, passthroughHeaders, false, cookieSessionId);
2642
2677
  await settleHostedIfNeeded({
2643
2678
  success: true,
2644
2679
  requestContent: finalArgs,
@@ -2790,7 +2825,7 @@ export const handleGetPromptRequest = async (request, extra) => {
2790
2825
  let promptNameForServer = name;
2791
2826
  const lookupGroup = getGroupLookupName(group);
2792
2827
  if (extra && extra.server) {
2793
- server = getServerByName(extra.server);
2828
+ server = getVisibleServerByName(extra.server);
2794
2829
  }
2795
2830
  else if (lookupGroup) {
2796
2831
  const groupPrompt = await resolvePromptInGroup(lookupGroup, name);
@@ -2801,7 +2836,7 @@ export const handleGetPromptRequest = async (request, extra) => {
2801
2836
  }
2802
2837
  else {
2803
2838
  // Find the first server that has this prompt
2804
- server = serverInfos.find((serverInfo) => serverInfo.status === 'connected' &&
2839
+ server = getVisibleServerInfos().find((serverInfo) => serverInfo.status === 'connected' &&
2805
2840
  serverInfo.enabled !== false &&
2806
2841
  serverInfo.prompts.find((prompt) => prompt.name === name));
2807
2842
  }
@@ -3108,7 +3143,7 @@ export const getFilteredServerInfosForGroup = async (group, options) => {
3108
3143
  const serverNamesInGroup = new Set(serverConfigs.map((serverConfig) => serverConfig.name));
3109
3144
  const serverConfigsByName = new Map(serverConfigs.map((serverConfig) => [serverConfig.name, serverConfig]));
3110
3145
  const filteredServerInfos = [];
3111
- for (const serverInfo of getDataService().filterData(serverInfos)) {
3146
+ for (const serverInfo of getVisibleServerInfos()) {
3112
3147
  if (serverInfo.enabled === false)
3113
3148
  continue;
3114
3149
  if (options?.requireClient && !serverInfo.client)