@samanhappy/mcphub 1.0.46 → 1.1.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.
- package/dist/betterAuth.js +5 -0
- package/dist/betterAuth.js.map +1 -1
- package/dist/clients/legacyMcpClient.js +30 -4
- package/dist/clients/legacyMcpClient.js.map +1 -1
- package/dist/clients/openapi.js +4 -1
- package/dist/clients/openapi.js.map +1 -1
- 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/serverController.js +49 -9
- 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/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/routes/index.js +3 -5
- package/dist/routes/index.js.map +1 -1
- package/dist/services/betterAuthConfig.js +17 -0
- package/dist/services/betterAuthConfig.js.map +1 -1
- package/dist/services/mcpOAuthProvider.js +11 -0
- package/dist/services/mcpOAuthProvider.js.map +1 -1
- package/dist/services/mcpService.js +109 -23
- 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/proxy.js +4 -0
- package/dist/services/proxy.js.map +1 -1
- package/dist/services/smartRoutingService.js +54 -52
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/services/sseService.js +11 -8
- package/dist/services/sseService.js.map +1 -1
- package/dist/utils/listFreshness.js +44 -0
- package/dist/utils/listFreshness.js.map +1 -0
- 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 +4 -0
- package/dist/utils/serverConfigPersistence.js.map +1 -1
- 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 +49 -2
- 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-C4vGn8kC.js → ActivityPage-D3Oto1RE.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-C4vGn8kC.js.map → ActivityPage-D3Oto1RE.js.map} +1 -1
- package/frontend/dist/assets/{CopyClientConfigDialog-gqu8M16p.js → CopyClientConfigDialog-kX5ewtFp.js} +2 -2
- package/frontend/dist/assets/{CopyClientConfigDialog-gqu8M16p.js.map → CopyClientConfigDialog-kX5ewtFp.js.map} +1 -1
- package/frontend/dist/assets/{CredentialsPage-EJYe8sf_.js → CredentialsPage-C9dHU6Wf.js} +2 -2
- package/frontend/dist/assets/{CredentialsPage-EJYe8sf_.js.map → CredentialsPage-C9dHU6Wf.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-CqqzEeM3.js → Dashboard-DFyfT22A.js} +2 -2
- package/frontend/dist/assets/{Dashboard-CqqzEeM3.js.map → Dashboard-DFyfT22A.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-sH8M0Zl-.js → EndpointCopy-6sqlZ15W.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-sH8M0Zl-.js.map → EndpointCopy-6sqlZ15W.js.map} +1 -1
- package/frontend/dist/assets/{GroupsPage-BAiER86x.js → GroupsPage-joJmAW7F.js} +2 -2
- package/frontend/dist/assets/{GroupsPage-BAiER86x.js.map → GroupsPage-joJmAW7F.js.map} +1 -1
- package/frontend/dist/assets/LoginPage-10tXreQ_.js +2 -0
- package/frontend/dist/assets/LoginPage-10tXreQ_.js.map +1 -0
- package/frontend/dist/assets/{LogsPage-Ci0H5Hbz.js → LogsPage-yBj3gqOU.js} +2 -2
- package/frontend/dist/assets/{LogsPage-Ci0H5Hbz.js.map → LogsPage-yBj3gqOU.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-B0bU64Fu.js → MarketPage-CuAl4IXf.js} +2 -2
- package/frontend/dist/assets/{MarketPage-B0bU64Fu.js.map → MarketPage-CuAl4IXf.js.map} +1 -1
- package/frontend/dist/assets/{OAuthConsentPage-COPngtNn.js → OAuthConsentPage-BuvpRon8.js} +2 -2
- package/frontend/dist/assets/{OAuthConsentPage-COPngtNn.js.map → OAuthConsentPage-BuvpRon8.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-DkRz0aOM.js → PromptsPage-DsYU0Kgm.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-DkRz0aOM.js.map → PromptsPage-DsYU0Kgm.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-D9UvvBJO.js → ResourcesPage-DVxFnDJE.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-D9UvvBJO.js.map → ResourcesPage-DVxFnDJE.js.map} +1 -1
- package/frontend/dist/assets/{ServersPage-D9DDRTwG.js → ServersPage-DWq6jqvv.js} +4 -4
- package/frontend/dist/assets/{ServersPage-D9DDRTwG.js.map → ServersPage-DWq6jqvv.js.map} +1 -1
- package/frontend/dist/assets/SettingsPage-Db0jJzi0.js +12 -0
- package/frontend/dist/assets/SettingsPage-Db0jJzi0.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-rU2z4UIB.js → StatusDot-DdZTtyw1.js} +2 -2
- package/frontend/dist/assets/{StatusDot-rU2z4UIB.js.map → StatusDot-DdZTtyw1.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-B-m_CA-c.js → ToggleGroup-tJbhgavL.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-B-m_CA-c.js.map → ToggleGroup-tJbhgavL.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-BtCcY8iT.js → UsersPage-BqM1rGSo.js} +2 -2
- package/frontend/dist/assets/{UsersPage-BtCcY8iT.js.map → UsersPage-BqM1rGSo.js.map} +1 -1
- package/frontend/dist/assets/{clipboard-ZgOiZZfc.js → clipboard-CakNzStU.js} +2 -2
- package/frontend/dist/assets/{clipboard-ZgOiZZfc.js.map → clipboard-CakNzStU.js.map} +1 -1
- package/frontend/dist/assets/index--NhF_yyy.css +1 -0
- package/frontend/dist/assets/index-DE5u_PAU.js +3 -0
- package/frontend/dist/assets/index-DE5u_PAU.js.map +1 -0
- package/frontend/dist/assets/{resourceService-B_JEhB8X.js → resourceService-BkO_Mq1D.js} +2 -2
- package/frontend/dist/assets/{resourceService-B_JEhB8X.js.map → resourceService-BkO_Mq1D.js.map} +1 -1
- package/frontend/dist/assets/{useCostData-B9tFvSJs.js → useCostData-inPDjb84.js} +2 -2
- package/frontend/dist/assets/{useCostData-B9tFvSJs.js.map → useCostData-inPDjb84.js.map} +1 -1
- package/frontend/dist/assets/variableDetection-Dt-yEyj8.js +16 -0
- package/frontend/dist/assets/variableDetection-Dt-yEyj8.js.map +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/LoginPage-BPhnf62f.js +0 -2
- package/frontend/dist/assets/LoginPage-BPhnf62f.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-p0RqcQ3b.js +0 -12
- package/frontend/dist/assets/SettingsPage-p0RqcQ3b.js.map +0 -1
- package/frontend/dist/assets/index-D-J2WUmj.css +0 -1
- package/frontend/dist/assets/index-DeDkxr8P.js +0 -3
- package/frontend/dist/assets/index-DeDkxr8P.js.map +0 -1
- package/frontend/dist/assets/variableDetection-DSNGipcp.js +0 -16
- package/frontend/dist/assets/variableDetection-DSNGipcp.js.map +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { preserveListFreshness, combineListFreshness, remainingListTtl, } from '../utils/listFreshness.js';
|
|
1
2
|
import { expandServerConfig, getServerEnvironment } from './serverConfigEnvironment.js';
|
|
2
3
|
import { getMcpRequestGroup } from '../utils/mcpRequestGroup.js';
|
|
3
4
|
import { LegacyMcpClient } from '../clients/legacyMcpClient.js';
|
|
4
5
|
import { LEGACY_PROTOCOL_VERSIONS } from '../utils/mcpProtocol.js';
|
|
5
6
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
7
|
import { canAccessGroupRoute } from '../utils/groupAccess.js';
|
|
8
|
+
import { canServeToolRequests } from '../utils/serverAvailability.js';
|
|
7
9
|
import { PrincipalRuntimeService } from './principalRuntimeService.js';
|
|
8
10
|
import { UserContextService } from './userContextService.js';
|
|
9
11
|
import { deleteCredentialBindings, missingCredentialError } from './credentialBindingService.js';
|
|
@@ -23,6 +25,7 @@ import { ResilientJsonSchemaValidator } from '../utils/jsonSchemaValidator.js';
|
|
|
23
25
|
import { getUserDao } from '../dao/index.js';
|
|
24
26
|
import { expandEnvVars, replaceEnvVars, getNameSeparator } from '../config/index.js';
|
|
25
27
|
import config from '../config/index.js';
|
|
28
|
+
import { validateMaxBufferSize } from '../utils/stdioOptions.js';
|
|
26
29
|
import { validateServerName } from '../utils/serverNameValidation.js';
|
|
27
30
|
import { getGroup } from './sseService.js';
|
|
28
31
|
import { getServerConfigInGroup, normalizeGroupServers } from './groupService.js';
|
|
@@ -30,7 +33,7 @@ import { removeServerToolEmbeddings, removeToolEmbeddings, saveToolsAsVectorEmbe
|
|
|
30
33
|
import { OpenAPIClient } from '../clients/openapi.js';
|
|
31
34
|
import { RequestContextService } from './requestContextService.js';
|
|
32
35
|
import { getDataService } from './services.js';
|
|
33
|
-
import { getServerDao, getGroupDao, getSystemConfigDao, getBuiltinPromptDao, getBuiltinResourceDao, } from '../dao/index.js';
|
|
36
|
+
import { getBearerKeyDao, getServerDao, getGroupDao, getSystemConfigDao, getBuiltinPromptDao, getBuiltinResourceDao, } from '../dao/index.js';
|
|
34
37
|
import { initializeAllOAuthClients } from './oauthService.js';
|
|
35
38
|
import { createOAuthProvider } from './mcpOAuthProvider.js';
|
|
36
39
|
import { initSmartRoutingService, getSmartRoutingTools, handleSearchToolsRequest, handleDescribeToolRequest, isSmartRoutingGroup, } from './smartRoutingService.js';
|
|
@@ -782,7 +785,7 @@ export const setServerInfosForTest = (infos) => {
|
|
|
782
785
|
serverInfos = infos;
|
|
783
786
|
};
|
|
784
787
|
export const updateServerToolsCache = (serverInfo, tools, options) => {
|
|
785
|
-
serverInfo.tools = tools.map((tool) => normalizeToolForCache(serverInfo.name, tool));
|
|
788
|
+
serverInfo.tools = preserveListFreshness(tools, tools.map((tool) => normalizeToolForCache(serverInfo.name, tool)));
|
|
786
789
|
syncToolsAsVectorEmbeddings(serverInfo.name, serverInfo.tools, {
|
|
787
790
|
reportProgress: options?.reportEmbeddingProgress === true,
|
|
788
791
|
}).catch(() => {
|
|
@@ -790,10 +793,10 @@ export const updateServerToolsCache = (serverInfo, tools, options) => {
|
|
|
790
793
|
});
|
|
791
794
|
};
|
|
792
795
|
const updateServerPromptsCache = (serverInfo, prompts) => {
|
|
793
|
-
serverInfo.prompts = prompts.map((prompt) => normalizePromptForCache(serverInfo.name, prompt));
|
|
796
|
+
serverInfo.prompts = preserveListFreshness(prompts, prompts.map((prompt) => normalizePromptForCache(serverInfo.name, prompt)));
|
|
794
797
|
};
|
|
795
798
|
const updateServerResourcesCache = (serverInfo, resources) => {
|
|
796
|
-
serverInfo.resources = resources.map(normalizeResourceForCache);
|
|
799
|
+
serverInfo.resources = preserveListFreshness(resources, resources.map(normalizeResourceForCache));
|
|
797
800
|
};
|
|
798
801
|
const logListChangedRefreshError = (listType) => {
|
|
799
802
|
logger.warn(`Failed to refresh ${listType} list after upstream notification`);
|
|
@@ -807,6 +810,7 @@ const createUpstreamMcpClient = (name, getServerInfo) => {
|
|
|
807
810
|
jsonSchemaValidator: new ResilientJsonSchemaValidator(),
|
|
808
811
|
listChanged: {
|
|
809
812
|
tools: {
|
|
813
|
+
debounceMs: 0,
|
|
810
814
|
onChanged: (error, tools) => {
|
|
811
815
|
const serverInfo = getServerInfo();
|
|
812
816
|
if (error) {
|
|
@@ -821,6 +825,7 @@ const createUpstreamMcpClient = (name, getServerInfo) => {
|
|
|
821
825
|
},
|
|
822
826
|
},
|
|
823
827
|
prompts: {
|
|
828
|
+
debounceMs: 0,
|
|
824
829
|
onChanged: (error, prompts) => {
|
|
825
830
|
const serverInfo = getServerInfo();
|
|
826
831
|
if (error) {
|
|
@@ -835,6 +840,7 @@ const createUpstreamMcpClient = (name, getServerInfo) => {
|
|
|
835
840
|
},
|
|
836
841
|
},
|
|
837
842
|
resources: {
|
|
843
|
+
debounceMs: 0,
|
|
838
844
|
onChanged: (error, resources) => {
|
|
839
845
|
const serverInfo = getServerInfo();
|
|
840
846
|
if (error) {
|
|
@@ -1289,6 +1295,7 @@ export const createTransportFromConfig = async (name, conf) => {
|
|
|
1289
1295
|
args: finalArgs,
|
|
1290
1296
|
env: env,
|
|
1291
1297
|
stderr: 'pipe',
|
|
1298
|
+
maxBufferSize: validateMaxBufferSize(conf.options?.maxBufferSize),
|
|
1292
1299
|
});
|
|
1293
1300
|
if (transport.stderr && hasCredentialTemplate(conf)) {
|
|
1294
1301
|
transport.stderr.on('data', () => undefined); // Personal upstream diagnostics can contain raw credentials.
|
|
@@ -1386,6 +1393,7 @@ const callToolWithReconnect = async (serverInfo, toolParams, options, maxRetries
|
|
|
1386
1393
|
serverInfo.client = newClient;
|
|
1387
1394
|
serverInfo.transport = newTransport;
|
|
1388
1395
|
serverInfo.status = 'connected';
|
|
1396
|
+
setupServerKeepAlive(serverInfo, server);
|
|
1389
1397
|
}
|
|
1390
1398
|
// Point the local refs at the new connection for the next attempt.
|
|
1391
1399
|
client = newClient;
|
|
@@ -2329,6 +2337,7 @@ const classifyUnavailableReason = (qualifiedName) => {
|
|
|
2329
2337
|
};
|
|
2330
2338
|
// Add new server
|
|
2331
2339
|
export const addServer = async (name, config) => {
|
|
2340
|
+
validateMaxBufferSize(config.options?.maxBufferSize);
|
|
2332
2341
|
validateCredentialTemplate(config);
|
|
2333
2342
|
const server = { name, ...config };
|
|
2334
2343
|
const result = await getServerDao().create(server);
|
|
@@ -2363,6 +2372,7 @@ export const removeServer = async (name) => {
|
|
|
2363
2372
|
// Add or update server (supports overriding existing servers for MCPB)
|
|
2364
2373
|
export const addOrUpdateServer = async (name, config, allowOverride = false) => {
|
|
2365
2374
|
try {
|
|
2375
|
+
validateMaxBufferSize(config.options?.maxBufferSize);
|
|
2366
2376
|
validateCredentialTemplate(config);
|
|
2367
2377
|
const exists = await getServerDao().exists(name);
|
|
2368
2378
|
if (exists && !allowOverride) {
|
|
@@ -2934,11 +2944,8 @@ const resolveToolInGroup = async (group, toolName) => {
|
|
|
2934
2944
|
serverConfigsByName.size <= 1 &&
|
|
2935
2945
|
filteredServerInfos.length === 1;
|
|
2936
2946
|
for (const serverInfo of filteredServerInfos) {
|
|
2937
|
-
//
|
|
2938
|
-
|
|
2939
|
-
// caller. Other disconnected servers cannot serve the request. See #1029.
|
|
2940
|
-
if ((serverInfo.status !== 'connected' && !serverInfo.config?.startOnDemand) ||
|
|
2941
|
-
serverInfo.enabled === false) {
|
|
2947
|
+
// An idle on-demand server is selected here and woken by the caller (#1029)
|
|
2948
|
+
if (!canServeToolRequests(serverInfo)) {
|
|
2942
2949
|
continue;
|
|
2943
2950
|
}
|
|
2944
2951
|
const serverConfig = serverConfigsByName.get(serverInfo.name);
|
|
@@ -3764,19 +3771,24 @@ const handleListResourceTemplatesRequestImpl = async (_, extra) => {
|
|
|
3764
3771
|
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup, {
|
|
3765
3772
|
requireClient: true,
|
|
3766
3773
|
});
|
|
3774
|
+
const snapshots = [];
|
|
3767
3775
|
const results = await Promise.allSettled(filteredServerInfos.map(async (serverInfo) => {
|
|
3768
3776
|
if (!serverInfo.client?.listResourceTemplates) {
|
|
3769
3777
|
return [];
|
|
3770
3778
|
}
|
|
3771
3779
|
const templates = await serverInfo.client.listResourceTemplates({}, serverInfo.options || {});
|
|
3780
|
+
snapshots.push(templates.resourceTemplates);
|
|
3772
3781
|
const filteredTemplates = await filterResourceTemplatesByGroup(lookupGroup, serverInfo.name, templates.resourceTemplates || [], serverConfigsByName.get(serverInfo.name));
|
|
3773
3782
|
return appsRouteContext.enabled
|
|
3774
3783
|
? filteredTemplates
|
|
3775
3784
|
: filteredTemplates.map((template) => stripMcpAppsMetadata(template));
|
|
3776
3785
|
}));
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3786
|
+
const resourceTemplates = results.flatMap((result) => result.status === 'fulfilled' ? result.value : []);
|
|
3787
|
+
if (results.every((result) => result.status === 'fulfilled') &&
|
|
3788
|
+
snapshots.length === filteredServerInfos.length) {
|
|
3789
|
+
combineListFreshness(snapshots, resourceTemplates);
|
|
3790
|
+
}
|
|
3791
|
+
return { resourceTemplates };
|
|
3780
3792
|
};
|
|
3781
3793
|
const handleReadResourceRequestImpl = async (request, extra) => {
|
|
3782
3794
|
try {
|
|
@@ -3954,12 +3966,18 @@ const createPrincipalRuntime = async (name, resolvedConfig) => {
|
|
|
3954
3966
|
info.client = protectClient(client);
|
|
3955
3967
|
await client.connect(transport, resolvedConfig.options || {});
|
|
3956
3968
|
const capabilities = client.getServerCapabilities();
|
|
3957
|
-
if (capabilities?.tools)
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
if (capabilities?.
|
|
3962
|
-
|
|
3969
|
+
if (capabilities?.tools) {
|
|
3970
|
+
const { tools } = await client.listTools({}, resolvedConfig.options);
|
|
3971
|
+
info.tools = preserveListFreshness(tools, tools.map((tool) => normalizeToolForCache(name, tool)));
|
|
3972
|
+
}
|
|
3973
|
+
if (capabilities?.prompts) {
|
|
3974
|
+
const { prompts } = await client.listPrompts({}, resolvedConfig.options);
|
|
3975
|
+
info.prompts = preserveListFreshness(prompts, prompts.map((prompt) => normalizePromptForCache(name, prompt)));
|
|
3976
|
+
}
|
|
3977
|
+
if (capabilities?.resources) {
|
|
3978
|
+
const { resources } = await client.listResources({}, resolvedConfig.options);
|
|
3979
|
+
info.resources = preserveListFreshness(resources, resources.map(normalizeResourceForCache));
|
|
3980
|
+
}
|
|
3963
3981
|
info.version = client.getServerVersion()?.version;
|
|
3964
3982
|
// Record the resolved npx/uvx package version backing this server.
|
|
3965
3983
|
applyResolvedPackageVersion(info, resolvedConfig);
|
|
@@ -4111,14 +4129,82 @@ const withPrincipalServers = (handler, operation) => async (request, extra) => {
|
|
|
4111
4129
|
release();
|
|
4112
4130
|
}
|
|
4113
4131
|
};
|
|
4132
|
+
// Re-read authoritative inputs across awaits; no projected response is cached.
|
|
4133
|
+
const listProjectionInputs = async (group, field) => {
|
|
4134
|
+
const user = UserContextService.getInstance().getCurrentUser();
|
|
4135
|
+
const keyId = RequestContextService.getInstance().getBearerKeyContext().keyId;
|
|
4136
|
+
const liveUser = user?.username ? await getUserDao().findByUsername(user.username) : undefined;
|
|
4137
|
+
const key = keyId ? await getBearerKeyDao().findById(keyId) : undefined;
|
|
4138
|
+
if ((user?.username && (!liveUser || Boolean(user.isAdmin) !== Boolean(liveUser.isAdmin))) ||
|
|
4139
|
+
(keyId && !key?.enabled))
|
|
4140
|
+
return undefined;
|
|
4141
|
+
const lookupGroup = getGroupLookupName(group);
|
|
4142
|
+
const groupDao = getGroupDao();
|
|
4143
|
+
let routeGroup = lookupGroup ? await groupDao.findByName(lookupGroup) : undefined;
|
|
4144
|
+
if (!routeGroup &&
|
|
4145
|
+
lookupGroup &&
|
|
4146
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(lookupGroup)) {
|
|
4147
|
+
routeGroup = await groupDao.findById(lookupGroup);
|
|
4148
|
+
}
|
|
4149
|
+
return JSON.stringify([
|
|
4150
|
+
await getServerDao().findAll(),
|
|
4151
|
+
routeGroup,
|
|
4152
|
+
await getSystemConfigDao().get(),
|
|
4153
|
+
field === 'prompts' ? await getBuiltinPromptDao().findEnabled() : undefined,
|
|
4154
|
+
field === 'resources' ? await getBuiltinResourceDao().findEnabled() : undefined,
|
|
4155
|
+
liveUser,
|
|
4156
|
+
key,
|
|
4157
|
+
RequestContextService.getInstance().getRequestContext()?.clientCapabilities,
|
|
4158
|
+
getVisibleServerInfos().map((info) => ({
|
|
4159
|
+
name: info.name,
|
|
4160
|
+
status: info.status,
|
|
4161
|
+
enabled: info.enabled,
|
|
4162
|
+
owner: info.owner,
|
|
4163
|
+
visibility: info.visibility,
|
|
4164
|
+
sharedWithUsers: info.sharedWithUsers,
|
|
4165
|
+
config: info.config,
|
|
4166
|
+
})),
|
|
4167
|
+
]);
|
|
4168
|
+
};
|
|
4114
4169
|
// Lists combine cached discovery with live gateway configuration and authorization.
|
|
4115
4170
|
// Upstream freshness alone cannot guarantee freshness of the resulting projection.
|
|
4116
|
-
const withCacheHints = (handler, passthroughTtl = false) => async (request, extra) => {
|
|
4171
|
+
const withCacheHints = (handler, passthroughTtl = false, listField) => async (request, extra) => {
|
|
4117
4172
|
if (!RequestContextService.getInstance().getRequestContext()?.stateless) {
|
|
4118
4173
|
return handler(request, extra);
|
|
4119
4174
|
}
|
|
4120
4175
|
const startedAt = performance.now();
|
|
4176
|
+
const group = getMcpRequestGroup(extra);
|
|
4177
|
+
const inputs = listField ? await listProjectionInputs(group, listField) : undefined;
|
|
4178
|
+
const before = listField
|
|
4179
|
+
? await getFilteredServerInfosForGroup(getGroupLookupName(group))
|
|
4180
|
+
: undefined;
|
|
4181
|
+
const snapshots = before && listField && listField !== 'resourceTemplates'
|
|
4182
|
+
? before.filteredServerInfos.map((info) => info[listField])
|
|
4183
|
+
: [];
|
|
4184
|
+
const clients = before?.filteredServerInfos.map((info) => info.client);
|
|
4121
4185
|
const result = await handler(request, extra);
|
|
4186
|
+
if (listField) {
|
|
4187
|
+
const after = await getFilteredServerInfosForGroup(getGroupLookupName(group));
|
|
4188
|
+
const stable = typeof inputs === 'string' &&
|
|
4189
|
+
inputs === (await listProjectionInputs(group, listField)) &&
|
|
4190
|
+
before?.filteredServerInfos.length === after.filteredServerInfos.length &&
|
|
4191
|
+
before.filteredServerInfos.every((info, index) => info === after.filteredServerInfos[index] &&
|
|
4192
|
+
info.client === clients?.[index] &&
|
|
4193
|
+
info.status === 'connected' &&
|
|
4194
|
+
(listField === 'resourceTemplates' || info[listField] === snapshots[index]));
|
|
4195
|
+
const generated = (listField === 'tools' && isSmartRoutingGroup(group)) ||
|
|
4196
|
+
(listField === 'prompts' && (await getBuiltinPromptDao().findEnabled()).length > 0) ||
|
|
4197
|
+
(listField === 'resources' && (await getBuiltinResourceDao().findEnabled()).length > 0);
|
|
4198
|
+
const unknownPermissions = RequestContextService.getInstance().getHostedAuthContext();
|
|
4199
|
+
const dependencies = listField === 'resourceTemplates' ? [result.resourceTemplates] : snapshots;
|
|
4200
|
+
return {
|
|
4201
|
+
...result,
|
|
4202
|
+
ttlMs: stable && !generated && !unknownPermissions
|
|
4203
|
+
? remainingListTtl(dependencies, startedAt, performance.now())
|
|
4204
|
+
: 0,
|
|
4205
|
+
cacheScope: 'private',
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4122
4208
|
const upstreamTtl = result.ttlMs;
|
|
4123
4209
|
const ttlMs = passthroughTtl &&
|
|
4124
4210
|
typeof upstreamTtl === 'number' &&
|
|
@@ -4130,12 +4216,12 @@ const withCacheHints = (handler, passthroughTtl = false) => async (request, extr
|
|
|
4130
4216
|
// and capabilities. Shared caches must not reuse the gateway result across users.
|
|
4131
4217
|
return { ...result, ttlMs, cacheScope: 'private' };
|
|
4132
4218
|
};
|
|
4133
|
-
export const handleListToolsRequest = withPrincipalServers(withCacheHints(handleListToolsRequestImpl), 'list');
|
|
4219
|
+
export const handleListToolsRequest = withPrincipalServers(withCacheHints(handleListToolsRequestImpl, false, 'tools'), 'list');
|
|
4134
4220
|
export const handleCallToolRequest = withPrincipalServers(handleCallToolRequestImpl, 'tool');
|
|
4135
4221
|
export const handleGetPromptRequest = withPrincipalServers(handleGetPromptRequestImpl, 'prompt');
|
|
4136
|
-
export const handleListPromptsRequest = withPrincipalServers(withCacheHints(handleListPromptsRequestImpl), 'list');
|
|
4137
|
-
export const handleListResourcesRequest = withPrincipalServers(withCacheHints(handleListResourcesRequestImpl), 'list');
|
|
4138
|
-
export const handleListResourceTemplatesRequest = withPrincipalServers(withCacheHints(handleListResourceTemplatesRequestImpl), 'list');
|
|
4222
|
+
export const handleListPromptsRequest = withPrincipalServers(withCacheHints(handleListPromptsRequestImpl, false, 'prompts'), 'list');
|
|
4223
|
+
export const handleListResourcesRequest = withPrincipalServers(withCacheHints(handleListResourcesRequestImpl, false, 'resources'), 'list');
|
|
4224
|
+
export const handleListResourceTemplatesRequest = withPrincipalServers(withCacheHints(handleListResourceTemplatesRequestImpl, false, 'resourceTemplates'), 'list');
|
|
4139
4225
|
export const handleReadResourceRequest = withPrincipalServers(withCacheHints(handleReadResourceRequestImpl, true), 'resource');
|
|
4140
4226
|
export const createMcpServer = (name, version, options) => {
|
|
4141
4227
|
const normalizedOptions = typeof options === 'string' ? { group: options } : (options ?? {});
|