@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.
- package/README.fr.md +2 -1
- package/README.md +2 -1
- package/README.zh.md +2 -1
- package/dist/betterAuth.js +3 -6
- package/dist/betterAuth.js.map +1 -1
- package/dist/clients/openapi.js +194 -15
- package/dist/clients/openapi.js.map +1 -1
- package/dist/controllers/serverController.js +108 -16
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/dao/ServerDao.js +3 -1
- package/dist/dao/ServerDao.js.map +1 -1
- package/dist/dao/ServerDaoDbImpl.js +3 -0
- package/dist/dao/ServerDaoDbImpl.js.map +1 -1
- package/dist/db/entities/Server.js +4 -0
- package/dist/db/entities/Server.js.map +1 -1
- package/dist/db/repositories/ServerRepository.js +1 -0
- package/dist/db/repositories/ServerRepository.js.map +1 -1
- package/dist/routes/index.js +2 -1
- package/dist/routes/index.js.map +1 -1
- package/dist/server.js +12 -9
- package/dist/server.js.map +1 -1
- package/dist/services/betterAuthConfig.js +4 -0
- package/dist/services/betterAuthConfig.js.map +1 -1
- package/dist/services/dataService.js +8 -3
- package/dist/services/dataService.js.map +1 -1
- package/dist/services/mcpOAuthProvider.js +13 -4
- package/dist/services/mcpOAuthProvider.js.map +1 -1
- package/dist/services/mcpService.js +47 -12
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/smartRoutingService.js +4 -3
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/utils/migration.js +1 -0
- package/dist/utils/migration.js.map +1 -1
- package/dist/utils/serverConfigPersistence.js +10 -1
- package/dist/utils/serverConfigPersistence.js.map +1 -1
- package/dist/utils/serverShutdown.js +27 -0
- package/dist/utils/serverShutdown.js.map +1 -0
- package/frontend/dist/assets/{ActivityPage-D0SFqsRe.js → ActivityPage-U9om5C9X.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-D0SFqsRe.js.map → ActivityPage-U9om5C9X.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-DzWkQnht.js → Dashboard-DGqpU4bd.js} +2 -2
- package/frontend/dist/assets/{Dashboard-DzWkQnht.js.map → Dashboard-DGqpU4bd.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-Bqjt3lGe.js → EndpointCopy-DuGmhiMq.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-Bqjt3lGe.js.map → EndpointCopy-DuGmhiMq.js.map} +1 -1
- package/frontend/dist/assets/{GroupsPage-DsmZMSK0.js → GroupsPage-Dhqy02FY.js} +2 -2
- package/frontend/dist/assets/{GroupsPage-DsmZMSK0.js.map → GroupsPage-Dhqy02FY.js.map} +1 -1
- package/frontend/dist/assets/{LoginPage-_Gr1ere-.js → LoginPage-BXRn47pC.js} +2 -2
- package/frontend/dist/assets/{LoginPage-_Gr1ere-.js.map → LoginPage-BXRn47pC.js.map} +1 -1
- package/frontend/dist/assets/{LogsPage-D2vByutC.js → LogsPage-CiO6LZEJ.js} +2 -2
- package/frontend/dist/assets/LogsPage-CiO6LZEJ.js.map +1 -0
- package/frontend/dist/assets/{MarketPage-BhyrxwBv.js → MarketPage-BEdWfj0u.js} +2 -2
- package/frontend/dist/assets/{MarketPage-BhyrxwBv.js.map → MarketPage-BEdWfj0u.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-BLn95Icp.js → PromptsPage-BG6oWgAk.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-BLn95Icp.js.map → PromptsPage-BG6oWgAk.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-B27GVOO6.js → ResourcesPage-BqJUKqnV.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-B27GVOO6.js.map → ResourcesPage-BqJUKqnV.js.map} +1 -1
- package/frontend/dist/assets/{ServersPage-BuoWpLAq.js → ServersPage-CHbXHEcG.js} +4 -4
- package/frontend/dist/assets/{ServersPage-BuoWpLAq.js.map → ServersPage-CHbXHEcG.js.map} +1 -1
- package/frontend/dist/assets/SettingsPage-B6yl-U9T.js +12 -0
- package/frontend/dist/assets/SettingsPage-B6yl-U9T.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-BUWJw3dA.js → StatusDot-Bz7sBh0t.js} +2 -2
- package/frontend/dist/assets/{StatusDot-BUWJw3dA.js.map → StatusDot-Bz7sBh0t.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-BPAIOm5B.js → ToggleGroup-60voS_0U.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-BPAIOm5B.js.map → ToggleGroup-60voS_0U.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-SZrL0bKd.js → UsersPage-Dzzo9CC-.js} +2 -2
- package/frontend/dist/assets/{UsersPage-SZrL0bKd.js.map → UsersPage-Dzzo9CC-.js.map} +1 -1
- package/frontend/dist/assets/{contextCost-BxT09q-Y.js → contextCost-CIOdvJNu.js} +2 -2
- package/frontend/dist/assets/{contextCost-BxT09q-Y.js.map → contextCost-CIOdvJNu.js.map} +1 -1
- package/frontend/dist/assets/index-CGvImZ3F.css +1 -0
- package/frontend/dist/assets/index-Ca9HKIrV.js +3 -0
- package/frontend/dist/assets/index-Ca9HKIrV.js.map +1 -0
- package/frontend/dist/assets/{resourceService-CWKSpa0p.js → resourceService-DIIsi_li.js} +2 -2
- package/frontend/dist/assets/{resourceService-CWKSpa0p.js.map → resourceService-DIIsi_li.js.map} +1 -1
- package/frontend/dist/assets/useSettingsData-C84qj7ge.js +2 -0
- package/frontend/dist/assets/{useSettingsData-0sbahDtL.js.map → useSettingsData-C84qj7ge.js.map} +1 -1
- package/frontend/dist/assets/variableDetection-Y9ymMI80.js +16 -0
- package/frontend/dist/assets/variableDetection-Y9ymMI80.js.map +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +4 -3
- package/frontend/dist/assets/LogsPage-D2vByutC.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-BzCwUvx7.js +0 -12
- package/frontend/dist/assets/SettingsPage-BzCwUvx7.js.map +0 -1
- package/frontend/dist/assets/index-Bz_euXvO.js +0 -3
- package/frontend/dist/assets/index-Bz_euXvO.js.map +0 -1
- package/frontend/dist/assets/index-DEAy4k6z.css +0 -1
- package/frontend/dist/assets/useSettingsData-0sbahDtL.js +0 -2
- package/frontend/dist/assets/variableDetection-DfkBZ0gv.js +0 -16
- 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(
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
3146
|
+
for (const serverInfo of getVisibleServerInfos()) {
|
|
3112
3147
|
if (serverInfo.enabled === false)
|
|
3113
3148
|
continue;
|
|
3114
3149
|
if (options?.requireClient && !serverInfo.client)
|