@loop_ouroboros/mcp-hub-lite 1.3.2 → 1.3.3
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/CHANGELOG.md +14 -0
- package/dist/server/src/services/hub-tools/index.d.ts +1 -0
- package/dist/server/src/services/hub-tools/index.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/index.js +1 -0
- package/dist/server/src/services/hub-tools/resource-generator.d.ts +1 -1
- package/dist/server/src/services/hub-tools/resource-generator.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/resource-generator.js +11 -29
- package/dist/server/src/services/hub-tools/server-metadata-cache.d.ts +19 -0
- package/dist/server/src/services/hub-tools/server-metadata-cache.d.ts.map +1 -0
- package/dist/server/src/services/hub-tools/server-metadata-cache.js +117 -0
- package/dist/server/src/services/hub-tools/server-selector.d.ts +1 -1
- package/dist/server/src/services/hub-tools/server-selector.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/server-selector.js +3 -8
- package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/system-tool-definitions.js +1 -1
- package/dist/server/src/services/hub-tools.service.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools.service.js +42 -74
- package/dist/server/tests/unit/services/hub-tools.service.test.js +63 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.3] - 2026-06-10
|
|
6
|
+
|
|
7
|
+
### Hub
|
|
8
|
+
|
|
9
|
+
- fix tag-match-unique error when reading server-level resources
|
|
10
|
+
- return per-instance tags array instead of merged object
|
|
11
|
+
- remove dead strictMode parameter from server-selector
|
|
12
|
+
- remove L1 fallback in call-tool, route directly to RANDOM
|
|
13
|
+
- remove gateway mode in call-tool, simplify tool validation
|
|
14
|
+
|
|
15
|
+
### Docs
|
|
16
|
+
|
|
17
|
+
- update hub-tools CLAUDE.md with current architecture
|
|
18
|
+
|
|
5
19
|
## [1.3.2] - 2026-06-09
|
|
6
20
|
|
|
7
21
|
### Search
|
|
@@ -5,4 +5,5 @@ export { getSystemTools } from './system-tool-definitions.js';
|
|
|
5
5
|
export type { ToolAnnotations, SystemToolDefinition } from './system-tool-definitions.js';
|
|
6
6
|
export { generateDynamicResources, readResource } from './resource-generator.js';
|
|
7
7
|
export type { ServerMetadata } from './resource-generator.js';
|
|
8
|
+
export { serverMetadataCache } from './server-metadata-cache.js';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG3E,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACjF,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG3E,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACjF,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -3,3 +3,4 @@ export { HubToolsService, hubToolsService } from '../hub-tools.service.js';
|
|
|
3
3
|
export { hasValidId, selectBestInstance, getServerDescription } from './server-selector.js';
|
|
4
4
|
export { getSystemTools } from './system-tool-definitions.js';
|
|
5
5
|
export { generateDynamicResources, readResource } from './resource-generator.js';
|
|
6
|
+
export { serverMetadataCache } from './server-metadata-cache.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-generator.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/resource-generator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"resource-generator.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/resource-generator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAoLlE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,IAAI,QAAQ,EAAE,CAqErD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,QAAQ,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,CAkFzD"}
|
|
@@ -3,7 +3,8 @@ import { dirname, join } from 'path';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import { hubManager } from '../hub-manager.service.js';
|
|
5
5
|
import { mcpConnectionManager } from '../connection/index.js';
|
|
6
|
-
import { hasValidId,
|
|
6
|
+
import { hasValidId, getServerDescription } from './server-selector.js';
|
|
7
|
+
import { serverMetadataCache } from './server-metadata-cache.js';
|
|
7
8
|
/**
|
|
8
9
|
* Maps Hub URI to original MCP URI for resource forwarding.
|
|
9
10
|
* Key: Hub URI (e.g., "hub://servers/exa-ai/0/tools/list")
|
|
@@ -280,43 +281,24 @@ export async function readResource(uri, language) {
|
|
|
280
281
|
if (instanceIndex === undefined) {
|
|
281
282
|
// Handle list requests first
|
|
282
283
|
if (listType) {
|
|
283
|
-
//
|
|
284
|
-
const
|
|
285
|
-
if (!
|
|
284
|
+
// Read from aggregated cache for server-level list queries
|
|
285
|
+
const metadata = serverMetadataCache.get(serverName);
|
|
286
|
+
if (!metadata) {
|
|
286
287
|
throw new Error(`Server not found or not connected: ${serverName}`);
|
|
287
288
|
}
|
|
288
|
-
const instanceIndex = serverInfo.instance.index;
|
|
289
289
|
if (listType === 'tools') {
|
|
290
|
-
return mcpConnectionManager.
|
|
290
|
+
return mcpConnectionManager.getToolsByServerName(serverName);
|
|
291
291
|
}
|
|
292
292
|
else {
|
|
293
|
-
return mcpConnectionManager.
|
|
293
|
+
return mcpConnectionManager.getResourcesByName(serverName);
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
|
-
// Server metadata request
|
|
297
|
-
const
|
|
298
|
-
if (!
|
|
296
|
+
// Server metadata request — read from aggregated cache (no instance selection needed)
|
|
297
|
+
const metadata = serverMetadataCache.get(serverName);
|
|
298
|
+
if (!metadata) {
|
|
299
299
|
throw new Error(`Server not found or not connected: ${serverName}`);
|
|
300
300
|
}
|
|
301
|
-
|
|
302
|
-
const tools = mcpConnectionManager.getTools(serverName, instanceIndex);
|
|
303
|
-
const resources = mcpConnectionManager.getResources(serverName, instanceIndex);
|
|
304
|
-
// Build tool name to description map
|
|
305
|
-
const toolsMap = {};
|
|
306
|
-
for (const tool of tools) {
|
|
307
|
-
toolsMap[tool.name] = tool.description || '';
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
name: serverName,
|
|
311
|
-
status: serverInfo.instance.status,
|
|
312
|
-
toolsCount: tools.length,
|
|
313
|
-
tools: toolsMap,
|
|
314
|
-
resourcesCount: resources.length,
|
|
315
|
-
tags: serverInfo.instance.tags || {},
|
|
316
|
-
lastHeartbeat: serverInfo.instance.lastHeartbeat,
|
|
317
|
-
uptime: serverInfo.instance.uptime,
|
|
318
|
-
description: getServerDescription(serverConfig, serverName)
|
|
319
|
-
};
|
|
301
|
+
return metadata;
|
|
320
302
|
}
|
|
321
303
|
// MCP native resource forwarding: hub://servers/{name}/{instanceIndex}/{mcpPath}
|
|
322
304
|
// Find the specific instance by index
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ServerMetadata } from './resource-generator.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cached server-level metadata aggregated across all connected instances.
|
|
4
|
+
* Updated reactively via EventBus subscriptions — no I/O, all synchronous cache lookups.
|
|
5
|
+
*/
|
|
6
|
+
declare class ServerMetadataCache {
|
|
7
|
+
private cache;
|
|
8
|
+
private unsubscribers;
|
|
9
|
+
private initialized;
|
|
10
|
+
initialize(): void;
|
|
11
|
+
destroy(): void;
|
|
12
|
+
get(serverName: string): ServerMetadata | undefined;
|
|
13
|
+
refresh(serverName: string): void;
|
|
14
|
+
refreshAll(): void;
|
|
15
|
+
private buildMetadata;
|
|
16
|
+
}
|
|
17
|
+
export declare const serverMetadataCache: ServerMetadataCache;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=server-metadata-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-metadata-cache.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/server-metadata-cache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAO9D;;;GAGG;AACH,cAAM,mBAAmB;IACvB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAS;IAE5B,UAAU,IAAI,IAAI;IA4BlB,OAAO,IAAI,IAAI;IASf,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAYnD,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IASjC,UAAU,IAAI,IAAI;IASlB,OAAO,CAAC,aAAa;CAkDtB;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { hubManager } from '../hub-manager.service.js';
|
|
2
|
+
import { mcpConnectionManager } from '../connection/index.js';
|
|
3
|
+
import { eventBus, EventTypes } from '../event-bus.service.js';
|
|
4
|
+
import { getServerDescription } from './server-selector.js';
|
|
5
|
+
/**
|
|
6
|
+
* Cached server-level metadata aggregated across all connected instances.
|
|
7
|
+
* Updated reactively via EventBus subscriptions — no I/O, all synchronous cache lookups.
|
|
8
|
+
*/
|
|
9
|
+
class ServerMetadataCache {
|
|
10
|
+
cache = new Map();
|
|
11
|
+
unsubscribers = [];
|
|
12
|
+
initialized = false;
|
|
13
|
+
initialize() {
|
|
14
|
+
if (this.initialized)
|
|
15
|
+
return;
|
|
16
|
+
this.initialized = true;
|
|
17
|
+
this.unsubscribers.push(eventBus.subscribe(EventTypes.SERVER_CONNECTED, (data) => {
|
|
18
|
+
this.refresh(data.serverName);
|
|
19
|
+
}));
|
|
20
|
+
this.unsubscribers.push(eventBus.subscribe(EventTypes.SERVER_DISCONNECTED, (data) => {
|
|
21
|
+
this.refresh(data.serverName);
|
|
22
|
+
}));
|
|
23
|
+
this.unsubscribers.push(eventBus.subscribe(EventTypes.TOOLS_UPDATED, (data) => {
|
|
24
|
+
this.refresh(data.serverName);
|
|
25
|
+
}));
|
|
26
|
+
this.unsubscribers.push(eventBus.subscribe(EventTypes.RESOURCES_UPDATED, (data) => {
|
|
27
|
+
this.refresh(data.serverName);
|
|
28
|
+
}));
|
|
29
|
+
// Cache populates lazily on first get() — avoids init-order issues in tests
|
|
30
|
+
}
|
|
31
|
+
destroy() {
|
|
32
|
+
for (const unsub of this.unsubscribers) {
|
|
33
|
+
unsub();
|
|
34
|
+
}
|
|
35
|
+
this.unsubscribers = [];
|
|
36
|
+
this.cache.clear();
|
|
37
|
+
this.initialized = false;
|
|
38
|
+
}
|
|
39
|
+
get(serverName) {
|
|
40
|
+
// Lazy build on first access — no pre-population needed
|
|
41
|
+
if (!this.cache.has(serverName)) {
|
|
42
|
+
const metadata = this.buildMetadata(serverName);
|
|
43
|
+
if (metadata) {
|
|
44
|
+
this.cache.set(serverName, metadata);
|
|
45
|
+
}
|
|
46
|
+
return metadata;
|
|
47
|
+
}
|
|
48
|
+
return this.cache.get(serverName);
|
|
49
|
+
}
|
|
50
|
+
refresh(serverName) {
|
|
51
|
+
const metadata = this.buildMetadata(serverName);
|
|
52
|
+
if (metadata) {
|
|
53
|
+
this.cache.set(serverName, metadata);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.cache.delete(serverName);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
refreshAll() {
|
|
60
|
+
const servers = hubManager.getAllServers();
|
|
61
|
+
for (const server of servers) {
|
|
62
|
+
if (server.name) {
|
|
63
|
+
this.refresh(server.name);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
buildMetadata(serverName) {
|
|
68
|
+
const serverConfig = hubManager.getServerByName(serverName);
|
|
69
|
+
if (!serverConfig)
|
|
70
|
+
return undefined;
|
|
71
|
+
const connectedIndexes = mcpConnectionManager.getConnectedIndexes(serverName);
|
|
72
|
+
if (connectedIndexes.length === 0)
|
|
73
|
+
return undefined;
|
|
74
|
+
// Aggregate tools across all instances (ToolCache already deduplicates by name)
|
|
75
|
+
const tools = mcpConnectionManager.getToolsByServerName(serverName) || [];
|
|
76
|
+
const toolsMap = {};
|
|
77
|
+
for (const tool of tools) {
|
|
78
|
+
toolsMap[tool.name] = tool.description || '';
|
|
79
|
+
}
|
|
80
|
+
// Aggregate resources across all instances
|
|
81
|
+
const resources = mcpConnectionManager.getResourcesByName(serverName) || [];
|
|
82
|
+
// Collect tags from all connected instances
|
|
83
|
+
const tags = [];
|
|
84
|
+
const instances = hubManager.getServerInstancesByName(serverName);
|
|
85
|
+
for (const instance of instances) {
|
|
86
|
+
if (instance.index !== undefined && connectedIndexes.includes(instance.index)) {
|
|
87
|
+
tags.push(instance.tags || {});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Aggregate heartbeat (max) and uptime (min startTime) across instances
|
|
91
|
+
let lastHeartbeat = 0;
|
|
92
|
+
let uptime = Infinity;
|
|
93
|
+
for (const idx of connectedIndexes) {
|
|
94
|
+
const status = mcpConnectionManager.getStatus(serverName, idx);
|
|
95
|
+
if (status) {
|
|
96
|
+
if (status.lastCheck > lastHeartbeat)
|
|
97
|
+
lastHeartbeat = status.lastCheck;
|
|
98
|
+
if (status.startTime && status.startTime < uptime)
|
|
99
|
+
uptime = status.startTime;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (uptime === Infinity)
|
|
103
|
+
uptime = 0;
|
|
104
|
+
return {
|
|
105
|
+
name: serverName,
|
|
106
|
+
status: 'online',
|
|
107
|
+
toolsCount: tools.length,
|
|
108
|
+
tools: toolsMap,
|
|
109
|
+
resourcesCount: resources.length,
|
|
110
|
+
tags,
|
|
111
|
+
lastHeartbeat,
|
|
112
|
+
uptime,
|
|
113
|
+
description: getServerDescription(serverConfig, serverName)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export const serverMetadataCache = new ServerMetadataCache();
|
|
@@ -72,5 +72,5 @@ export declare function hasValidId(server: unknown): server is ValidServer;
|
|
|
72
72
|
* const serverInfoNonStrict = selectBestInstance('my-mcp-server', undefined, false);
|
|
73
73
|
* ```
|
|
74
74
|
*/
|
|
75
|
-
export declare function selectBestInstance(serverName: string, requestOptions?: RequestOptions
|
|
75
|
+
export declare function selectBestInstance(serverName: string, requestOptions?: RequestOptions): ServerInstanceInfo | undefined;
|
|
76
76
|
//# sourceMappingURL=server-selector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-selector.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/server-selector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,EACjE,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,WAAW,CAMjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"server-selector.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/server-selector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,EACjE,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,WAAW,CAMjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,cAAc,GAC9B,kBAAkB,GAAG,SAAS,CAyChC"}
|
|
@@ -80,19 +80,16 @@ export function hasValidId(server) {
|
|
|
80
80
|
* const serverInfoNonStrict = selectBestInstance('my-mcp-server', undefined, false);
|
|
81
81
|
* ```
|
|
82
82
|
*/
|
|
83
|
-
export function selectBestInstance(serverName, requestOptions
|
|
84
|
-
// Get all instances of the server
|
|
83
|
+
export function selectBestInstance(serverName, requestOptions) {
|
|
85
84
|
const instances = hubManager.getServerInstancesByName(serverName);
|
|
86
85
|
if (instances.length === 0) {
|
|
87
86
|
return undefined;
|
|
88
87
|
}
|
|
89
|
-
// Get server configuration
|
|
90
88
|
const serverConfig = hubManager.getServerByName(serverName);
|
|
91
89
|
if (!serverConfig) {
|
|
92
90
|
return undefined;
|
|
93
91
|
}
|
|
94
92
|
try {
|
|
95
|
-
// Use the new instance selector
|
|
96
93
|
const selectedInstance = InstanceSelector.selectInstance(serverName, serverConfig, requestOptions);
|
|
97
94
|
if (!selectedInstance) {
|
|
98
95
|
return undefined;
|
|
@@ -104,12 +101,10 @@ export function selectBestInstance(serverName, requestOptions, strictMode = true
|
|
|
104
101
|
};
|
|
105
102
|
}
|
|
106
103
|
catch (error) {
|
|
107
|
-
//
|
|
108
|
-
if (
|
|
109
|
-
// Re-throw with server context added to message
|
|
104
|
+
// Re-throw tag matching errors with server context
|
|
105
|
+
if (error instanceof TagMatchUniqueError) {
|
|
110
106
|
throw new Error(`[${serverName}] ${error.message}`);
|
|
111
107
|
}
|
|
112
|
-
// Handle other errors or non-strict mode gracefully
|
|
113
108
|
logger.error(`Instance selection failed for server ${serverName}:`, error, LOG_MODULES.SERVER_SELECTOR);
|
|
114
109
|
return undefined;
|
|
115
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-tool-definitions.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/system-tool-definitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAY/D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,IAAI,oBAAoB,EAAE,
|
|
1
|
+
{"version":3,"file":"system-tool-definitions.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/system-tool-definitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAY/D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,IAAI,oBAAoB,EAAE,CAgMvD"}
|
|
@@ -111,7 +111,7 @@ export function getSystemTools() {
|
|
|
111
111
|
properties: {
|
|
112
112
|
serverName: {
|
|
113
113
|
type: 'string',
|
|
114
|
-
description: 'Name of the MCP server (
|
|
114
|
+
description: 'Name of the target MCP server. Use the exact server name from list_servers results. If you already know the server name (e.g., from previous list_tools or search_tools calls), call directly without re-searching.'
|
|
115
115
|
},
|
|
116
116
|
toolName: { type: 'string', description: 'Name of the tool to call' },
|
|
117
117
|
toolArgs: { type: 'object', description: 'Arguments to pass to the tool' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-tools.service.d.ts","sourceRoot":"","sources":["../../../../src/services/hub-tools.service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAQjE,OAAO,EAEL,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,8BAA8B,EAC9B,cAAc,EACd,iBAAiB,EAElB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,6BAA6B,EAC7B,cAAc,EACd,iBAAiB,EAClB,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"hub-tools.service.d.ts","sourceRoot":"","sources":["../../../../src/services/hub-tools.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import { countMatchingTokens } from '../utils/search-matcher.js';
|
|
|
10
10
|
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
11
11
|
import { MCP_HUB_LITE_SERVER, LIST_SERVERS_TOOL, LIST_TOOLS_TOOL, GET_TOOL_TOOL, CALL_TOOL_TOOL, UPDATE_SERVER_DESCRIPTION_TOOL, LIST_TAGS_TOOL, SEARCH_TOOLS_TOOL, SYSTEM_TOOL_NAMES } from '../models/system-tools.constants.js';
|
|
12
12
|
import { ToolArgsParser } from '../utils/tool-args-parser.js';
|
|
13
|
-
import { hasValidId, selectBestInstance, getServerDescription, getSystemTools, generateDynamicResources, readResource as readResourceUtil } from './hub-tools/index.js';
|
|
13
|
+
import { hasValidId, selectBestInstance, getServerDescription, getSystemTools, generateDynamicResources, readResource as readResourceUtil, serverMetadataCache } from './hub-tools/index.js';
|
|
14
14
|
import { InstanceSelector } from './hub-tools/instance-selector.js';
|
|
15
15
|
import { InstanceSelectionStrategy } from '../models/server.model.js';
|
|
16
16
|
/**
|
|
@@ -64,7 +64,7 @@ import { InstanceSelectionStrategy } from '../models/server.model.js';
|
|
|
64
64
|
export class HubToolsService {
|
|
65
65
|
// Cache removed - listResources() now calls generateDynamicResources() directly
|
|
66
66
|
constructor() {
|
|
67
|
-
|
|
67
|
+
serverMetadataCache.initialize();
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Retrieves the complete list of system tools provided by this service.
|
|
@@ -372,86 +372,54 @@ export class HubToolsService {
|
|
|
372
372
|
throw new McpError(-32801, `System tools cannot be called via 'call_tool'. Use 'tools/call' with the system tool name directly. ` +
|
|
373
373
|
`Example: use 'list_servers' directly instead of call_tool(serverName: "mcp-hub-lite", toolName: "list_servers").`);
|
|
374
374
|
}
|
|
375
|
-
// Not a system tool
|
|
376
|
-
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
const serverInfo = selectBestInstance(server.name, requestOptions, true);
|
|
385
|
-
if (serverInfo && serverInfo.instance.id) {
|
|
386
|
-
const instanceIndex = serverInfo.instance.index;
|
|
387
|
-
const tools = mcpConnectionManager.getTools(server.name, instanceIndex);
|
|
388
|
-
if (tools.some((tool) => normalizeToolName(tool.name) === normalizeToolName(toolName))) {
|
|
389
|
-
matchingServers.push(server.name);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
if (matchingServers.length === 0) {
|
|
394
|
-
logger.error(`Tool '${toolName}' not found in any connected server`, LOG_MODULES.HUB_TOOLS);
|
|
395
|
-
throw new Error(`Tool '${toolName}' not found`);
|
|
396
|
-
}
|
|
397
|
-
if (matchingServers.length > 1) {
|
|
398
|
-
logger.warn(`Tool '${toolName}' found in multiple servers: ${matchingServers.join(', ')}. Using first match.`, LOG_MODULES.HUB_TOOLS);
|
|
399
|
-
}
|
|
400
|
-
// Use the first matching server
|
|
401
|
-
serverName = matchingServers[0];
|
|
375
|
+
// Not a system tool — reject with actionable guidance
|
|
376
|
+
// The aggregated gateway tools (wrapped by tool-list-generator) already hardcode
|
|
377
|
+
// the correct serverName, so this path should only be hit when LLM manually fills
|
|
378
|
+
// "mcp-hub-lite" incorrectly. Guide them to use search_tools instead.
|
|
379
|
+
throw new McpError(-32602, `Cannot call external tool '${toolName}' with serverName "mcp-hub-lite". ` +
|
|
380
|
+
`Use 'search_tools' to find which server provides this tool, ` +
|
|
381
|
+
`then call it with the correct serverName.`);
|
|
402
382
|
}
|
|
403
383
|
logger.debug(`Tool call received: serverName=${serverName}, toolName=${toolName}, args=${stringifyForLogging(toolArgs)}`, LOG_MODULES.HUB_TOOLS);
|
|
404
|
-
// Validate tool exists
|
|
405
|
-
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const tools = mcpConnectionManager.getTools(serverName, instanceIndex);
|
|
411
|
-
const matchedTool = tools.find((tool) => normalizeToolName(tool.name) === normalizeToolName(toolName));
|
|
412
|
-
if (!matchedTool) {
|
|
413
|
-
throw new Error(`Tool '${toolName}' not found in server '${serverName}'. ` +
|
|
414
|
-
`Use list_tools(serverName: "${serverName}") to see available tools.`);
|
|
415
|
-
}
|
|
416
|
-
actualToolName = matchedTool.name;
|
|
384
|
+
// Validate tool exists using server-name-level aggregation (no instance selection needed)
|
|
385
|
+
const aggregatedTools = mcpConnectionManager.getToolsByServerName(serverName);
|
|
386
|
+
const matchedTool = aggregatedTools.find((tool) => normalizeToolName(tool.name) === normalizeToolName(toolName));
|
|
387
|
+
if (!matchedTool) {
|
|
388
|
+
throw new Error(`Tool '${toolName}' not found in server '${serverName}'. ` +
|
|
389
|
+
`Use list_tools(serverName: "${serverName}") to see available tools.`);
|
|
417
390
|
}
|
|
418
|
-
|
|
391
|
+
let actualToolName = matchedTool.name;
|
|
392
|
+
const serverInfo = selectBestInstance(serverName, requestOptions);
|
|
419
393
|
const requestId = `tool-call-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
420
394
|
if (!serverInfo) {
|
|
421
395
|
// Server not found in hubManager, try direct call by name through mcpConnectionManager
|
|
422
396
|
logger.debug(`Server not found in hubManager, trying direct call by name: ${serverName}`, LOG_MODULES.HUB_TOOLS);
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const selectedInstance = InstanceSelector.selectInstance(serverName, {
|
|
442
|
-
...serverConfig,
|
|
443
|
-
template: {
|
|
444
|
-
...serverConfig.template,
|
|
445
|
-
instanceSelectionStrategy: InstanceSelectionStrategy.RANDOM
|
|
446
|
-
}
|
|
447
|
-
}, undefined);
|
|
448
|
-
if (selectedInstance) {
|
|
449
|
-
fallbackServerInfo = {
|
|
450
|
-
name: serverName,
|
|
451
|
-
config: serverConfig,
|
|
452
|
-
instance: selectedInstance
|
|
453
|
-
};
|
|
397
|
+
// Fallback: force RANDOM strategy on any connected instance
|
|
398
|
+
// (selectBestInstance may fail for TAG_MATCH_UNIQUE without tags)
|
|
399
|
+
let fallbackServerInfo;
|
|
400
|
+
const serverConfig = hubManager.getServerByName(serverName);
|
|
401
|
+
if (serverConfig && serverConfig.instances.length > 0) {
|
|
402
|
+
// Filter: use runtime connected status, NOT config enabled flag
|
|
403
|
+
const connectedInstances = serverConfig.instances.filter((instance) => {
|
|
404
|
+
if (instance.index === undefined)
|
|
405
|
+
return false;
|
|
406
|
+
const status = mcpConnectionManager.getStatus(serverName, instance.index);
|
|
407
|
+
return status?.connected;
|
|
408
|
+
});
|
|
409
|
+
if (connectedInstances.length > 0) {
|
|
410
|
+
const selectedInstance = InstanceSelector.selectInstance(serverName, {
|
|
411
|
+
...serverConfig,
|
|
412
|
+
template: {
|
|
413
|
+
...serverConfig.template,
|
|
414
|
+
instanceSelectionStrategy: InstanceSelectionStrategy.RANDOM
|
|
454
415
|
}
|
|
416
|
+
}, undefined);
|
|
417
|
+
if (selectedInstance) {
|
|
418
|
+
fallbackServerInfo = {
|
|
419
|
+
name: serverName,
|
|
420
|
+
config: serverConfig,
|
|
421
|
+
instance: selectedInstance
|
|
422
|
+
};
|
|
455
423
|
}
|
|
456
424
|
}
|
|
457
425
|
}
|
|
@@ -1084,15 +1084,17 @@ describe('HubToolsService', () => {
|
|
|
1084
1084
|
});
|
|
1085
1085
|
it('should throw error for non-existent server', async () => {
|
|
1086
1086
|
// Arrange
|
|
1087
|
+
vi.mocked(hubManager.getServerByName).mockReturnValue(undefined);
|
|
1087
1088
|
vi.mocked(hubManager.getServerInstancesByName).mockReturnValue([]);
|
|
1088
1089
|
// Act & Assert
|
|
1089
|
-
await expect(hubToolsService.readResource('hub://servers/NonExistent')).rejects.toThrow('Server not found
|
|
1090
|
+
await expect(hubToolsService.readResource('hub://servers/NonExistent')).rejects.toThrow('Server not found');
|
|
1090
1091
|
});
|
|
1091
1092
|
it('should return server metadata for server URI with tools field', async () => {
|
|
1092
1093
|
// Arrange
|
|
1093
1094
|
const serverName = 'Test Server';
|
|
1094
1095
|
const mockInstance = {
|
|
1095
1096
|
id: 'test-instance',
|
|
1097
|
+
index: 0,
|
|
1096
1098
|
enabled: true,
|
|
1097
1099
|
args: [],
|
|
1098
1100
|
env: {},
|
|
@@ -1134,13 +1136,20 @@ describe('HubToolsService', () => {
|
|
|
1134
1136
|
const mockTools = [
|
|
1135
1137
|
{ name: 'testTool', description: 'Test tool description', serverName: 'test-server' }
|
|
1136
1138
|
];
|
|
1139
|
+
const mockResources = [{ uri: 'test://resource', name: 'Test Resource' }];
|
|
1137
1140
|
// @ts-expect-error - Mocking for test purposes with extra fields
|
|
1138
1141
|
vi.mocked(hubManager.getServerInstancesByName).mockReturnValue([mockInstance]);
|
|
1139
1142
|
vi.mocked(hubManager.getServerByName).mockReturnValue(mockConfig);
|
|
1140
|
-
vi.mocked(mcpConnectionManager.
|
|
1141
|
-
vi.mocked(mcpConnectionManager.
|
|
1142
|
-
|
|
1143
|
-
|
|
1143
|
+
vi.mocked(mcpConnectionManager.getConnectedIndexes).mockReturnValue([0]);
|
|
1144
|
+
vi.mocked(mcpConnectionManager.getToolsByServerName).mockReturnValue(mockTools);
|
|
1145
|
+
vi.mocked(mcpConnectionManager.getResourcesByName).mockReturnValue(mockResources);
|
|
1146
|
+
vi.mocked(mcpConnectionManager.getStatus).mockReturnValue({
|
|
1147
|
+
connected: true,
|
|
1148
|
+
lastCheck: mockInstance.lastHeartbeat,
|
|
1149
|
+
startTime: mockInstance.uptime,
|
|
1150
|
+
toolsCount: 1,
|
|
1151
|
+
resourcesCount: 1
|
|
1152
|
+
});
|
|
1144
1153
|
// Act
|
|
1145
1154
|
const result = await hubToolsService.readResource(`hub://servers/${serverName}`);
|
|
1146
1155
|
// Assert
|
|
@@ -1150,7 +1159,7 @@ describe('HubToolsService', () => {
|
|
|
1150
1159
|
toolsCount: 1,
|
|
1151
1160
|
tools: { testTool: 'Test tool description' },
|
|
1152
1161
|
resourcesCount: 1,
|
|
1153
|
-
tags: { env: 'test' },
|
|
1162
|
+
tags: [{ env: 'test' }],
|
|
1154
1163
|
// @ts-expect-error - Accessing extra fields on mock
|
|
1155
1164
|
lastHeartbeat: mockInstance.lastHeartbeat,
|
|
1156
1165
|
// @ts-expect-error - Accessing extra fields on mock
|
|
@@ -1176,9 +1185,32 @@ describe('HubToolsService', () => {
|
|
|
1176
1185
|
uptime: 1000
|
|
1177
1186
|
};
|
|
1178
1187
|
const mockTools = [{ name: 'testTool', description: 'Test tool', serverName: 'test-server' }];
|
|
1188
|
+
const mockConfig = {
|
|
1189
|
+
template: {
|
|
1190
|
+
type: 'stdio',
|
|
1191
|
+
command: '',
|
|
1192
|
+
args: [],
|
|
1193
|
+
env: {},
|
|
1194
|
+
headers: {},
|
|
1195
|
+
aggregatedTools: [],
|
|
1196
|
+
timeout: 30000
|
|
1197
|
+
},
|
|
1198
|
+
instances: [mockInstance],
|
|
1199
|
+
tagDefinitions: []
|
|
1200
|
+
};
|
|
1179
1201
|
// @ts-expect-error - Mocking for test purposes with extra fields
|
|
1180
1202
|
vi.mocked(hubManager.getServerInstancesByName).mockReturnValue([mockInstance]);
|
|
1181
|
-
|
|
1203
|
+
// @ts-expect-error - Mock config with simplified mock instance
|
|
1204
|
+
vi.mocked(hubManager.getServerByName).mockReturnValue(mockConfig);
|
|
1205
|
+
vi.mocked(mcpConnectionManager.getConnectedIndexes).mockReturnValue([0]);
|
|
1206
|
+
vi.mocked(mcpConnectionManager.getStatus).mockReturnValue({
|
|
1207
|
+
connected: true,
|
|
1208
|
+
lastCheck: mockInstance.lastHeartbeat,
|
|
1209
|
+
startTime: mockInstance.uptime,
|
|
1210
|
+
toolsCount: 1,
|
|
1211
|
+
resourcesCount: 0
|
|
1212
|
+
});
|
|
1213
|
+
vi.mocked(mcpConnectionManager.getToolsByServerName).mockReturnValue(mockTools);
|
|
1182
1214
|
// Act
|
|
1183
1215
|
const result = await hubToolsService.readResource(`hub://servers/${serverName}/tools`);
|
|
1184
1216
|
// Assert
|
|
@@ -1202,9 +1234,32 @@ describe('HubToolsService', () => {
|
|
|
1202
1234
|
uptime: 1000
|
|
1203
1235
|
};
|
|
1204
1236
|
const mockResources = [{ uri: 'test://resource', name: 'Test Resource' }];
|
|
1237
|
+
const mockConfig = {
|
|
1238
|
+
template: {
|
|
1239
|
+
type: 'stdio',
|
|
1240
|
+
command: '',
|
|
1241
|
+
args: [],
|
|
1242
|
+
env: {},
|
|
1243
|
+
headers: {},
|
|
1244
|
+
aggregatedTools: [],
|
|
1245
|
+
timeout: 30000
|
|
1246
|
+
},
|
|
1247
|
+
instances: [mockInstance],
|
|
1248
|
+
tagDefinitions: []
|
|
1249
|
+
};
|
|
1205
1250
|
// @ts-expect-error - Mocking for test purposes with extra fields
|
|
1206
1251
|
vi.mocked(hubManager.getServerInstancesByName).mockReturnValue([mockInstance]);
|
|
1207
|
-
|
|
1252
|
+
// @ts-expect-error - Mock config with simplified mock instance
|
|
1253
|
+
vi.mocked(hubManager.getServerByName).mockReturnValue(mockConfig);
|
|
1254
|
+
vi.mocked(mcpConnectionManager.getConnectedIndexes).mockReturnValue([0]);
|
|
1255
|
+
vi.mocked(mcpConnectionManager.getStatus).mockReturnValue({
|
|
1256
|
+
connected: true,
|
|
1257
|
+
lastCheck: mockInstance.lastHeartbeat,
|
|
1258
|
+
startTime: mockInstance.uptime,
|
|
1259
|
+
toolsCount: 0,
|
|
1260
|
+
resourcesCount: 1
|
|
1261
|
+
});
|
|
1262
|
+
vi.mocked(mcpConnectionManager.getResourcesByName).mockReturnValue(mockResources);
|
|
1208
1263
|
// Act
|
|
1209
1264
|
const result = await hubToolsService.readResource(`hub://servers/${serverName}/resources`);
|
|
1210
1265
|
// Assert
|