@samanhappy/mcphub 1.1.0 → 1.1.2
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/dist/betterAuth.js +5 -0
- package/dist/betterAuth.js.map +1 -1
- package/dist/clients/legacyMcpClient.js +1 -1
- package/dist/clients/legacyMcpClient.js.map +1 -1
- package/dist/clients/mcpSdkClient.js +28359 -0
- package/dist/clients/mcpSdkClient.js.map +7 -0
- package/dist/config/index.js +13 -7
- package/dist/config/index.js.map +1 -1
- package/dist/constants/smartRoutingDefaults.js +23 -0
- package/dist/constants/smartRoutingDefaults.js.map +1 -0
- package/dist/controllers/authController.js +5 -0
- package/dist/controllers/authController.js.map +1 -1
- package/dist/controllers/betterAuthController.js +3 -1
- package/dist/controllers/betterAuthController.js.map +1 -1
- package/dist/controllers/serverController.js +99 -18
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/dao/ServerDao.js.map +1 -1
- package/dist/dao/ServerDaoDbImpl.js +13 -1
- package/dist/dao/ServerDaoDbImpl.js.map +1 -1
- package/dist/dao/UserDao.js +57 -34
- package/dist/dao/UserDao.js.map +1 -1
- package/dist/dao/base/JsonFileBaseDao.js +11 -6
- package/dist/dao/base/JsonFileBaseDao.js.map +1 -1
- package/dist/db/entities/Server.js +4 -0
- package/dist/db/entities/Server.js.map +1 -1
- package/dist/middlewares/auth.js +17 -0
- package/dist/middlewares/auth.js.map +1 -1
- package/dist/routes/index.js +3 -5
- package/dist/routes/index.js.map +1 -1
- package/dist/services/betterAuthConfig.js +18 -95
- package/dist/services/betterAuthConfig.js.map +1 -1
- package/dist/services/betterAuthSession.js +19 -3
- package/dist/services/betterAuthSession.js.map +1 -1
- package/dist/services/credentialBindingService.js +42 -0
- package/dist/services/credentialBindingService.js.map +1 -1
- package/dist/services/forwardAuthService.js +277 -0
- package/dist/services/forwardAuthService.js.map +1 -0
- package/dist/services/keepAliveService.js +1 -1
- package/dist/services/keepAliveService.js.map +1 -1
- package/dist/services/mcpOAuthProvider.js +24 -0
- package/dist/services/mcpOAuthProvider.js.map +1 -1
- package/dist/services/mcpService.js +51 -15
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/oauthSettingsStore.js +1 -1
- package/dist/services/oauthSettingsStore.js.map +1 -1
- package/dist/services/smartRoutingService.js +54 -52
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/services/sseService.js +20 -0
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/templateService.js +10 -9
- package/dist/services/templateService.js.map +1 -1
- package/dist/utils/migration.js +8 -6
- package/dist/utils/migration.js.map +1 -1
- package/dist/utils/serverAvailability.js +9 -0
- package/dist/utils/serverAvailability.js.map +1 -0
- package/dist/utils/serverConfigPersistence.js +12 -1
- package/dist/utils/serverConfigPersistence.js.map +1 -1
- package/dist/utils/serverConfigValidation.js +5 -1
- package/dist/utils/serverConfigValidation.js.map +1 -1
- package/dist/utils/settingResolvers.js +97 -0
- package/dist/utils/settingResolvers.js.map +1 -0
- package/dist/utils/settingsSync.js +198 -0
- package/dist/utils/settingsSync.js.map +1 -0
- package/dist/utils/smartRouting.js +36 -0
- package/dist/utils/smartRouting.js.map +1 -1
- package/dist/utils/ssrf.js +1 -1
- package/dist/utils/ssrf.js.map +1 -1
- package/dist/utils/stdioOptions.js +11 -0
- package/dist/utils/stdioOptions.js.map +1 -0
- package/frontend/dist/assets/{ActivityPage-Dp0qfFJ7.js → ActivityPage-DFLMe7o2.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-Dp0qfFJ7.js.map → ActivityPage-DFLMe7o2.js.map} +1 -1
- package/frontend/dist/assets/{CopyClientConfigDialog-Bj0FEskD.js → CopyClientConfigDialog-DukyyXVp.js} +2 -2
- package/frontend/dist/assets/{CopyClientConfigDialog-Bj0FEskD.js.map → CopyClientConfigDialog-DukyyXVp.js.map} +1 -1
- package/frontend/dist/assets/{CredentialsPage-C-yB8aQU.js → CredentialsPage-BpBCEmEE.js} +2 -2
- package/frontend/dist/assets/{CredentialsPage-C-yB8aQU.js.map → CredentialsPage-BpBCEmEE.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-cbalSWB1.js → Dashboard-DjZkbAPt.js} +2 -2
- package/frontend/dist/assets/{Dashboard-cbalSWB1.js.map → Dashboard-DjZkbAPt.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-V8MoW7SG.js → EndpointCopy-Bv-_FPmR.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-V8MoW7SG.js.map → EndpointCopy-Bv-_FPmR.js.map} +1 -1
- package/frontend/dist/assets/{GroupsPage-DTXuiuqc.js → GroupsPage-B8BWRIWv.js} +2 -2
- package/frontend/dist/assets/{GroupsPage-DTXuiuqc.js.map → GroupsPage-B8BWRIWv.js.map} +1 -1
- package/frontend/dist/assets/LoginPage-OH73zcbT.js +2 -0
- package/frontend/dist/assets/LoginPage-OH73zcbT.js.map +1 -0
- package/frontend/dist/assets/{LogsPage-Bwv0shjb.js → LogsPage-B6P_uQvL.js} +2 -2
- package/frontend/dist/assets/{LogsPage-Bwv0shjb.js.map → LogsPage-B6P_uQvL.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-3jMYsH4H.js → MarketPage-CKjkgZpe.js} +2 -2
- package/frontend/dist/assets/{MarketPage-3jMYsH4H.js.map → MarketPage-CKjkgZpe.js.map} +1 -1
- package/frontend/dist/assets/{OAuthConsentPage-DI-IQ2gj.js → OAuthConsentPage-DjvtbLnm.js} +2 -2
- package/frontend/dist/assets/{OAuthConsentPage-DI-IQ2gj.js.map → OAuthConsentPage-DjvtbLnm.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-BuVTifDV.js → PromptsPage-CCMb9OH2.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-BuVTifDV.js.map → PromptsPage-CCMb9OH2.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-COSY92pD.js → ResourcesPage-BERaKn0d.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-COSY92pD.js.map → ResourcesPage-BERaKn0d.js.map} +1 -1
- package/frontend/dist/assets/{ServersPage-BlZvDfZE.js → ServersPage-BNsFiZez.js} +4 -4
- package/frontend/dist/assets/{ServersPage-BlZvDfZE.js.map → ServersPage-BNsFiZez.js.map} +1 -1
- package/frontend/dist/assets/SettingsPage-PiL7o9b9.js +12 -0
- package/frontend/dist/assets/SettingsPage-PiL7o9b9.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-BHfWZAic.js → StatusDot-IWYCjv_t.js} +2 -2
- package/frontend/dist/assets/{StatusDot-BHfWZAic.js.map → StatusDot-IWYCjv_t.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-C33UH_Ob.js → ToggleGroup-B_NGGU8p.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-C33UH_Ob.js.map → ToggleGroup-B_NGGU8p.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-BXjVPFoF.js → UsersPage-D9QdH-ma.js} +2 -2
- package/frontend/dist/assets/{UsersPage-BXjVPFoF.js.map → UsersPage-D9QdH-ma.js.map} +1 -1
- package/frontend/dist/assets/{clipboard-r7a9vj44.js → clipboard-CFeeSZ4D.js} +2 -2
- package/frontend/dist/assets/{clipboard-r7a9vj44.js.map → clipboard-CFeeSZ4D.js.map} +1 -1
- package/frontend/dist/assets/index--NhF_yyy.css +1 -0
- package/frontend/dist/assets/index-RTKBtgGw.js +3 -0
- package/frontend/dist/assets/index-RTKBtgGw.js.map +1 -0
- package/frontend/dist/assets/{resourceService-5J2x7MrD.js → resourceService-BoJqsssR.js} +2 -2
- package/frontend/dist/assets/{resourceService-5J2x7MrD.js.map → resourceService-BoJqsssR.js.map} +1 -1
- package/frontend/dist/assets/{useCostData-CjmWUnd9.js → useCostData-Dld-MisV.js} +2 -2
- package/frontend/dist/assets/{useCostData-CjmWUnd9.js.map → useCostData-Dld-MisV.js.map} +1 -1
- package/frontend/dist/assets/variableDetection-C1abuc-p.js +16 -0
- package/frontend/dist/assets/variableDetection-C1abuc-p.js.map +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +5 -3
- package/frontend/dist/assets/LoginPage-kfbiuyWa.js +0 -2
- package/frontend/dist/assets/LoginPage-kfbiuyWa.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-V0TrX4k8.js +0 -12
- package/frontend/dist/assets/SettingsPage-V0TrX4k8.js.map +0 -1
- package/frontend/dist/assets/index-D-J2WUmj.css +0 -1
- package/frontend/dist/assets/index-pouDG4uY.js +0 -3
- package/frontend/dist/assets/index-pouDG4uY.js.map +0 -1
- package/frontend/dist/assets/variableDetection-CAv2g3qV.js +0 -16
- package/frontend/dist/assets/variableDetection-CAv2g3qV.js.map +0 -1
|
@@ -5,6 +5,7 @@ import { LegacyMcpClient } from '../clients/legacyMcpClient.js';
|
|
|
5
5
|
import { LEGACY_PROTOCOL_VERSIONS } from '../utils/mcpProtocol.js';
|
|
6
6
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
7
7
|
import { canAccessGroupRoute } from '../utils/groupAccess.js';
|
|
8
|
+
import { canServeToolRequests } from '../utils/serverAvailability.js';
|
|
8
9
|
import { PrincipalRuntimeService } from './principalRuntimeService.js';
|
|
9
10
|
import { UserContextService } from './userContextService.js';
|
|
10
11
|
import { deleteCredentialBindings, missingCredentialError } from './credentialBindingService.js';
|
|
@@ -15,7 +16,7 @@ import fs from 'fs';
|
|
|
15
16
|
import treeKill from 'tree-kill';
|
|
16
17
|
import { isProcessTreeKillAvailable } from '../utils/processTree.js';
|
|
17
18
|
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
|
|
18
|
-
import { SSEClientTransport, StreamableHTTPClientTransport, } from '
|
|
19
|
+
import { SSEClientTransport, StreamableHTTPClientTransport, } from '../clients/mcpSdkClient.js';
|
|
19
20
|
import { Server } from '@modelcontextprotocol/server';
|
|
20
21
|
import { createFetchWithProxy, getProxyConfigFromEnv } from './proxy.js';
|
|
21
22
|
import { assertSafeUrl, createRedirectValidatingFetch } from '../utils/ssrf.js';
|
|
@@ -24,6 +25,7 @@ import { ResilientJsonSchemaValidator } from '../utils/jsonSchemaValidator.js';
|
|
|
24
25
|
import { getUserDao } from '../dao/index.js';
|
|
25
26
|
import { expandEnvVars, replaceEnvVars, getNameSeparator } from '../config/index.js';
|
|
26
27
|
import config from '../config/index.js';
|
|
28
|
+
import { validateMaxBufferSize } from '../utils/stdioOptions.js';
|
|
27
29
|
import { validateServerName } from '../utils/serverNameValidation.js';
|
|
28
30
|
import { getGroup } from './sseService.js';
|
|
29
31
|
import { getServerConfigInGroup, normalizeGroupServers } from './groupService.js';
|
|
@@ -1293,6 +1295,7 @@ export const createTransportFromConfig = async (name, conf) => {
|
|
|
1293
1295
|
args: finalArgs,
|
|
1294
1296
|
env: env,
|
|
1295
1297
|
stderr: 'pipe',
|
|
1298
|
+
maxBufferSize: validateMaxBufferSize(conf.options?.maxBufferSize),
|
|
1296
1299
|
});
|
|
1297
1300
|
if (transport.stderr && hasCredentialTemplate(conf)) {
|
|
1298
1301
|
transport.stderr.on('data', () => undefined); // Personal upstream diagnostics can contain raw credentials.
|
|
@@ -1390,6 +1393,7 @@ const callToolWithReconnect = async (serverInfo, toolParams, options, maxRetries
|
|
|
1390
1393
|
serverInfo.client = newClient;
|
|
1391
1394
|
serverInfo.transport = newTransport;
|
|
1392
1395
|
serverInfo.status = 'connected';
|
|
1396
|
+
setupServerKeepAlive(serverInfo, server);
|
|
1393
1397
|
}
|
|
1394
1398
|
// Point the local refs at the new connection for the next attempt.
|
|
1395
1399
|
client = newClient;
|
|
@@ -2333,6 +2337,7 @@ const classifyUnavailableReason = (qualifiedName) => {
|
|
|
2333
2337
|
};
|
|
2334
2338
|
// Add new server
|
|
2335
2339
|
export const addServer = async (name, config) => {
|
|
2340
|
+
validateMaxBufferSize(config.options?.maxBufferSize);
|
|
2336
2341
|
validateCredentialTemplate(config);
|
|
2337
2342
|
const server = { name, ...config };
|
|
2338
2343
|
const result = await getServerDao().create(server);
|
|
@@ -2367,6 +2372,7 @@ export const removeServer = async (name) => {
|
|
|
2367
2372
|
// Add or update server (supports overriding existing servers for MCPB)
|
|
2368
2373
|
export const addOrUpdateServer = async (name, config, allowOverride = false) => {
|
|
2369
2374
|
try {
|
|
2375
|
+
validateMaxBufferSize(config.options?.maxBufferSize);
|
|
2370
2376
|
validateCredentialTemplate(config);
|
|
2371
2377
|
const exists = await getServerDao().exists(name);
|
|
2372
2378
|
if (exists && !allowOverride) {
|
|
@@ -2664,12 +2670,19 @@ const primeOnDemandServers = (gate) => {
|
|
|
2664
2670
|
const run = gate ? gate.run : (start) => start();
|
|
2665
2671
|
return Promise.allSettled(targets.map(async (si) => {
|
|
2666
2672
|
try {
|
|
2667
|
-
await run(() => ensureServerReady(si)
|
|
2673
|
+
await run(() => ensureServerReady(si),
|
|
2674
|
+
// A reload may replace this runtime or finish its discovery while it
|
|
2675
|
+
// waits for a slot. Neither needs another child from the old queue.
|
|
2676
|
+
() => serverInfos.includes(si) && si.enabled !== false && si.tools.length === 0);
|
|
2668
2677
|
// Sleep the child unless a tool call is using it; keep the cached tool list.
|
|
2669
2678
|
shutdownOnDemandServer(si);
|
|
2670
2679
|
logger.log('On-demand server tool cache primed');
|
|
2671
2680
|
}
|
|
2672
2681
|
catch (error) {
|
|
2682
|
+
if (error instanceof QueuedConnectCancelledError) {
|
|
2683
|
+
logger.log('Cancelled queued on-demand discovery', { serverName: si.name });
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2673
2686
|
logger.warn('Failed to prime on-demand server', {
|
|
2674
2687
|
error: summarizeErrorForLogging(error),
|
|
2675
2688
|
});
|
|
@@ -2837,9 +2850,21 @@ const getMcpAppsRouteContext = async (sessionId, group) => {
|
|
|
2837
2850
|
: sessionId
|
|
2838
2851
|
? servers[sessionId]?.getClientCapabilities()
|
|
2839
2852
|
: undefined;
|
|
2840
|
-
if (
|
|
2853
|
+
if (!hasMcpAppsCapability(clientCapabilities)) {
|
|
2841
2854
|
return { enabled: false };
|
|
2842
2855
|
}
|
|
2856
|
+
if (isSmartRoutingGroup(group)) {
|
|
2857
|
+
// Only pinned tools are listed under their own descriptor, so only the servers
|
|
2858
|
+
// that pin something can own a widget here. Always the multi-server shape:
|
|
2859
|
+
// names stay qualified and calls resolve through the group, so a pin never
|
|
2860
|
+
// widens what the group allows.
|
|
2861
|
+
const pinned = await resolvePinnedSmartRoutingServers(group);
|
|
2862
|
+
// An idle on-demand server still advertises its cached tools, so it keeps its
|
|
2863
|
+
// widget link: dropping it here would hand the host a descriptor without one
|
|
2864
|
+
// that the next call, which wakes the server, can no longer correct.
|
|
2865
|
+
const eligible = (pinned?.pinnedServerInfos ?? []).filter(canServeToolRequests);
|
|
2866
|
+
return eligible.length > 0 ? { enabled: true, serverInfos: eligible } : { enabled: false };
|
|
2867
|
+
}
|
|
2843
2868
|
const { filteredServerInfos } = await getFilteredServerInfosForGroup(group);
|
|
2844
2869
|
const connectedServerInfos = filteredServerInfos.filter((serverInfo) => serverInfo.status === 'connected' && !!serverInfo.client);
|
|
2845
2870
|
if (connectedServerInfos.length === 0) {
|
|
@@ -2938,11 +2963,8 @@ const resolveToolInGroup = async (group, toolName) => {
|
|
|
2938
2963
|
serverConfigsByName.size <= 1 &&
|
|
2939
2964
|
filteredServerInfos.length === 1;
|
|
2940
2965
|
for (const serverInfo of filteredServerInfos) {
|
|
2941
|
-
//
|
|
2942
|
-
|
|
2943
|
-
// caller. Other disconnected servers cannot serve the request. See #1029.
|
|
2944
|
-
if ((serverInfo.status !== 'connected' && !serverInfo.config?.startOnDemand) ||
|
|
2945
|
-
serverInfo.enabled === false) {
|
|
2966
|
+
// An idle on-demand server is selected here and woken by the caller (#1029)
|
|
2967
|
+
if (!canServeToolRequests(serverInfo)) {
|
|
2946
2968
|
continue;
|
|
2947
2969
|
}
|
|
2948
2970
|
const serverConfig = serverConfigsByName.get(serverInfo.name);
|
|
@@ -3038,13 +3060,12 @@ const listGroupTools = async (group, filteredServerInfos, serverConfigsByName, a
|
|
|
3038
3060
|
// Names the call handler always routes to a meta-tool on a Smart Routing session,
|
|
3039
3061
|
// whichever of them the current mode lists (see handleCallToolRequestImpl)
|
|
3040
3062
|
const SMART_ROUTING_META_TOOL_NAMES = new Set(['search_tools', 'describe_tool', 'call_tool']);
|
|
3041
|
-
//
|
|
3042
|
-
//
|
|
3043
|
-
|
|
3044
|
-
const getPinnedSmartRoutingTools = async (group) => {
|
|
3063
|
+
// Each member's pinnedTools, narrowed to its tools selection. Empty when the
|
|
3064
|
+
// route is not a $smart/<group> or the group pins nothing.
|
|
3065
|
+
const resolvePinnedSmartRoutingServers = async (group) => {
|
|
3045
3066
|
const lookupGroup = getGroupLookupName(group);
|
|
3046
3067
|
if (!lookupGroup) {
|
|
3047
|
-
return
|
|
3068
|
+
return undefined;
|
|
3048
3069
|
}
|
|
3049
3070
|
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup);
|
|
3050
3071
|
const pinnedConfigsByName = new Map();
|
|
@@ -3056,9 +3077,23 @@ const getPinnedSmartRoutingTools = async (group) => {
|
|
|
3056
3077
|
}
|
|
3057
3078
|
}
|
|
3058
3079
|
if (pinnedConfigsByName.size === 0) {
|
|
3080
|
+
return undefined;
|
|
3081
|
+
}
|
|
3082
|
+
return {
|
|
3083
|
+
lookupGroup,
|
|
3084
|
+
pinnedServerInfos: filteredServerInfos.filter((serverInfo) => pinnedConfigsByName.has(serverInfo.name)),
|
|
3085
|
+
pinnedConfigsByName,
|
|
3086
|
+
};
|
|
3087
|
+
};
|
|
3088
|
+
// Tools a $smart/<group> lists next to its meta-tools: each member's pinnedTools,
|
|
3089
|
+
// narrowed to its tools selection and then run through the same filtering and
|
|
3090
|
+
// projection as a direct group listing.
|
|
3091
|
+
const getPinnedSmartRoutingTools = async (group, appsRouteContext = { enabled: false }) => {
|
|
3092
|
+
const pinned = await resolvePinnedSmartRoutingServers(group);
|
|
3093
|
+
if (!pinned) {
|
|
3059
3094
|
return [];
|
|
3060
3095
|
}
|
|
3061
|
-
return listGroupTools(lookupGroup,
|
|
3096
|
+
return listGroupTools(pinned.lookupGroup, pinned.pinnedServerInfos, pinned.pinnedConfigsByName, appsRouteContext);
|
|
3062
3097
|
};
|
|
3063
3098
|
const handleListToolsRequestImpl = async (_, extra) => {
|
|
3064
3099
|
const sessionId = extra.sessionId || '';
|
|
@@ -3070,10 +3105,11 @@ const handleListToolsRequestImpl = async (_, extra) => {
|
|
|
3070
3105
|
const smartRoutingTools = await getSmartRoutingTools(group);
|
|
3071
3106
|
let pinnedTools = [];
|
|
3072
3107
|
try {
|
|
3108
|
+
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
3073
3109
|
// A pin named like a meta-tool (e.g. alias "describe" + separator "_" +
|
|
3074
3110
|
// tool "tool") would be intercepted by it on call, even in a mode that
|
|
3075
3111
|
// does not list that meta-tool, so it is not listed at all
|
|
3076
|
-
pinnedTools = (await getPinnedSmartRoutingTools(group)).filter((tool) => !SMART_ROUTING_META_TOOL_NAMES.has(tool.name));
|
|
3112
|
+
pinnedTools = (await getPinnedSmartRoutingTools(group, appsRouteContext)).filter((tool) => !SMART_ROUTING_META_TOOL_NAMES.has(tool.name));
|
|
3077
3113
|
}
|
|
3078
3114
|
catch (error) {
|
|
3079
3115
|
// Pins are an optimisation: failing to resolve them must not cost the meta-tools
|