@samanhappy/mcphub 1.0.18 → 1.0.20
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/clients/openapi.js +28 -0
- package/dist/clients/openapi.js.map +1 -1
- package/dist/controllers/serverController.js +22 -4
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/controllers/userController.js +10 -1
- package/dist/controllers/userController.js.map +1 -1
- package/dist/dao/SystemConfigDaoDbImpl.js +3 -0
- package/dist/dao/SystemConfigDaoDbImpl.js.map +1 -1
- package/dist/db/entities/SystemConfig.js +4 -0
- package/dist/db/entities/SystemConfig.js.map +1 -1
- package/dist/services/activityLoggingService.js +36 -6
- package/dist/services/activityLoggingService.js.map +1 -1
- package/dist/services/mcpService.js +54 -17
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/requestContextService.js +15 -0
- package/dist/services/requestContextService.js.map +1 -1
- package/dist/services/sseService.js +5 -2
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/userService.js +18 -0
- package/dist/services/userService.js.map +1 -1
- package/dist/utils/serialization.js +85 -12
- package/dist/utils/serialization.js.map +1 -1
- package/dist/utils/ssrf.js +155 -0
- package/dist/utils/ssrf.js.map +1 -0
- package/dist/utils/tokenTruncation.js +14 -0
- package/dist/utils/tokenTruncation.js.map +1 -1
- package/frontend/dist/assets/{ActivityPage-C36IBbnj.js → ActivityPage-CmDatV8h.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-C36IBbnj.js.map → ActivityPage-CmDatV8h.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-BsUNNsqb.js → Dashboard-D4mJrnD5.js} +2 -2
- package/frontend/dist/assets/{Dashboard-BsUNNsqb.js.map → Dashboard-D4mJrnD5.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-eF3xFbaZ.js → EndpointCopy-YSgbA8oS.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-eF3xFbaZ.js.map → EndpointCopy-YSgbA8oS.js.map} +1 -1
- package/frontend/dist/assets/{GroupsPage-Dp-NU5wZ.js → GroupsPage-Dbsjqynx.js} +2 -2
- package/frontend/dist/assets/{GroupsPage-Dp-NU5wZ.js.map → GroupsPage-Dbsjqynx.js.map} +1 -1
- package/frontend/dist/assets/{LoginPage-9PIMlvGe.js → LoginPage-BJsFWBRX.js} +2 -2
- package/frontend/dist/assets/{LoginPage-9PIMlvGe.js.map → LoginPage-BJsFWBRX.js.map} +1 -1
- package/frontend/dist/assets/{LogsPage-C8WS43Dw.js → LogsPage-sKpr_d-I.js} +2 -2
- package/frontend/dist/assets/{LogsPage-C8WS43Dw.js.map → LogsPage-sKpr_d-I.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-Cyl5VvwI.js → MarketPage-Dl66sBVk.js} +2 -2
- package/frontend/dist/assets/{MarketPage-Cyl5VvwI.js.map → MarketPage-Dl66sBVk.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-CqxKWAR9.js → PromptsPage-BIJz-1mn.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-CqxKWAR9.js.map → PromptsPage-BIJz-1mn.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-Dq7Grza0.js → ResourcesPage-CVZLtp7s.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-Dq7Grza0.js.map → ResourcesPage-CVZLtp7s.js.map} +1 -1
- package/frontend/dist/assets/ServersPage-CJQ-LCYS.js +37 -0
- package/frontend/dist/assets/ServersPage-CJQ-LCYS.js.map +1 -0
- package/frontend/dist/assets/SettingsPage-CzbexjdA.js +12 -0
- package/frontend/dist/assets/SettingsPage-CzbexjdA.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-DAStUyI3.js → StatusDot-9Gg6xqv-.js} +2 -2
- package/frontend/dist/assets/{StatusDot-DAStUyI3.js.map → StatusDot-9Gg6xqv-.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-CQvqGQLA.js → ToggleGroup-C6qyrY2I.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-CQvqGQLA.js.map → ToggleGroup-C6qyrY2I.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-Bsa2NGcJ.js → UsersPage-D4jwwoib.js} +2 -2
- package/frontend/dist/assets/{UsersPage-Bsa2NGcJ.js.map → UsersPage-D4jwwoib.js.map} +1 -1
- package/frontend/dist/assets/{contextCost-DrQqHXcP.js → contextCost-nh7hHroj.js} +2 -2
- package/frontend/dist/assets/{contextCost-DrQqHXcP.js.map → contextCost-nh7hHroj.js.map} +1 -1
- package/frontend/dist/assets/index-CGgjgCxq.js +3 -0
- package/frontend/dist/assets/index-CGgjgCxq.js.map +1 -0
- package/frontend/dist/assets/{resourceService-Bg941cnv.js → resourceService-BJBWJ8vG.js} +2 -2
- package/frontend/dist/assets/{resourceService-Bg941cnv.js.map → resourceService-BJBWJ8vG.js.map} +1 -1
- package/frontend/dist/assets/useSettingsData-ClaXqU1-.js +2 -0
- package/frontend/dist/assets/{useSettingsData-BwKohDXD.js.map → useSettingsData-ClaXqU1-.js.map} +1 -1
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
- package/frontend/dist/assets/ServersPage-xt7QkwjZ.js +0 -37
- package/frontend/dist/assets/ServersPage-xt7QkwjZ.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-Cn5Krvgb.js +0 -12
- package/frontend/dist/assets/SettingsPage-Cn5Krvgb.js.map +0 -1
- package/frontend/dist/assets/index-BsgjLwhT.js +0 -3
- package/frontend/dist/assets/index-BsgjLwhT.js.map +0 -1
- package/frontend/dist/assets/useSettingsData-BwKohDXD.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SystemConfig.js","sourceRoot":"","sources":["../../../src/db/entities/SystemConfig.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5F;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;
|
|
1
|
+
{"version":3,"file":"SystemConfig.js","sourceRoot":"","sources":["../../../src/db/entities/SystemConfig.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5F;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;CA6CxB,CAAA;AA3CC;IADC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;;wCACxD;AAGX;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CAClB;AAG9B;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CAClB;AAG9B;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACb;AAGnC;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2DACJ;AAG5C;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CAChB;AAGhC;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACjC;AAGvB;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACpB;AAG5B;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACd;AAGlC;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACrB;AAG3B;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0DACb;AAG/B;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CAChB;AAGhC;IADC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACd;AAGlC;IADC,gBAAgB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;8BACjD,IAAI;+CAAC;AAGhB;IADC,gBAAgB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;8BACjD,IAAI;+CAAC;AA5CL,YAAY;IADxB,MAAM,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;GACrB,YAAY,CA6CxB;;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { getActivityDao, isActivityLoggingEnabled } from '../dao/DaoFactory.js';
|
|
2
|
-
import {
|
|
2
|
+
import { stringifyWithoutRedaction, sanitizeStringForLogging, } from '../utils/serialization.js';
|
|
3
|
+
import { getCachedSystemConfig } from '../utils/systemConfigCache.js';
|
|
4
|
+
const PAYLOAD_OMITTED = JSON.stringify({
|
|
5
|
+
_omitted: true,
|
|
6
|
+
_reason: 'activityLog.storeToolPayload is disabled',
|
|
7
|
+
});
|
|
3
8
|
/**
|
|
4
9
|
* Service for logging tool call activities
|
|
5
10
|
* Only logs when in database mode
|
|
@@ -32,14 +37,15 @@ export class ActivityLoggingService {
|
|
|
32
37
|
return;
|
|
33
38
|
}
|
|
34
39
|
try {
|
|
40
|
+
const storePayload = this.shouldStoreToolPayload();
|
|
35
41
|
const activity = {
|
|
36
42
|
timestamp: new Date(),
|
|
37
43
|
server: params.server,
|
|
38
44
|
tool: params.tool,
|
|
39
45
|
duration: params.duration,
|
|
40
46
|
status: params.status,
|
|
41
|
-
input:
|
|
42
|
-
output:
|
|
47
|
+
input: this.serializePayload(params.input, storePayload),
|
|
48
|
+
output: this.serializePayload(params.output, storePayload),
|
|
43
49
|
group: params.group,
|
|
44
50
|
username: params.username,
|
|
45
51
|
keyId: params.keyId,
|
|
@@ -57,12 +63,36 @@ export class ActivityLoggingService {
|
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
/**
|
|
60
|
-
*
|
|
66
|
+
* Whether to persist full tool call payloads. Defaults to true; deployments
|
|
67
|
+
* that treat tool arguments as sensitive can opt out via system config.
|
|
68
|
+
*/
|
|
69
|
+
shouldStoreToolPayload() {
|
|
70
|
+
return getCachedSystemConfig()?.activityLog?.storeToolPayload !== false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Serialize a tool call payload for storage.
|
|
74
|
+
*
|
|
75
|
+
* Payloads are stored verbatim (no field-level redaction): heuristic
|
|
76
|
+
* redaction corrupts the audit record on false positives and gives false
|
|
77
|
+
* assurance on false negatives, so the decision is whether to store at all.
|
|
78
|
+
*/
|
|
79
|
+
serializePayload(payload, storePayload) {
|
|
80
|
+
if (payload === undefined || payload === null) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
if (!storePayload) {
|
|
84
|
+
return PAYLOAD_OMITTED;
|
|
85
|
+
}
|
|
86
|
+
return this.safeStringify(payload);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Stringify an object without redaction, handling circular references and
|
|
90
|
+
* capping the stored size.
|
|
61
91
|
*/
|
|
62
92
|
safeStringify(obj) {
|
|
63
93
|
try {
|
|
64
94
|
// Limit the size of the stringified data
|
|
65
|
-
const str =
|
|
95
|
+
const str = stringifyWithoutRedaction(obj, 2);
|
|
66
96
|
// Limit to 100KB
|
|
67
97
|
if (str.length > 100000) {
|
|
68
98
|
return JSON.stringify({
|
|
@@ -73,7 +103,7 @@ export class ActivityLoggingService {
|
|
|
73
103
|
}
|
|
74
104
|
return str;
|
|
75
105
|
}
|
|
76
|
-
catch (
|
|
106
|
+
catch (_error) {
|
|
77
107
|
return JSON.stringify({
|
|
78
108
|
_error: 'Failed to stringify data',
|
|
79
109
|
_type: typeof obj,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activityLoggingService.js","sourceRoot":"","sources":["../../src/services/activityLoggingService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"activityLoggingService.js","sourceRoot":"","sources":["../../src/services/activityLoggingService.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,8 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
|
10
10
|
import { StreamableHTTPClientTransport, } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
11
11
|
import { normalizeHeaders } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
12
12
|
import { createFetchWithProxy, getProxyConfigFromEnv } from './proxy.js';
|
|
13
|
+
import { assertSafeUrl, createRedirectValidatingFetch } from '../utils/ssrf.js';
|
|
14
|
+
import { getUserDao } from '../dao/index.js';
|
|
13
15
|
import { expandEnvVars, replaceEnvVars, getNameSeparator } from '../config/index.js';
|
|
14
16
|
import config from '../config/index.js';
|
|
15
17
|
import { getGroup } from './sseService.js';
|
|
@@ -698,11 +700,23 @@ export const createTransportFromConfig = async (name, conf) => {
|
|
|
698
700
|
...process.env,
|
|
699
701
|
...replaceEnvVars(conf.env || {}),
|
|
700
702
|
};
|
|
703
|
+
// SSRF guard: block URL/streamable-http transports from reaching
|
|
704
|
+
// loopback / RFC1918 / link-local targets (e.g. cloud metadata service).
|
|
705
|
+
// Admin-owned servers may legitimately target internal services, so they
|
|
706
|
+
// skip the internal-IP blocklist. allowInternal also governs per-hop
|
|
707
|
+
// redirect validation in createRedirectValidatingFetch below.
|
|
708
|
+
const ownerUser = conf.owner
|
|
709
|
+
? await getUserDao().findByUsername(conf.owner)
|
|
710
|
+
: null;
|
|
711
|
+
const allowInternal = !!ownerUser?.isAdmin;
|
|
712
|
+
if (conf.url) {
|
|
713
|
+
await assertSafeUrl(conf.url, { allowInternal });
|
|
714
|
+
}
|
|
701
715
|
if (conf.type === 'streamable-http') {
|
|
702
716
|
const options = {};
|
|
703
717
|
const headers = conf.headers ? replaceEnvVars(conf.headers, env) : {};
|
|
704
718
|
const baseFetch = createFetchWithProxy(getProxyConfigFromEnv(env));
|
|
705
|
-
const requestAwareFetch = createRequestContextAwareFetch(baseFetch, conf.passthroughHeaders);
|
|
719
|
+
const requestAwareFetch = createRedirectValidatingFetch(createRequestContextAwareFetch(baseFetch, conf.passthroughHeaders), allowInternal);
|
|
706
720
|
if (Object.keys(headers).length > 0) {
|
|
707
721
|
options.requestInit = {
|
|
708
722
|
headers,
|
|
@@ -722,7 +736,7 @@ export const createTransportFromConfig = async (name, conf) => {
|
|
|
722
736
|
const options = {};
|
|
723
737
|
const headers = conf.headers ? replaceEnvVars(conf.headers, env) : {};
|
|
724
738
|
const baseFetch = createFetchWithProxy(getProxyConfigFromEnv(env));
|
|
725
|
-
const requestAwareFetch = createRequestContextAwareFetch(baseFetch, conf.passthroughHeaders);
|
|
739
|
+
const requestAwareFetch = createRedirectValidatingFetch(createRequestContextAwareFetch(baseFetch, conf.passthroughHeaders), allowInternal);
|
|
726
740
|
if (Object.keys(headers).length > 0) {
|
|
727
741
|
options.eventSourceInit = {
|
|
728
742
|
headers,
|
|
@@ -989,6 +1003,9 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
|
|
|
989
1003
|
serverInfo.tools = mcpTools;
|
|
990
1004
|
serverInfo.openApiClient = openApiClient;
|
|
991
1005
|
console.log(`Successfully initialized OpenAPI server: ${name} with ${mcpTools.length} tools`);
|
|
1006
|
+
// Broadcast now that tools are loaded. OpenAPI servers expose tools
|
|
1007
|
+
// only (no prompts/resources). See the standard-path note above.
|
|
1008
|
+
broadcastToolListChanged();
|
|
992
1009
|
// Save tools as vector embeddings for search
|
|
993
1010
|
syncToolsAsVectorEmbeddings(name, mcpTools, {
|
|
994
1011
|
reportProgress: options?.reportEmbeddingProgress === true && serverName === name,
|
|
@@ -1077,6 +1094,11 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
|
|
|
1077
1094
|
updateServerToolsCache(serverInfo, tools.tools, {
|
|
1078
1095
|
reportEmbeddingProgress: options?.reportEmbeddingProgress === true && serverName === name,
|
|
1079
1096
|
});
|
|
1097
|
+
// Broadcast only after tools are actually loaded into the cache.
|
|
1098
|
+
// The connection completes asynchronously, so callers (e.g. enabling
|
|
1099
|
+
// a server) cannot broadcast a correct tool list themselves — doing so
|
|
1100
|
+
// would race ahead of this point and push a stale (empty) list.
|
|
1101
|
+
broadcastToolListChanged();
|
|
1080
1102
|
})
|
|
1081
1103
|
.catch((error) => {
|
|
1082
1104
|
console.error('Failed to list tools for server', {
|
|
@@ -1092,6 +1114,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
|
|
|
1092
1114
|
.then((prompts) => {
|
|
1093
1115
|
console.log(`Successfully listed ${prompts.prompts.length} prompts for server: ${name}`);
|
|
1094
1116
|
updateServerPromptsCache(serverInfo, prompts.prompts);
|
|
1117
|
+
broadcastPromptListChanged();
|
|
1095
1118
|
})
|
|
1096
1119
|
.catch((error) => {
|
|
1097
1120
|
console.error('Failed to list prompts for server', {
|
|
@@ -1107,6 +1130,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
|
|
|
1107
1130
|
.then((resources) => {
|
|
1108
1131
|
console.log(`Successfully listed ${resources.resources.length} resources for server: ${name}`);
|
|
1109
1132
|
updateServerResourcesCache(serverInfo, resources.resources);
|
|
1133
|
+
broadcastResourceListChanged();
|
|
1110
1134
|
})
|
|
1111
1135
|
.catch((error) => {
|
|
1112
1136
|
console.error('Failed to list resources for server', {
|
|
@@ -1607,6 +1631,9 @@ const normalizeToolNameForServer = (serverName, toolName) => {
|
|
|
1607
1631
|
return toolName.startsWith(prefix) ? toolName.substring(prefix.length) : toolName;
|
|
1608
1632
|
};
|
|
1609
1633
|
const getGroupLookupName = (group) => {
|
|
1634
|
+
if (group === '$smart') {
|
|
1635
|
+
return undefined;
|
|
1636
|
+
}
|
|
1610
1637
|
if (group?.startsWith('$smart/')) {
|
|
1611
1638
|
return group.substring(7) || undefined;
|
|
1612
1639
|
}
|
|
@@ -1749,7 +1776,10 @@ export const handleCallToolRequest = async (request, extra) => {
|
|
|
1749
1776
|
// Extract group and key info from request context (set by SSE/HTTP handlers)
|
|
1750
1777
|
// Fallback to extra for backward compatibility (e.g., direct API calls)
|
|
1751
1778
|
const group = requestContextService.getGroupContext() || extra?.group || getGroup(sessionId) || undefined;
|
|
1752
|
-
const username = requestContextService.getUsernameContext() ||
|
|
1779
|
+
const username = requestContextService.getUsernameContext() ||
|
|
1780
|
+
extra?.username ||
|
|
1781
|
+
(requestContextService.getKeyKindContext() === 'system' ? 'system' : undefined) ||
|
|
1782
|
+
undefined;
|
|
1753
1783
|
let appsRouteContext = { enabled: false };
|
|
1754
1784
|
const keyId = bearerKeyContext.keyId || extra?.keyId || undefined;
|
|
1755
1785
|
const keyName = bearerKeyContext.keyName || extra?.keyName || undefined;
|
|
@@ -1798,7 +1828,7 @@ export const handleCallToolRequest = async (request, extra) => {
|
|
|
1798
1828
|
else if (extra && extra.server) {
|
|
1799
1829
|
targetServerInfo = getServerByName(extra.server);
|
|
1800
1830
|
}
|
|
1801
|
-
else if (group) {
|
|
1831
|
+
else if (getGroupLookupName(group)) {
|
|
1802
1832
|
const groupTool = await resolveToolInGroup(group, toolName, appsRouteContext.enabled);
|
|
1803
1833
|
if (groupTool) {
|
|
1804
1834
|
targetServerInfo = groupTool.serverInfo;
|
|
@@ -1949,12 +1979,14 @@ export const handleCallToolRequest = async (request, extra) => {
|
|
|
1949
1979
|
});
|
|
1950
1980
|
}
|
|
1951
1981
|
// Regular tool handling
|
|
1952
|
-
const
|
|
1953
|
-
|
|
1982
|
+
const lookupGroup = getGroupLookupName(group);
|
|
1983
|
+
const groupTool = !appsRouteContext.enabled && lookupGroup
|
|
1984
|
+
? await resolveToolInGroup(lookupGroup, request.params.name, appsRouteContext.enabled)
|
|
1954
1985
|
: undefined;
|
|
1955
1986
|
const serverInfo = appsRouteContext.enabled
|
|
1956
1987
|
? appsRouteContext.serverInfo
|
|
1957
|
-
: (groupTool?.serverInfo ??
|
|
1988
|
+
: (groupTool?.serverInfo ??
|
|
1989
|
+
(lookupGroup ? undefined : getServerByTool(request.params.name)));
|
|
1958
1990
|
const routeToolName = groupTool?.toolName ?? request.params.name;
|
|
1959
1991
|
const tool = groupTool?.tool ??
|
|
1960
1992
|
(serverInfo
|
|
@@ -2144,11 +2176,12 @@ export const handleGetPromptRequest = async (request, extra) => {
|
|
|
2144
2176
|
}
|
|
2145
2177
|
let server;
|
|
2146
2178
|
let promptNameForServer = name;
|
|
2179
|
+
const lookupGroup = getGroupLookupName(group);
|
|
2147
2180
|
if (extra && extra.server) {
|
|
2148
2181
|
server = getServerByName(extra.server);
|
|
2149
2182
|
}
|
|
2150
|
-
else if (
|
|
2151
|
-
const groupPrompt = await resolvePromptInGroup(
|
|
2183
|
+
else if (lookupGroup) {
|
|
2184
|
+
const groupPrompt = await resolvePromptInGroup(lookupGroup, name);
|
|
2152
2185
|
if (groupPrompt) {
|
|
2153
2186
|
server = groupPrompt.serverInfo;
|
|
2154
2187
|
promptNameForServer = groupPrompt.promptName;
|
|
@@ -2202,6 +2235,7 @@ export const handleGetPromptRequest = async (request, extra) => {
|
|
|
2202
2235
|
export const handleListPromptsRequest = async (_, extra) => {
|
|
2203
2236
|
const sessionId = extra.sessionId || '';
|
|
2204
2237
|
const group = getGroup(sessionId);
|
|
2238
|
+
const lookupGroup = getGroupLookupName(group);
|
|
2205
2239
|
console.log(`Handling ListPromptsRequest for group: ${group}`);
|
|
2206
2240
|
// Start with built-in prompts (only enabled ones)
|
|
2207
2241
|
const builtinPrompts = await getBuiltinPromptDao().findEnabled();
|
|
@@ -2211,7 +2245,7 @@ export const handleListPromptsRequest = async (_, extra) => {
|
|
|
2211
2245
|
description: bp.description,
|
|
2212
2246
|
arguments: bp.arguments,
|
|
2213
2247
|
}));
|
|
2214
|
-
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(
|
|
2248
|
+
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup);
|
|
2215
2249
|
for (const serverInfo of filteredServerInfos) {
|
|
2216
2250
|
if (serverInfo.prompts && serverInfo.prompts.length > 0) {
|
|
2217
2251
|
const groupServerConfig = serverConfigsByName.get(serverInfo.name);
|
|
@@ -2225,7 +2259,7 @@ export const handleListPromptsRequest = async (_, extra) => {
|
|
|
2225
2259
|
return promptConfig?.enabled !== false;
|
|
2226
2260
|
});
|
|
2227
2261
|
}
|
|
2228
|
-
enabledPrompts = await filterPromptsByGroup(
|
|
2262
|
+
enabledPrompts = await filterPromptsByGroup(lookupGroup, serverInfo.name, enabledPrompts, groupServerConfig);
|
|
2229
2263
|
// Apply custom descriptions from server configuration
|
|
2230
2264
|
const promptsWithCustomDescriptions = enabledPrompts.map((prompt) => {
|
|
2231
2265
|
const promptConfig = serverConfig?.prompts?.[prompt.name];
|
|
@@ -2245,6 +2279,7 @@ export const handleListPromptsRequest = async (_, extra) => {
|
|
|
2245
2279
|
export const handleListResourcesRequest = async (_, extra) => {
|
|
2246
2280
|
const sessionId = extra.sessionId || '';
|
|
2247
2281
|
const group = getGroup(sessionId);
|
|
2282
|
+
const lookupGroup = getGroupLookupName(group);
|
|
2248
2283
|
console.log(`Handling ListResourcesRequest for group: ${group}`);
|
|
2249
2284
|
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
2250
2285
|
// Start with built-in resources (only enabled ones)
|
|
@@ -2255,7 +2290,7 @@ export const handleListResourcesRequest = async (_, extra) => {
|
|
|
2255
2290
|
description: br.description,
|
|
2256
2291
|
mimeType: br.mimeType,
|
|
2257
2292
|
}));
|
|
2258
|
-
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(
|
|
2293
|
+
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup);
|
|
2259
2294
|
for (const serverInfo of filteredServerInfos) {
|
|
2260
2295
|
if (serverInfo.resources && serverInfo.resources.length > 0) {
|
|
2261
2296
|
// Filter resources based on server configuration
|
|
@@ -2267,7 +2302,7 @@ export const handleListResourcesRequest = async (_, extra) => {
|
|
|
2267
2302
|
return resourceConfig?.enabled !== false;
|
|
2268
2303
|
});
|
|
2269
2304
|
}
|
|
2270
|
-
enabledResources = await filterResourcesByGroup(
|
|
2305
|
+
enabledResources = await filterResourcesByGroup(lookupGroup, serverInfo.name, enabledResources, serverConfigsByName.get(serverInfo.name));
|
|
2271
2306
|
// Apply custom descriptions from server configuration
|
|
2272
2307
|
const resourcesWithCustomDescriptions = enabledResources.map((resource) => {
|
|
2273
2308
|
const resourceConfig = serverConfig?.resources?.[resource.uri];
|
|
@@ -2289,9 +2324,10 @@ export const handleListResourcesRequest = async (_, extra) => {
|
|
|
2289
2324
|
export const handleListResourceTemplatesRequest = async (_, extra) => {
|
|
2290
2325
|
const sessionId = extra.sessionId || '';
|
|
2291
2326
|
const group = getGroup(sessionId);
|
|
2327
|
+
const lookupGroup = getGroupLookupName(group);
|
|
2292
2328
|
console.log(`Handling ListResourceTemplatesRequest for group: ${group}`);
|
|
2293
2329
|
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
2294
|
-
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(
|
|
2330
|
+
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup, {
|
|
2295
2331
|
requireClient: true,
|
|
2296
2332
|
});
|
|
2297
2333
|
const results = await Promise.allSettled(filteredServerInfos.map(async (serverInfo) => {
|
|
@@ -2299,7 +2335,7 @@ export const handleListResourceTemplatesRequest = async (_, extra) => {
|
|
|
2299
2335
|
return [];
|
|
2300
2336
|
}
|
|
2301
2337
|
const templates = await serverInfo.client.listResourceTemplates({}, serverInfo.options || {});
|
|
2302
|
-
const filteredTemplates = await filterResourceTemplatesByGroup(
|
|
2338
|
+
const filteredTemplates = await filterResourceTemplatesByGroup(lookupGroup, serverInfo.name, templates.resourceTemplates || [], serverConfigsByName.get(serverInfo.name));
|
|
2303
2339
|
return appsRouteContext.enabled
|
|
2304
2340
|
? filteredTemplates
|
|
2305
2341
|
: filteredTemplates.map((template) => stripMcpAppsMetadata(template));
|
|
@@ -2313,6 +2349,7 @@ export const handleReadResourceRequest = async (request, extra) => {
|
|
|
2313
2349
|
const { uri } = request.params;
|
|
2314
2350
|
const sessionId = extra.sessionId || '';
|
|
2315
2351
|
const group = getGroup(sessionId);
|
|
2352
|
+
const lookupGroup = getGroupLookupName(group);
|
|
2316
2353
|
const appsRouteContext = await getMcpAppsRouteContext(sessionId, group);
|
|
2317
2354
|
// Check built-in resources first
|
|
2318
2355
|
const builtinResource = await getBuiltinResourceDao().findByUri(uri);
|
|
@@ -2327,7 +2364,7 @@ export const handleReadResourceRequest = async (request, extra) => {
|
|
|
2327
2364
|
],
|
|
2328
2365
|
};
|
|
2329
2366
|
}
|
|
2330
|
-
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(
|
|
2367
|
+
const { filteredServerInfos, serverConfigsByName } = await getFilteredServerInfosForGroup(lookupGroup);
|
|
2331
2368
|
let server;
|
|
2332
2369
|
for (const serverInfo of filteredServerInfos) {
|
|
2333
2370
|
if (serverInfo.status !== 'connected') {
|
|
@@ -2338,7 +2375,7 @@ export const handleReadResourceRequest = async (request, extra) => {
|
|
|
2338
2375
|
if (serverConfig?.resources) {
|
|
2339
2376
|
enabledResources = enabledResources.filter((resource) => serverConfig.resources?.[resource.uri]?.enabled !== false);
|
|
2340
2377
|
}
|
|
2341
|
-
enabledResources = await filterResourcesByGroup(
|
|
2378
|
+
enabledResources = await filterResourcesByGroup(lookupGroup, serverInfo.name, enabledResources, serverConfigsByName.get(serverInfo.name));
|
|
2342
2379
|
if (enabledResources.some((resource) => resource.uri === uri)) {
|
|
2343
2380
|
server = serverInfo;
|
|
2344
2381
|
break;
|