@samanhappy/mcphub 1.0.41 → 1.0.43
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 +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/clients/legacyMcpClient.js +37 -0
- package/dist/clients/legacyMcpClient.js.map +1 -0
- package/dist/config/index.js +19 -9
- package/dist/config/index.js.map +1 -1
- package/dist/controllers/groupController.js +14 -0
- package/dist/controllers/groupController.js.map +1 -1
- package/dist/controllers/oauthCallbackController.js +6 -2
- package/dist/controllers/oauthCallbackController.js.map +1 -1
- package/dist/controllers/serverController.js +102 -3
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/controllers/smartRoutingController.js +15 -5
- package/dist/controllers/smartRoutingController.js.map +1 -1
- package/dist/db/entities/Group.js.map +1 -1
- package/dist/db/repositories/VectorEmbeddingRepository.js +21 -1
- package/dist/db/repositories/VectorEmbeddingRepository.js.map +1 -1
- package/dist/server.js +3 -2
- package/dist/server.js.map +1 -1
- package/dist/services/contextCostService.js +11 -4
- package/dist/services/contextCostService.js.map +1 -1
- package/dist/services/groupService.js +19 -2
- package/dist/services/groupService.js.map +1 -1
- package/dist/services/keepAliveService.js +1 -2
- package/dist/services/keepAliveService.js.map +1 -1
- package/dist/services/mcpOAuthProvider.js +40 -11
- package/dist/services/mcpOAuthProvider.js.map +1 -1
- package/dist/services/mcpService.js +203 -69
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/oauthClientRegistration.js +53 -8
- package/dist/services/oauthClientRegistration.js.map +1 -1
- package/dist/services/oauthService.js +1 -2
- package/dist/services/oauthService.js.map +1 -1
- package/dist/services/oauthSettingsStore.js +5 -3
- package/dist/services/oauthSettingsStore.js.map +1 -1
- package/dist/services/proxy.js +0 -8
- package/dist/services/proxy.js.map +1 -1
- package/dist/services/smartRoutingService.js +109 -50
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/services/sseService.js +52 -5
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/vectorSearchService.js +72 -18
- package/dist/services/vectorSearchService.js.map +1 -1
- package/dist/utils/jsonSchemaValidator.js +5 -5
- package/dist/utils/jsonSchemaValidator.js.map +1 -1
- package/dist/utils/mcpProtocol.js +9 -0
- package/dist/utils/mcpProtocol.js.map +1 -0
- package/dist/utils/mcpRequestGroup.js +8 -0
- package/dist/utils/mcpRequestGroup.js.map +1 -0
- package/dist/utils/oauthIssuer.js +9 -16
- package/dist/utils/oauthIssuer.js.map +1 -1
- package/dist/utils/serverConfigPersistence.js +13 -2
- package/dist/utils/serverConfigPersistence.js.map +1 -1
- package/dist/utils/smartRouting.js +32 -0
- package/dist/utils/smartRouting.js.map +1 -1
- package/frontend/dist/assets/{ActivityPage-adLbSuxT.js → ActivityPage-Y8gIRxkd.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-adLbSuxT.js.map → ActivityPage-Y8gIRxkd.js.map} +1 -1
- package/frontend/dist/assets/{CopyClientConfigDialog-hRNdG_Hz.js → CopyClientConfigDialog-BHijFbyw.js} +2 -2
- package/frontend/dist/assets/{CopyClientConfigDialog-hRNdG_Hz.js.map → CopyClientConfigDialog-BHijFbyw.js.map} +1 -1
- package/frontend/dist/assets/{CredentialsPage-BSogppb6.js → CredentialsPage-1UqVqEMQ.js} +2 -2
- package/frontend/dist/assets/{CredentialsPage-BSogppb6.js.map → CredentialsPage-1UqVqEMQ.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-BttcAnP2.js → Dashboard-CswKNreX.js} +2 -2
- package/frontend/dist/assets/{Dashboard-BttcAnP2.js.map → Dashboard-CswKNreX.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-CNhtxQdx.js → EndpointCopy-B0yIEBjO.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-CNhtxQdx.js.map → EndpointCopy-B0yIEBjO.js.map} +1 -1
- package/frontend/dist/assets/GroupsPage-KJJsAprG.js +33 -0
- package/frontend/dist/assets/GroupsPage-KJJsAprG.js.map +1 -0
- package/frontend/dist/assets/{LoginPage-C4qBJzYk.js → LoginPage-B1heSsJ6.js} +2 -2
- package/frontend/dist/assets/{LoginPage-C4qBJzYk.js.map → LoginPage-B1heSsJ6.js.map} +1 -1
- package/frontend/dist/assets/{LogsPage-BXHqsBa0.js → LogsPage-C4BOgOSW.js} +2 -2
- package/frontend/dist/assets/{LogsPage-BXHqsBa0.js.map → LogsPage-C4BOgOSW.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-C2q7W2an.js → MarketPage-D0RXyf6g.js} +2 -2
- package/frontend/dist/assets/{MarketPage-C2q7W2an.js.map → MarketPage-D0RXyf6g.js.map} +1 -1
- package/frontend/dist/assets/{OAuthConsentPage-D-UU5XNF.js → OAuthConsentPage-DLveAKh7.js} +2 -2
- package/frontend/dist/assets/{OAuthConsentPage-D-UU5XNF.js.map → OAuthConsentPage-DLveAKh7.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-2BBvSosU.js → PromptsPage-cPHf4CM9.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-2BBvSosU.js.map → PromptsPage-cPHf4CM9.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-DLVYyIm0.js → ResourcesPage-DcXEwXg9.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-DLVYyIm0.js.map → ResourcesPage-DcXEwXg9.js.map} +1 -1
- package/frontend/dist/assets/{ServersPage-Cx9zIvKw.js → ServersPage-CIPZUbqT.js} +4 -4
- package/frontend/dist/assets/{ServersPage-Cx9zIvKw.js.map → ServersPage-CIPZUbqT.js.map} +1 -1
- package/frontend/dist/assets/SettingsPage-vMxO3bOc.js +12 -0
- package/frontend/dist/assets/SettingsPage-vMxO3bOc.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-CAv_zsld.js → StatusDot-LGEKnAGN.js} +2 -2
- package/frontend/dist/assets/{StatusDot-CAv_zsld.js.map → StatusDot-LGEKnAGN.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-ffEOMWal.js → ToggleGroup-Ad_I2spO.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-ffEOMWal.js.map → ToggleGroup-Ad_I2spO.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-Cx6EDM_s.js → UsersPage-CbfajFCc.js} +2 -2
- package/frontend/dist/assets/{UsersPage-Cx6EDM_s.js.map → UsersPage-CbfajFCc.js.map} +1 -1
- package/frontend/dist/assets/{clipboard-_snued0J.js → clipboard-CjtPOmJs.js} +2 -2
- package/frontend/dist/assets/{clipboard-_snued0J.js.map → clipboard-CjtPOmJs.js.map} +1 -1
- package/frontend/dist/assets/{icons-vendor-CNEaJWqW.js → icons-vendor-CvmEvkQ-.js} +69 -64
- package/frontend/dist/assets/icons-vendor-CvmEvkQ-.js.map +1 -0
- package/frontend/dist/assets/index-Cla8rRxG.css +1 -0
- package/frontend/dist/assets/index-DJUr-vo3.js +3 -0
- package/frontend/dist/assets/index-DJUr-vo3.js.map +1 -0
- package/frontend/dist/assets/{resourceService-BCl2XrNf.js → resourceService-D4dgqjzY.js} +2 -2
- package/frontend/dist/assets/{resourceService-BCl2XrNf.js.map → resourceService-D4dgqjzY.js.map} +1 -1
- package/frontend/dist/assets/{useCostData-D3JNFGsT.js → useCostData-CT2iwvzx.js} +2 -2
- package/frontend/dist/assets/{useCostData-D3JNFGsT.js.map → useCostData-CT2iwvzx.js.map} +1 -1
- package/frontend/dist/assets/{variableDetection-CGuC1br1.js → variableDetection-BHrhKDaj.js} +2 -2
- package/frontend/dist/assets/{variableDetection-CGuC1br1.js.map → variableDetection-BHrhKDaj.js.map} +1 -1
- package/frontend/dist/index.html +4 -7
- package/package.json +9 -3
- package/frontend/dist/assets/GroupsPage-CQA7x2cz.js +0 -33
- package/frontend/dist/assets/GroupsPage-CQA7x2cz.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-pZCOi8NF.js +0 -12
- package/frontend/dist/assets/SettingsPage-pZCOi8NF.js.map +0 -1
- package/frontend/dist/assets/icons-vendor-CNEaJWqW.js.map +0 -1
- package/frontend/dist/assets/index-Dt8f_hd5.js +0 -3
- package/frontend/dist/assets/index-Dt8f_hd5.js.map +0 -1
- package/frontend/dist/assets/index-ru-OAIcF.css +0 -1
|
@@ -1,21 +1,20 @@
|
|
|
1
|
+
import { getMcpRequestGroup } from '../utils/mcpRequestGroup.js';
|
|
2
|
+
import { LegacyMcpClient } from '../clients/legacyMcpClient.js';
|
|
3
|
+
import { LEGACY_PROTOCOL_VERSIONS } from '../utils/mcpProtocol.js';
|
|
1
4
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
5
|
import { canAccessGroupRoute } from '../utils/groupAccess.js';
|
|
3
6
|
import { PrincipalRuntimeService } from './principalRuntimeService.js';
|
|
4
7
|
import { UserContextService } from './userContextService.js';
|
|
5
8
|
import { deleteCredentialBindings, missingCredentialError } from './credentialBindingService.js';
|
|
6
|
-
import { CredentialBindingError, hasCredentialTemplate, validateCredentialTemplate } from '../utils/credentialTemplate.js';
|
|
9
|
+
import { CredentialBindingError, hasCredentialTemplate, validateCredentialTemplate, } from '../utils/credentialTemplate.js';
|
|
7
10
|
import os from 'os';
|
|
8
11
|
import path from 'path';
|
|
9
12
|
import fs from 'fs';
|
|
10
13
|
import treeKill from 'tree-kill';
|
|
11
14
|
import { isProcessTreeKillAvailable } from '../utils/processTree.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
16
|
-
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
17
|
-
import { StreamableHTTPClientTransport, } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
18
|
-
import { normalizeHeaders } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
15
|
+
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
|
|
16
|
+
import { SSEClientTransport, StreamableHTTPClientTransport, } from '@modelcontextprotocol/client';
|
|
17
|
+
import { Server } from '@modelcontextprotocol/server';
|
|
19
18
|
import { createFetchWithProxy, getProxyConfigFromEnv } from './proxy.js';
|
|
20
19
|
import { assertSafeUrl, createRedirectValidatingFetch } from '../utils/ssrf.js';
|
|
21
20
|
import { createAbortIsolatingFetch } from '../utils/abortIsolatingFetch.js';
|
|
@@ -26,7 +25,7 @@ import config from '../config/index.js';
|
|
|
26
25
|
import { validateServerName } from '../utils/serverNameValidation.js';
|
|
27
26
|
import { getGroup } from './sseService.js';
|
|
28
27
|
import { getServerConfigInGroup, normalizeGroupServers } from './groupService.js';
|
|
29
|
-
import { removeServerToolEmbeddings, saveToolsAsVectorEmbeddings } from './vectorSearchService.js';
|
|
28
|
+
import { removeServerToolEmbeddings, removeToolEmbeddings, saveToolsAsVectorEmbeddings, } from './vectorSearchService.js';
|
|
30
29
|
import { OpenAPIClient } from '../clients/openapi.js';
|
|
31
30
|
import { RequestContextService } from './requestContextService.js';
|
|
32
31
|
import { getDataService } from './services.js';
|
|
@@ -40,7 +39,7 @@ import { assertHostedToolAllowed, filterHostedTools, reserveHostedToolCall, sett
|
|
|
40
39
|
import { formatErrorForLogging, sanitizeStringForLogging, summarizeErrorForLogging, } from '../utils/serialization.js';
|
|
41
40
|
import { addStdioErrorContext, observeStdioStderr } from '../utils/stdioDiagnostics.js';
|
|
42
41
|
import { MCP_APPS_CAPABILITIES, filterModelVisibleTools, hasMcpAppsCapability, isAppOnlyTool, stripMcpAppsMetadata, } from '../utils/mcpApps.js';
|
|
43
|
-
import { supportsCacheRefresh, injectRefreshFlag, clearRunnerCache, resolveRunnerPackageVersion } from '../utils/cacheUtils.js';
|
|
42
|
+
import { supportsCacheRefresh, injectRefreshFlag, clearRunnerCache, resolveRunnerPackageVersion, } from '../utils/cacheUtils.js';
|
|
44
43
|
import { checkPackageUpdate } from '../utils/packageUpdate.js';
|
|
45
44
|
const servers = {};
|
|
46
45
|
import { setupClientKeepAlive } from './keepAliveService.js';
|
|
@@ -519,7 +518,24 @@ const applyDescriptionOverridesForEmbedding = async (serverName, tools) => {
|
|
|
519
518
|
: tool;
|
|
520
519
|
});
|
|
521
520
|
};
|
|
522
|
-
|
|
521
|
+
// Tail of the embedding tasks queued per server. A per-user connect and a
|
|
522
|
+
// reindex, or two users connecting at once, often race on the same tool set;
|
|
523
|
+
// queued behind the first run, the second one hits the skip check in
|
|
524
|
+
// saveToolsAsVectorEmbeddings instead of embedding every tool a second time.
|
|
525
|
+
const embeddingSyncTails = new Map();
|
|
526
|
+
// Run `task` after every embedding task already queued for the server. A task
|
|
527
|
+
// must not wait for another queued task of the same server, or both stall.
|
|
528
|
+
const enqueueEmbeddingTask = (serverName, task) => {
|
|
529
|
+
const run = (embeddingSyncTails.get(serverName) ?? Promise.resolve()).then(task);
|
|
530
|
+
const tail = run.catch(() => undefined);
|
|
531
|
+
embeddingSyncTails.set(serverName, tail);
|
|
532
|
+
void tail.then(() => {
|
|
533
|
+
if (embeddingSyncTails.get(serverName) === tail)
|
|
534
|
+
embeddingSyncTails.delete(serverName);
|
|
535
|
+
});
|
|
536
|
+
return run;
|
|
537
|
+
};
|
|
538
|
+
export const syncToolsAsVectorEmbeddings = (serverName, tools, options) => enqueueEmbeddingTask(serverName, async () => {
|
|
523
539
|
const toolsWithOverrides = await applyDescriptionOverridesForEmbedding(serverName, tools);
|
|
524
540
|
const modelVisibleTools = filterModelVisibleTools(toolsWithOverrides);
|
|
525
541
|
if (modelVisibleTools.length === 0) {
|
|
@@ -529,6 +545,38 @@ const syncToolsAsVectorEmbeddings = async (serverName, tools, options) => {
|
|
|
529
545
|
return;
|
|
530
546
|
}
|
|
531
547
|
await saveToolsAsVectorEmbeddings(serverName, modelVisibleTools, options);
|
|
548
|
+
});
|
|
549
|
+
/**
|
|
550
|
+
* Embed the tools of a credential server, which never connects globally.
|
|
551
|
+
* Tools disabled in the server's tools config are never embedded: search
|
|
552
|
+
* filters them out of the hits anyway, but only after they have taken result
|
|
553
|
+
* slots. A toggle reaches the index without extra wiring: the tools config is
|
|
554
|
+
* part of the runtime revision, so the next acquire reconnects and re-syncs.
|
|
555
|
+
*
|
|
556
|
+
* Two callers, two scopes:
|
|
557
|
+
* - `partial: true` (every per-user connect): the list is what one user's
|
|
558
|
+
* credentials show, and users may see different tools. The rows of the
|
|
559
|
+
* listed, enabled tools are added or refreshed and nothing else is pruned,
|
|
560
|
+
* so tools only other users see stay indexed. The listed tools that are
|
|
561
|
+
* disabled are removed by name, queued before the sync.
|
|
562
|
+
* - otherwise (the reindex endpoint, which passes the union of every binding's
|
|
563
|
+
* tools): the list is complete, so rows of tools outside it are pruned.
|
|
564
|
+
*
|
|
565
|
+
* Resolves to the number of tools handed to the vector store.
|
|
566
|
+
*/
|
|
567
|
+
export const syncCredentialServerToolEmbeddings = async (serverName, tools, options) => {
|
|
568
|
+
const enabledTools = await filterToolsByConfig(serverName, tools);
|
|
569
|
+
if (options?.partial) {
|
|
570
|
+
const enabledNames = new Set(enabledTools.map((tool) => tool.name));
|
|
571
|
+
const disabledNames = tools
|
|
572
|
+
.map((tool) => tool.name)
|
|
573
|
+
.filter((toolName) => !enabledNames.has(toolName));
|
|
574
|
+
if (disabledNames.length > 0) {
|
|
575
|
+
void enqueueEmbeddingTask(serverName, () => removeToolEmbeddings(serverName, disabledNames)).catch(() => undefined);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
await syncToolsAsVectorEmbeddings(serverName, enabledTools, options);
|
|
579
|
+
return enabledTools.length;
|
|
532
580
|
};
|
|
533
581
|
// Normalize prompt payload to satisfy MCP ListPrompts response schema
|
|
534
582
|
const normalizePromptForList = (prompt) => {
|
|
@@ -691,7 +739,7 @@ const logListChangedRefreshError = (listType) => {
|
|
|
691
739
|
logger.warn(`Failed to refresh ${listType} list after upstream notification`);
|
|
692
740
|
};
|
|
693
741
|
const createUpstreamMcpClient = (name, getServerInfo) => {
|
|
694
|
-
return new
|
|
742
|
+
return new LegacyMcpClient({
|
|
695
743
|
name: `mcp-client-${name}`,
|
|
696
744
|
version: '1.0.0',
|
|
697
745
|
}, {
|
|
@@ -1029,10 +1077,17 @@ export const createRequestContextAwareFetch = (baseFetch, passthroughHeaderNames
|
|
|
1029
1077
|
if (Object.keys(passthroughHeaders).length === 0) {
|
|
1030
1078
|
return baseFetch(url, init);
|
|
1031
1079
|
}
|
|
1080
|
+
const headers = init?.headers;
|
|
1081
|
+
const entries = headers instanceof Headers
|
|
1082
|
+
? [...headers.entries()]
|
|
1083
|
+
: Array.isArray(headers)
|
|
1084
|
+
? headers
|
|
1085
|
+
: Object.entries(headers ?? {});
|
|
1086
|
+
const replacedNames = new Set(Object.keys(passthroughHeaders).map((name) => name.toLowerCase()));
|
|
1032
1087
|
return baseFetch(url, {
|
|
1033
1088
|
...init,
|
|
1034
1089
|
headers: {
|
|
1035
|
-
...
|
|
1090
|
+
...Object.fromEntries(entries.filter(([name]) => !replacedNames.has(name.toLowerCase()))),
|
|
1036
1091
|
...passthroughHeaders,
|
|
1037
1092
|
},
|
|
1038
1093
|
});
|
|
@@ -1070,11 +1125,17 @@ export const createTransportFromConfig = async (name, conf) => {
|
|
|
1070
1125
|
}
|
|
1071
1126
|
if (conf.type === 'streamable-http') {
|
|
1072
1127
|
const options = {};
|
|
1073
|
-
let headers = conf.headers
|
|
1128
|
+
let headers = conf.headers
|
|
1129
|
+
? hasCredentialTemplate(conf)
|
|
1130
|
+
? conf.headers
|
|
1131
|
+
: replaceEnvVars(conf.headers, env)
|
|
1132
|
+
: {};
|
|
1074
1133
|
const baseFetch = createAbortIsolatingFetch(createFetchWithProxy(getProxyConfigFromEnv(env)));
|
|
1075
1134
|
const requestAwareFetch = createRedirectValidatingFetch(createRequestContextAwareFetch(baseFetch, conf.passthroughHeaders), allowInternal);
|
|
1076
1135
|
// Create OAuth provider if configured - SDK will handle authentication automatically
|
|
1077
|
-
const authProvider = hasCredentialTemplate(conf)
|
|
1136
|
+
const authProvider = hasCredentialTemplate(conf)
|
|
1137
|
+
? undefined
|
|
1138
|
+
: await createOAuthProvider(name, conf);
|
|
1078
1139
|
if (authProvider) {
|
|
1079
1140
|
options.authProvider = authProvider;
|
|
1080
1141
|
// When the OAuth provider is active, strip any static Authorization
|
|
@@ -1097,11 +1158,17 @@ export const createTransportFromConfig = async (name, conf) => {
|
|
|
1097
1158
|
else if (conf.url) {
|
|
1098
1159
|
// SSE transport
|
|
1099
1160
|
const options = {};
|
|
1100
|
-
let headers = conf.headers
|
|
1161
|
+
let headers = conf.headers
|
|
1162
|
+
? hasCredentialTemplate(conf)
|
|
1163
|
+
? conf.headers
|
|
1164
|
+
: replaceEnvVars(conf.headers, env)
|
|
1165
|
+
: {};
|
|
1101
1166
|
const baseFetch = createAbortIsolatingFetch(createFetchWithProxy(getProxyConfigFromEnv(env)));
|
|
1102
1167
|
const requestAwareFetch = createRedirectValidatingFetch(createRequestContextAwareFetch(baseFetch, conf.passthroughHeaders), allowInternal);
|
|
1103
1168
|
// Create OAuth provider if configured - SDK will handle authentication automatically
|
|
1104
|
-
const authProvider = hasCredentialTemplate(conf)
|
|
1169
|
+
const authProvider = hasCredentialTemplate(conf)
|
|
1170
|
+
? undefined
|
|
1171
|
+
: await createOAuthProvider(name, conf);
|
|
1105
1172
|
if (authProvider) {
|
|
1106
1173
|
options.authProvider = authProvider;
|
|
1107
1174
|
// Drop static Authorization header when OAuth manages auth (see above).
|
|
@@ -1189,11 +1256,11 @@ const callToolWithReconnect = async (serverInfo, toolParams, options, maxRetries
|
|
|
1189
1256
|
throw new Error(`Client not found for server: ${serverInfo.name}`);
|
|
1190
1257
|
}
|
|
1191
1258
|
if (hasCredentialTemplate(serverInfo.config)) {
|
|
1192
|
-
return client.callTool(toolParams,
|
|
1259
|
+
return client.callTool(toolParams, options || {});
|
|
1193
1260
|
}
|
|
1194
1261
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1195
1262
|
try {
|
|
1196
|
-
const result = await client.callTool(toolParams,
|
|
1263
|
+
const result = await client.callTool(toolParams, options || {});
|
|
1197
1264
|
// Check auth error
|
|
1198
1265
|
checkAuthError(result);
|
|
1199
1266
|
return result;
|
|
@@ -1206,10 +1273,12 @@ const callToolWithReconnect = async (serverInfo, toolParams, options, maxRetries
|
|
|
1206
1273
|
if (attempt < maxRetries && transport && ((isStreamableHttp && isHttp40xError) || isSSE)) {
|
|
1207
1274
|
logger.warn(`${isHttp40xError ? 'HTTP 40x error' : 'error'} detected for ${isStreamableHttp ? 'StreamableHTTP' : 'SSE'} server ${serverInfo.name}${isolated ? ` (isolated session ${isolated.sessionId})` : ''}, attempting reconnection (attempt ${attempt + 1}/${maxRetries + 1})`);
|
|
1208
1275
|
try {
|
|
1209
|
-
const
|
|
1210
|
-
if (!
|
|
1276
|
+
const rawConfig = await getServerDao().findById(serverInfo.name);
|
|
1277
|
+
if (!rawConfig) {
|
|
1211
1278
|
throw new Error(`Server configuration not found for: ${serverInfo.name}`);
|
|
1212
1279
|
}
|
|
1280
|
+
// Match initial connection expansion before URL validation and transport creation.
|
|
1281
|
+
const server = replaceEnvVars(rawConfig);
|
|
1213
1282
|
const newTransport = await createTransportFromConfig(serverInfo.name, server);
|
|
1214
1283
|
const newClient = createUpstreamMcpClient(serverInfo.name, () => serverInfo);
|
|
1215
1284
|
// Reconnect with new transport
|
|
@@ -1363,10 +1432,18 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
|
|
|
1363
1432
|
if (hasCredentialTemplate(expandedConf)) {
|
|
1364
1433
|
validateCredentialTemplate(expandedConf);
|
|
1365
1434
|
nextServerInfos.push({
|
|
1366
|
-
name,
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1435
|
+
name,
|
|
1436
|
+
owner: expandedConf.owner,
|
|
1437
|
+
visibility: expandedConf.visibility,
|
|
1438
|
+
sharedWithUsers: expandedConf.sharedWithUsers,
|
|
1439
|
+
enabled: true,
|
|
1440
|
+
status: 'disconnected',
|
|
1441
|
+
error: null,
|
|
1442
|
+
tools: [],
|
|
1443
|
+
prompts: [],
|
|
1444
|
+
resources: [],
|
|
1445
|
+
createTime: Date.now(),
|
|
1446
|
+
config: expandedConf,
|
|
1370
1447
|
});
|
|
1371
1448
|
continue;
|
|
1372
1449
|
}
|
|
@@ -1952,7 +2029,9 @@ export const getServersInfo = async (page, limit, user) => {
|
|
|
1952
2029
|
config: resolvedType || serverConfig?.description || serverConfig?.command
|
|
1953
2030
|
? {
|
|
1954
2031
|
...(resolvedType ? { type: resolvedType } : {}),
|
|
1955
|
-
...(hasCredentialTemplate(serverConfig)
|
|
2032
|
+
...(hasCredentialTemplate(serverConfig)
|
|
2033
|
+
? { credentialTemplate: validateCredentialTemplate(serverConfig) }
|
|
2034
|
+
: {}),
|
|
1956
2035
|
...(serverConfig?.description ? { description: serverConfig.description } : {}),
|
|
1957
2036
|
// Expose command so the frontend can determine if reinstall is
|
|
1958
2037
|
// supported (npx/uvx only). This is not a secret — it's the
|
|
@@ -2851,17 +2930,7 @@ const projectToolForDownstream = (serverName, tool, appsRouteContext, serverConf
|
|
|
2851
2930
|
name: projectNameForGroup(projectedTool.name, serverName, serverConfig),
|
|
2852
2931
|
};
|
|
2853
2932
|
};
|
|
2854
|
-
const
|
|
2855
|
-
const sessionId = extra.sessionId || '';
|
|
2856
|
-
const group = getGroup(sessionId);
|
|
2857
|
-
logger.log(`Handling ListToolsRequest for group: ${group}`);
|
|
2858
|
-
// Special handling for $smart group to return smart routing tools
|
|
2859
|
-
// Support both $smart and $smart/{group} patterns
|
|
2860
|
-
if (isSmartRoutingGroup(group)) {
|
|
2861
|
-
return getSmartRoutingTools(group);
|
|
2862
|
-
}
|
|
2863
|
-
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(group);
|
|
2864
|
-
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
2933
|
+
const listGroupTools = async (group, filteredServerInfos, serverConfigsByName, appsRouteContext) => {
|
|
2865
2934
|
// If the startup prime of an on-demand server is still in flight, wait for it
|
|
2866
2935
|
// so this list reflects the freshly cached tools instead of returning empty.
|
|
2867
2936
|
// No wake is triggered from list itself; the prime handles that. See #1029.
|
|
@@ -2891,8 +2960,60 @@ const handleListToolsRequestImpl = async (_, extra) => {
|
|
|
2891
2960
|
}));
|
|
2892
2961
|
}
|
|
2893
2962
|
}
|
|
2963
|
+
return allTools;
|
|
2964
|
+
};
|
|
2965
|
+
// Names the call handler always routes to a meta-tool on a Smart Routing session,
|
|
2966
|
+
// whichever of them the current mode lists (see handleCallToolRequestImpl)
|
|
2967
|
+
const SMART_ROUTING_META_TOOL_NAMES = new Set(['search_tools', 'describe_tool', 'call_tool']);
|
|
2968
|
+
// Tools a $smart/<group> lists next to its meta-tools: each member's pinnedTools,
|
|
2969
|
+
// narrowed to its tools selection and then run through the same filtering and
|
|
2970
|
+
// projection as a direct group listing.
|
|
2971
|
+
const getPinnedSmartRoutingTools = async (group) => {
|
|
2972
|
+
const lookupGroup = getGroupLookupName(group);
|
|
2973
|
+
if (!lookupGroup) {
|
|
2974
|
+
return [];
|
|
2975
|
+
}
|
|
2976
|
+
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup);
|
|
2977
|
+
const pinnedConfigsByName = new Map();
|
|
2978
|
+
for (const [serverName, serverConfig] of serverConfigsByName) {
|
|
2979
|
+
const selection = serverConfig.tools;
|
|
2980
|
+
const pinnedTools = (serverConfig.pinnedTools ?? []).filter((toolName) => !Array.isArray(selection) || selection.includes(toolName));
|
|
2981
|
+
if (pinnedTools.length > 0) {
|
|
2982
|
+
pinnedConfigsByName.set(serverName, { ...serverConfig, tools: pinnedTools });
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
if (pinnedConfigsByName.size === 0) {
|
|
2986
|
+
return [];
|
|
2987
|
+
}
|
|
2988
|
+
return listGroupTools(lookupGroup, filteredServerInfos.filter((serverInfo) => pinnedConfigsByName.has(serverInfo.name)), pinnedConfigsByName, { enabled: false });
|
|
2989
|
+
};
|
|
2990
|
+
const handleListToolsRequestImpl = async (_, extra) => {
|
|
2991
|
+
const sessionId = extra.sessionId || '';
|
|
2992
|
+
const group = getMcpRequestGroup(extra);
|
|
2993
|
+
logger.log(`Handling ListToolsRequest for group: ${group}`);
|
|
2994
|
+
// Special handling for $smart group to return smart routing tools
|
|
2995
|
+
// Support both $smart and $smart/{group} patterns
|
|
2996
|
+
if (isSmartRoutingGroup(group)) {
|
|
2997
|
+
const smartRoutingTools = await getSmartRoutingTools(group);
|
|
2998
|
+
let pinnedTools = [];
|
|
2999
|
+
try {
|
|
3000
|
+
// A pin named like a meta-tool (e.g. alias "describe" + separator "_" +
|
|
3001
|
+
// tool "tool") would be intercepted by it on call, even in a mode that
|
|
3002
|
+
// does not list that meta-tool, so it is not listed at all
|
|
3003
|
+
pinnedTools = (await getPinnedSmartRoutingTools(group)).filter((tool) => !SMART_ROUTING_META_TOOL_NAMES.has(tool.name));
|
|
3004
|
+
}
|
|
3005
|
+
catch (error) {
|
|
3006
|
+
// Pins are an optimisation: failing to resolve them must not cost the meta-tools
|
|
3007
|
+
logger.warn(`Failed to list pinned tools for ${group}; returning meta-tools only`, error);
|
|
3008
|
+
}
|
|
3009
|
+
return pinnedTools.length > 0
|
|
3010
|
+
? { tools: [...smartRoutingTools.tools, ...pinnedTools] }
|
|
3011
|
+
: smartRoutingTools;
|
|
3012
|
+
}
|
|
3013
|
+
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(group);
|
|
3014
|
+
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
2894
3015
|
return {
|
|
2895
|
-
tools:
|
|
3016
|
+
tools: await listGroupTools(group, filteredServerInfos, serverConfigsByName, appsRouteContext),
|
|
2896
3017
|
};
|
|
2897
3018
|
};
|
|
2898
3019
|
const handleCallToolRequestImpl = async (request, extra) => {
|
|
@@ -2917,7 +3038,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
2917
3038
|
].find((id) => typeof id === 'string' && id.length > 0 && !isSyntheticSessionFallback(id));
|
|
2918
3039
|
// Extract group and key info from request context (set by SSE/HTTP handlers)
|
|
2919
3040
|
// Fallback to extra for backward compatibility (e.g., direct API calls)
|
|
2920
|
-
const group =
|
|
3041
|
+
const group = getMcpRequestGroup(extra);
|
|
2921
3042
|
const username = requestContextService.getUsernameContext() ||
|
|
2922
3043
|
extra?.username ||
|
|
2923
3044
|
(requestContextService.getKeyKindContext() === 'system' ? 'system' : undefined) ||
|
|
@@ -2948,12 +3069,12 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
2948
3069
|
// Special handling for smart routing tools
|
|
2949
3070
|
if (request.params.name === 'search_tools') {
|
|
2950
3071
|
const { query, limit = 10 } = request.params.arguments || {};
|
|
2951
|
-
return await handleSearchToolsRequest(query, limit, sessionId);
|
|
3072
|
+
return await handleSearchToolsRequest(query, limit, sessionId, group);
|
|
2952
3073
|
}
|
|
2953
3074
|
// Special handling for describe_tool (progressive disclosure mode)
|
|
2954
3075
|
if (request.params.name === 'describe_tool') {
|
|
2955
3076
|
const { toolName } = request.params.arguments || {};
|
|
2956
|
-
return await handleDescribeToolRequest(toolName, sessionId);
|
|
3077
|
+
return await handleDescribeToolRequest(toolName, sessionId, group);
|
|
2957
3078
|
}
|
|
2958
3079
|
// Special handling for call_tool
|
|
2959
3080
|
if (request.params.name === 'call_tool') {
|
|
@@ -3051,6 +3172,9 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
3051
3172
|
}
|
|
3052
3173
|
}
|
|
3053
3174
|
}
|
|
3175
|
+
if (targetServerInfo.config?.openapi?.cookieSession && !cookieSessionId) {
|
|
3176
|
+
throw new Error(`OpenAPI server '${targetServerInfo.name}' requires cookie-session state and is not yet available on stateless MCP requests`);
|
|
3177
|
+
}
|
|
3054
3178
|
await reserveHostedIfNeeded(targetServerInfo.name, cleanToolName);
|
|
3055
3179
|
const result = await openApiClient.callTool(cleanToolName, finalArgs, passthroughHeaders, false, cookieSessionId);
|
|
3056
3180
|
await settleHostedIfNeeded({
|
|
@@ -3095,7 +3219,11 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
3095
3219
|
// Call the tool on the target server (MCP servers)
|
|
3096
3220
|
// For servers with perSessionClient: true, use a per-session dedicated client
|
|
3097
3221
|
let isolatedCtx;
|
|
3098
|
-
if (targetServerInfo.config?.perSessionClient &&
|
|
3222
|
+
if (targetServerInfo.config?.perSessionClient &&
|
|
3223
|
+
!hasCredentialTemplate(targetServerInfo.config)) {
|
|
3224
|
+
if (!sessionId) {
|
|
3225
|
+
throw new Error(`Server '${targetServerInfo.name}' requires per-session client isolation and is not yet available on stateless MCP requests`);
|
|
3226
|
+
}
|
|
3099
3227
|
const isolated = await getOrCreateIsolatedClient(sessionId, targetServerInfo);
|
|
3100
3228
|
isolatedCtx = { sessionId, client: isolated.client, transport: isolated.transport };
|
|
3101
3229
|
}
|
|
@@ -3215,6 +3343,9 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
3215
3343
|
}
|
|
3216
3344
|
}
|
|
3217
3345
|
const finalArgs = request.params.arguments || {};
|
|
3346
|
+
if (serverInfo.config?.openapi?.cookieSession && !cookieSessionId) {
|
|
3347
|
+
throw new Error(`OpenAPI server '${serverInfo.name}' requires cookie-session state and is not yet available on stateless MCP requests`);
|
|
3348
|
+
}
|
|
3218
3349
|
await reserveHostedIfNeeded(serverInfo.name, cleanToolName);
|
|
3219
3350
|
const result = await openApiClient.callTool(cleanToolName, finalArgs, passthroughHeaders, false, cookieSessionId);
|
|
3220
3351
|
await settleHostedIfNeeded({
|
|
@@ -3259,7 +3390,10 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
3259
3390
|
// Handle MCP servers
|
|
3260
3391
|
// For servers with perSessionClient: true, use a per-session dedicated client
|
|
3261
3392
|
let isolatedCtx;
|
|
3262
|
-
if (serverInfo.config?.perSessionClient && !hasCredentialTemplate(serverInfo.config)
|
|
3393
|
+
if (serverInfo.config?.perSessionClient && !hasCredentialTemplate(serverInfo.config)) {
|
|
3394
|
+
if (!sessionId) {
|
|
3395
|
+
throw new Error(`Server '${serverInfo.name}' requires per-session client isolation and is not yet available on stateless MCP requests`);
|
|
3396
|
+
}
|
|
3263
3397
|
const isolated = await getOrCreateIsolatedClient(sessionId, serverInfo);
|
|
3264
3398
|
isolatedCtx = { sessionId, client: isolated.client, transport: isolated.transport };
|
|
3265
3399
|
}
|
|
@@ -3359,8 +3493,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
|
|
|
3359
3493
|
const handleGetPromptRequestImpl = async (request, extra) => {
|
|
3360
3494
|
try {
|
|
3361
3495
|
const { name, arguments: promptArgs } = request.params;
|
|
3362
|
-
const
|
|
3363
|
-
const group = extra?.group || getGroup(sessionId) || undefined;
|
|
3496
|
+
const group = getMcpRequestGroup(extra);
|
|
3364
3497
|
// Check built-in prompts first
|
|
3365
3498
|
const builtinPrompt = await getBuiltinPromptDao().findByName(name);
|
|
3366
3499
|
if (builtinPrompt && builtinPrompt.enabled !== false) {
|
|
@@ -3446,8 +3579,7 @@ const handleGetPromptRequestImpl = async (request, extra) => {
|
|
|
3446
3579
|
}
|
|
3447
3580
|
};
|
|
3448
3581
|
const handleListPromptsRequestImpl = async (_, extra) => {
|
|
3449
|
-
const
|
|
3450
|
-
const group = getGroup(sessionId);
|
|
3582
|
+
const group = getMcpRequestGroup(extra);
|
|
3451
3583
|
const lookupGroup = getGroupLookupName(group);
|
|
3452
3584
|
logger.log(`Handling ListPromptsRequest for group: ${group}`);
|
|
3453
3585
|
// Start with built-in prompts (only enabled ones)
|
|
@@ -3491,7 +3623,7 @@ const handleListPromptsRequestImpl = async (_, extra) => {
|
|
|
3491
3623
|
};
|
|
3492
3624
|
const handleListResourcesRequestImpl = async (_, extra) => {
|
|
3493
3625
|
const sessionId = extra.sessionId || '';
|
|
3494
|
-
const group =
|
|
3626
|
+
const group = getMcpRequestGroup(extra);
|
|
3495
3627
|
const lookupGroup = getGroupLookupName(group);
|
|
3496
3628
|
logger.log(`Handling ListResourcesRequest for group: ${group}`);
|
|
3497
3629
|
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
@@ -3536,7 +3668,7 @@ const handleListResourcesRequestImpl = async (_, extra) => {
|
|
|
3536
3668
|
};
|
|
3537
3669
|
const handleListResourceTemplatesRequestImpl = async (_, extra) => {
|
|
3538
3670
|
const sessionId = extra.sessionId || '';
|
|
3539
|
-
const group =
|
|
3671
|
+
const group = getMcpRequestGroup(extra);
|
|
3540
3672
|
const lookupGroup = getGroupLookupName(group);
|
|
3541
3673
|
logger.log(`Handling ListResourceTemplatesRequest for group: ${group}`);
|
|
3542
3674
|
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
@@ -3561,7 +3693,7 @@ const handleReadResourceRequestImpl = async (request, extra) => {
|
|
|
3561
3693
|
try {
|
|
3562
3694
|
const { uri } = request.params;
|
|
3563
3695
|
const sessionId = extra.sessionId || '';
|
|
3564
|
-
const group =
|
|
3696
|
+
const group = getMcpRequestGroup(extra);
|
|
3565
3697
|
const lookupGroup = getGroupLookupName(group);
|
|
3566
3698
|
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
3567
3699
|
// Check built-in resources first
|
|
@@ -3654,7 +3786,9 @@ const handleReadResourceRequestImpl = async (request, extra) => {
|
|
|
3654
3786
|
};
|
|
3655
3787
|
}
|
|
3656
3788
|
};
|
|
3657
|
-
// Personal runtimes never enter serverInfos
|
|
3789
|
+
// Personal runtimes never enter serverInfos or config exports. Their tool list
|
|
3790
|
+
// is added to the rows under the server name, the index the reindex endpoint
|
|
3791
|
+
// rebuilds from every binding.
|
|
3658
3792
|
const createPrincipalRuntime = async (name, resolvedConfig) => {
|
|
3659
3793
|
const info = {
|
|
3660
3794
|
name,
|
|
@@ -3706,9 +3840,7 @@ const createPrincipalRuntime = async (name, resolvedConfig) => {
|
|
|
3706
3840
|
const client = new OpenAPIClient(resolvedConfig);
|
|
3707
3841
|
await client.initialize();
|
|
3708
3842
|
info.openApiClient = protectClient(client);
|
|
3709
|
-
info.tools = client
|
|
3710
|
-
.getTools()
|
|
3711
|
-
.map((tool) => ({
|
|
3843
|
+
info.tools = client.getTools().map((tool) => ({
|
|
3712
3844
|
...tool,
|
|
3713
3845
|
name: `${name}${getNameSeparator()}${tool.name}`,
|
|
3714
3846
|
inputSchema: cleanInputSchema(tool.inputSchema),
|
|
@@ -3716,7 +3848,7 @@ const createPrincipalRuntime = async (name, resolvedConfig) => {
|
|
|
3716
3848
|
}
|
|
3717
3849
|
else {
|
|
3718
3850
|
const transport = await createTransportFromConfig(name, resolvedConfig);
|
|
3719
|
-
const client = new
|
|
3851
|
+
const client = new LegacyMcpClient({ name: `mcp-client-${name}`, version: '1.0.0' }, {
|
|
3720
3852
|
capabilities: MCP_APPS_CAPABILITIES,
|
|
3721
3853
|
jsonSchemaValidator: new ResilientJsonSchemaValidator(),
|
|
3722
3854
|
});
|
|
@@ -3738,6 +3870,12 @@ const createPrincipalRuntime = async (name, resolvedConfig) => {
|
|
|
3738
3870
|
};
|
|
3739
3871
|
}
|
|
3740
3872
|
info.status = 'connected';
|
|
3873
|
+
// A credential server never connects globally, so this is the only place
|
|
3874
|
+
// its tools are seen outside a reindex. One user's list is not the whole
|
|
3875
|
+
// server's, so this only adds and refreshes rows; unchanged ones are skipped.
|
|
3876
|
+
syncCredentialServerToolEmbeddings(name, info.tools, { partial: true }).catch(() => {
|
|
3877
|
+
logger.warn(`[EMBED_SYNC_ERROR] Failed to sync tool embeddings for "${name}"`);
|
|
3878
|
+
});
|
|
3741
3879
|
return info;
|
|
3742
3880
|
}
|
|
3743
3881
|
catch (error) {
|
|
@@ -3784,19 +3922,14 @@ const withPrincipalServers = (handler, operation) => async (request, extra) => {
|
|
|
3784
3922
|
if (builtin && builtin.enabled !== false)
|
|
3785
3923
|
return handler(request, extra);
|
|
3786
3924
|
}
|
|
3787
|
-
const group =
|
|
3788
|
-
extra?.group ||
|
|
3789
|
-
getGroup(extra?.sessionId || '') ||
|
|
3790
|
-
undefined;
|
|
3925
|
+
const group = getMcpRequestGroup(extra);
|
|
3791
3926
|
const { filteredServerInfos: candidates, serverConfigsByName } = await getFilteredServerInfosForGroup(getGroupLookupName(group));
|
|
3792
3927
|
const requestedName = ['call_tool', 'describe_tool'].includes(request?.params?.name)
|
|
3793
3928
|
? request.params.arguments?.toolName
|
|
3794
3929
|
: request?.params?.name;
|
|
3795
3930
|
// An explicit REST target takes precedence over every qualified-name prefix.
|
|
3796
3931
|
// Otherwise prefer the longest exposed prefix, including group aliases.
|
|
3797
|
-
let target = extra?.server
|
|
3798
|
-
? candidates.find((info) => info.name === extra.server)
|
|
3799
|
-
: undefined;
|
|
3932
|
+
let target = extra?.server ? candidates.find((info) => info.name === extra.server) : undefined;
|
|
3800
3933
|
if (extra?.server && !target && (operation === 'tool' || operation === 'prompt')) {
|
|
3801
3934
|
throw new ToolUnavailableError(`Tool not available: ${requestedName}`, 'server-or-name-not-found');
|
|
3802
3935
|
}
|
|
@@ -3898,6 +4031,7 @@ export const createMcpServer = (name, version, options) => {
|
|
|
3898
4031
|
}
|
|
3899
4032
|
// If no group, use default name (global routing)
|
|
3900
4033
|
const server = new Server({ name: serverName, version }, {
|
|
4034
|
+
supportedProtocolVersions: LEGACY_PROTOCOL_VERSIONS,
|
|
3901
4035
|
capabilities: {
|
|
3902
4036
|
tools: { listChanged: true },
|
|
3903
4037
|
prompts: { listChanged: true },
|
|
@@ -3908,13 +4042,13 @@ export const createMcpServer = (name, version, options) => {
|
|
|
3908
4042
|
? { instructions: normalizedOptions.instructions }
|
|
3909
4043
|
: {}),
|
|
3910
4044
|
});
|
|
3911
|
-
server.setRequestHandler(
|
|
3912
|
-
server.setRequestHandler(
|
|
3913
|
-
server.setRequestHandler(
|
|
3914
|
-
server.setRequestHandler(
|
|
3915
|
-
server.setRequestHandler(
|
|
3916
|
-
server.setRequestHandler(
|
|
3917
|
-
server.setRequestHandler(
|
|
4045
|
+
server.setRequestHandler('tools/list', handleListToolsRequest);
|
|
4046
|
+
server.setRequestHandler('tools/call', handleCallToolRequest);
|
|
4047
|
+
server.setRequestHandler('prompts/get', handleGetPromptRequest);
|
|
4048
|
+
server.setRequestHandler('prompts/list', handleListPromptsRequest);
|
|
4049
|
+
server.setRequestHandler('resources/list', handleListResourcesRequest);
|
|
4050
|
+
server.setRequestHandler('resources/templates/list', handleListResourceTemplatesRequest);
|
|
4051
|
+
server.setRequestHandler('resources/read', handleReadResourceRequest);
|
|
3918
4052
|
return server;
|
|
3919
4053
|
};
|
|
3920
4054
|
export const getFilteredServerInfosForGroup = async (group, options) => {
|