@loop_ouroboros/mcp-hub-lite 1.2.7 → 1.2.9
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 +24 -0
- package/dist/client/assets/{HomeView-CgEri1kD.js → HomeView-CGezWc0j.js} +1 -1
- package/dist/client/assets/{ResourceDetailView-B8Qo1_jK.js → ResourceDetailView-Bi5UsbFq.js} +1 -1
- package/dist/client/assets/{ResourceDetailView-DUJZbegl.css → ResourceDetailView-CDmWGdAK.css} +1 -1
- package/dist/client/assets/{ResourcesView-B12FzUdo.js → ResourcesView-B9anSm85.js} +1 -1
- package/dist/client/assets/{ServerDashboard-B3O-crvv.js → ServerDashboard-g5p4VC_-.js} +1 -1
- package/dist/client/assets/{ServerDetail-CXg8rI3q.css → ServerDetail-DCQH8HIb.css} +1 -1
- package/dist/client/assets/ServerDetail-DMoFqWCp.js +2 -0
- package/dist/client/assets/{ServerListView-SlZN8ppC.js → ServerListView-DZsy2gaQ.js} +1 -1
- package/dist/client/assets/{SettingsView-D8fiOG0O.js → SettingsView-DQSWb9xM.js} +1 -1
- package/dist/client/assets/{ToolCallDialog-DYEdhnCk.js → ToolCallDialog-BEyRp_J3.js} +1 -1
- package/dist/client/assets/ToolsView-BU7PKJwt.js +1 -0
- package/dist/client/assets/ToolsView-BkrQLjH9.css +1 -0
- package/dist/client/assets/{_baseClone-BYxCbA_9.js → _baseClone-DsVtZfPm.js} +1 -1
- package/dist/client/assets/{el-form-item-ySymCPMr.js → el-form-item-CTsVV8sm.js} +1 -1
- package/dist/client/assets/{el-input-C85p6Nqj.js → el-input-Bh1VGJTU.js} +1 -1
- package/dist/client/assets/{el-loading-DIjKEx81.js → el-loading-huOeK9cW.js} +1 -1
- package/dist/client/assets/{el-overlay-B_CxiSem.js → el-overlay-CR_KVhLU.js} +1 -1
- package/dist/client/assets/{el-radio-group-BjkTCPRf.js → el-radio-group-BSbtAW4k.js} +1 -1
- package/dist/client/assets/{el-skeleton-item-CupTKK6n.js → el-skeleton-item-BSxOLPFM.js} +1 -1
- package/dist/client/assets/{el-switch-BosIJ9jf.js → el-switch-BaQUQWTL.js} +1 -1
- package/dist/client/assets/{el-tab-pane-BydxdJoc.js → el-tab-pane-9JxLgdS7.js} +1 -1
- package/dist/client/assets/{el-table-column-DV5TZOUW.js → el-table-column-Du1l9Ww3.js} +1 -1
- package/dist/client/assets/{index-xJkq2euk.css → index-BNmwPGMT.css} +1 -1
- package/dist/client/assets/index-CsZoFRv1.js +2 -0
- package/dist/client/assets/{omit-DxDGRttI.js → omit-Btci9mp3.js} +1 -1
- package/dist/client/assets/{raf-Y9AoxecD.js → raf-tUu4BwZS.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/src/api/web/health.d.ts.map +1 -1
- package/dist/server/src/api/web/health.js +5 -0
- package/dist/server/src/api/web/search.d.ts +15 -0
- package/dist/server/src/api/web/search.d.ts.map +1 -1
- package/dist/server/src/api/web/search.js +22 -1
- package/dist/server/src/app.d.ts +7 -0
- package/dist/server/src/app.d.ts.map +1 -1
- package/dist/server/src/app.js +43 -2
- package/dist/server/src/models/event.model.d.ts +0 -98
- package/dist/server/src/models/event.model.d.ts.map +1 -1
- package/dist/server/src/models/event.model.js +0 -4
- package/dist/server/src/models/server.model.d.ts +0 -2
- package/dist/server/src/models/server.model.d.ts.map +1 -1
- package/dist/server/src/models/system-tools.constants.d.ts +6 -2
- package/dist/server/src/models/system-tools.constants.d.ts.map +1 -1
- package/dist/server/src/models/system-tools.constants.js +3 -1
- package/dist/server/src/pid/types.d.ts +0 -5
- package/dist/server/src/pid/types.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/system-tools-handler.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/system-tools-handler.js +28 -1
- package/dist/server/src/services/hub-manager.service.d.ts.map +1 -1
- package/dist/server/src/services/hub-manager.service.js +6 -2
- package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts +1 -0
- 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 +25 -1
- package/dist/server/src/services/hub-tools.service.d.ts +21 -4
- package/dist/server/src/services/hub-tools.service.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools.service.js +59 -1
- package/dist/server/src/services/system-tool-handler.d.ts.map +1 -1
- package/dist/server/src/services/system-tool-handler.js +9 -1
- package/dist/server/src/utils/instance-id.d.ts +0 -8
- package/dist/server/src/utils/instance-id.d.ts.map +1 -1
- package/dist/server/src/utils/instance-id.js +1 -1
- package/dist/server/src/utils/logger/index.d.ts +0 -22
- package/dist/server/src/utils/logger/index.d.ts.map +1 -1
- package/dist/server/src/utils/logger/index.js +0 -29
- package/dist/server/src/utils/sort-utils.d.ts +0 -16
- package/dist/server/src/utils/sort-utils.d.ts.map +1 -1
- package/dist/server/src/utils/sort-utils.js +0 -42
- package/dist/server/tests/unit/api/search.test.d.ts +2 -0
- package/dist/server/tests/unit/api/search.test.d.ts.map +1 -0
- package/dist/server/tests/unit/api/search.test.js +61 -0
- package/dist/server/tests/unit/services/hub-tools.service.test.js +2 -1
- package/dist/server/tests/unit/utils/logger.test.js +1 -23
- package/dist/server/tests/unit/utils/sort-utils.test.js +1 -92
- package/package.json +1 -1
- package/dist/client/assets/ServerDetail-Bz5_9yOY.js +0 -2
- package/dist/client/assets/ToolsView-BreAl-yn.js +0 -1
- package/dist/client/assets/ToolsView-BxgXvPC3.css +0 -1
- package/dist/client/assets/index-kC4mf0Vo.js +0 -2
|
@@ -6,7 +6,7 @@ import { logger, LOG_MODULES } from '../utils/logger.js';
|
|
|
6
6
|
import { stringifyForLogging } from '../utils/json-utils.js';
|
|
7
7
|
import { normalizeToolName } from '../utils/name-converter.js';
|
|
8
8
|
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
-
import { MCP_HUB_LITE_SERVER, LIST_SERVERS_TOOL, LIST_TOOLS_TOOL, GET_TOOL_TOOL, CALL_TOOL_TOOL, UPDATE_SERVER_DESCRIPTION_TOOL, LIST_TAGS_TOOL, SYSTEM_TOOL_NAMES } from '../models/system-tools.constants.js';
|
|
9
|
+
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';
|
|
10
10
|
import { ToolArgsParser } from '../utils/tool-args-parser.js';
|
|
11
11
|
import { hasValidId, selectBestInstance, getServerDescription, getSystemTools, generateDynamicResources, readResource as readResourceUtil } from './hub-tools/index.js';
|
|
12
12
|
import { InstanceSelector } from './hub-tools/instance-selector.js';
|
|
@@ -309,6 +309,14 @@ export class HubToolsService {
|
|
|
309
309
|
result = await this.listTags(toolArgs);
|
|
310
310
|
break;
|
|
311
311
|
}
|
|
312
|
+
case SEARCH_TOOLS_TOOL: {
|
|
313
|
+
const searchArgs = toolArgs;
|
|
314
|
+
if (!searchArgs.query) {
|
|
315
|
+
throw new Error('query is required for search_tools');
|
|
316
|
+
}
|
|
317
|
+
result = await this.searchTools(searchArgs.query);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
312
320
|
default:
|
|
313
321
|
throw new Error(`System tool "${toolName}" not found`);
|
|
314
322
|
}
|
|
@@ -579,6 +587,56 @@ export class HubToolsService {
|
|
|
579
587
|
}
|
|
580
588
|
return allTools;
|
|
581
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Searches for tools matching the query across all connected MCP servers.
|
|
592
|
+
*
|
|
593
|
+
* Results are grouped by server name, and only servers with at least one
|
|
594
|
+
* matching tool are included. Matching is case-insensitive on tool name and description.
|
|
595
|
+
*
|
|
596
|
+
* @param {string} query - Search query string for matching tool names and descriptions
|
|
597
|
+
* @returns {Promise<Record<string, { description: string; tools: ToolSummary[] }>>}
|
|
598
|
+
* Object mapping server names to their descriptions and matching tools
|
|
599
|
+
*/
|
|
600
|
+
async searchTools(query) {
|
|
601
|
+
if (!query || typeof query !== 'string') {
|
|
602
|
+
throw new Error('query is required and must be a non-empty string');
|
|
603
|
+
}
|
|
604
|
+
const normalizedQuery = query.toLowerCase();
|
|
605
|
+
const servers = hubManager.getAllServers();
|
|
606
|
+
const result = {};
|
|
607
|
+
for (const server of servers) {
|
|
608
|
+
if (!hasValidId(server)) {
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
const indexes = mcpConnectionManager.getConnectedIndexes(server.name);
|
|
612
|
+
if (indexes.length === 0) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
const description = getServerDescription(server.config, server.name);
|
|
616
|
+
const tools = mcpConnectionManager.getToolsByServerName(server.name);
|
|
617
|
+
if (tools.length === 0) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const matchingTools = tools
|
|
621
|
+
.filter((tool) => {
|
|
622
|
+
const nameMatch = tool.name.toLowerCase().includes(normalizedQuery);
|
|
623
|
+
const descMatch = tool.description?.toLowerCase().includes(normalizedQuery);
|
|
624
|
+
return nameMatch || descMatch;
|
|
625
|
+
})
|
|
626
|
+
.map((tool) => ({
|
|
627
|
+
name: tool.name,
|
|
628
|
+
description: tool.description,
|
|
629
|
+
serverName: server.name
|
|
630
|
+
}));
|
|
631
|
+
if (matchingTools.length > 0) {
|
|
632
|
+
result[server.name] = {
|
|
633
|
+
description,
|
|
634
|
+
tools: matchingTools
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
582
640
|
/**
|
|
583
641
|
* Lists all dynamically generated Hub resources based on connected MCP servers.
|
|
584
642
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-tool-handler.d.ts","sourceRoot":"","sources":["../../../../src/services/system-tool-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"system-tool-handler.d.ts","sourceRoot":"","sources":["../../../../src/services/system-tool-handler.ts"],"names":[],"mappings":"AAuBA;;GAEG;AACH,qBAAa,iBAAiB;IAC5B;;OAEG;WACU,oBAAoB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,OAAO,CAAC;CAmFpB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hubToolsService } from './hub-tools.service.js';
|
|
2
2
|
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { logger, LOG_MODULES } from '../utils/logger.js';
|
|
4
|
-
import { LIST_SERVERS_TOOL, LIST_TOOLS_TOOL, GET_TOOL_TOOL, CALL_TOOL_TOOL, UPDATE_SERVER_DESCRIPTION_TOOL, LIST_TAGS_TOOL } from '../models/system-tools.constants.js';
|
|
4
|
+
import { LIST_SERVERS_TOOL, LIST_TOOLS_TOOL, GET_TOOL_TOOL, CALL_TOOL_TOOL, UPDATE_SERVER_DESCRIPTION_TOOL, LIST_TAGS_TOOL, SEARCH_TOOLS_TOOL } from '../models/system-tools.constants.js';
|
|
5
5
|
import { stringifyForLogging } from '../utils/json-utils.js';
|
|
6
6
|
/**
|
|
7
7
|
* Unified system tool call handler
|
|
@@ -58,6 +58,14 @@ export class SystemToolHandler {
|
|
|
58
58
|
result = await hubToolsService.listTags(listTagsArgs);
|
|
59
59
|
break;
|
|
60
60
|
}
|
|
61
|
+
case SEARCH_TOOLS_TOOL: {
|
|
62
|
+
const searchArgs = toolArgs;
|
|
63
|
+
if (!searchArgs.query) {
|
|
64
|
+
throw new McpError(-32802, 'query is required for search_tools');
|
|
65
|
+
}
|
|
66
|
+
result = await hubToolsService.searchTools(searchArgs.query);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
61
69
|
default:
|
|
62
70
|
throw new McpError(-32801, `Unknown system tool: ${toolName}`);
|
|
63
71
|
}
|
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
* Instance ID generation utilities using content-based hashing.
|
|
3
3
|
*/
|
|
4
4
|
import type { ServerInstance } from '../config/config.schema.js';
|
|
5
|
-
/**
|
|
6
|
-
* Generates a stable 8-character hash from an object.
|
|
7
|
-
* Uses SHA-256 and returns the first 8 hex characters.
|
|
8
|
-
*
|
|
9
|
-
* @param obj - The object to hash
|
|
10
|
-
* @returns An 8-character hex string
|
|
11
|
-
*/
|
|
12
|
-
export declare function generateInstanceHash(obj: Record<string, unknown>): string;
|
|
13
5
|
/**
|
|
14
6
|
* Generates a deterministic instance ID based on server name and instance configuration.
|
|
15
7
|
* The ID format is: ${serverName}-${hash}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance-id.d.ts","sourceRoot":"","sources":["../../../../src/utils/instance-id.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"instance-id.d.ts","sourceRoot":"","sources":["../../../../src/utils/instance-id.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAoC/D;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,GACtC,MAAM,CAiBR"}
|
|
@@ -9,7 +9,7 @@ import { createHash } from 'node:crypto';
|
|
|
9
9
|
* @param obj - The object to hash
|
|
10
10
|
* @returns An 8-character hex string
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
function generateInstanceHash(obj) {
|
|
13
13
|
function stableStringify(value) {
|
|
14
14
|
if (value === null || typeof value !== 'object') {
|
|
15
15
|
return JSON.stringify(value);
|
|
@@ -10,27 +10,5 @@ export { formatTimestamp, formatLogLevel, formatPid, createColoredLogMessage, cr
|
|
|
10
10
|
export { isToolsListResponse, simplifyToolsListResponse, hasImageContent, simplifyImageContent, formatMcpMessageForLogging, isNotificationMessage, logNotificationMessage } from './log-output.js';
|
|
11
11
|
export { Logger } from './logger.js';
|
|
12
12
|
import { Logger } from './logger.js';
|
|
13
|
-
import type { LogContext } from './log-context.js';
|
|
14
13
|
export declare const logger: Logger;
|
|
15
|
-
/**
|
|
16
|
-
* Log a message with color formatting for console and plain text for file output.
|
|
17
|
-
*
|
|
18
|
-
* This function provides a convenient way to log messages that appear with
|
|
19
|
-
* ANSI color codes in the console but are written as plain text to log files.
|
|
20
|
-
* It uses the logger's internal formatting methods to ensure consistent output.
|
|
21
|
-
*
|
|
22
|
-
* @param coloredMessage - The message to display in the console with color formatting
|
|
23
|
-
* @param plainMessage - The message to write to log files in plain text format
|
|
24
|
-
* @param context - Optional logging context including PID, server name, trace ID, etc.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```typescript
|
|
28
|
-
* logWithColor(
|
|
29
|
-
* '\x1b[32m[SUCCESS]\x1b[0m Server started',
|
|
30
|
-
* '[SUCCESS] Server started',
|
|
31
|
-
* { serverName: 'mcp-hub', pid: process.pid }
|
|
32
|
-
* );
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export declare function logWithColor(coloredMessage: string, plainMessage: string, context?: LogContext): void;
|
|
36
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/utils/logger/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAG9E,OAAO,EACL,eAAe,EACf,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/utils/logger/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAG9E,OAAO,EACL,eAAe,EACf,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,eAAO,MAAM,MAAM,QAAe,CAAC"}
|
|
@@ -14,33 +14,4 @@ export { isToolsListResponse, simplifyToolsListResponse, hasImageContent, simpli
|
|
|
14
14
|
export { Logger } from './logger.js';
|
|
15
15
|
// Create and export the default logger instance
|
|
16
16
|
import { Logger } from './logger.js';
|
|
17
|
-
import { createColoredLogMessage, createLogMessage } from './log-formatter.js';
|
|
18
17
|
export const logger = new Logger();
|
|
19
|
-
/**
|
|
20
|
-
* Log a message with color formatting for console and plain text for file output.
|
|
21
|
-
*
|
|
22
|
-
* This function provides a convenient way to log messages that appear with
|
|
23
|
-
* ANSI color codes in the console but are written as plain text to log files.
|
|
24
|
-
* It uses the logger's internal formatting methods to ensure consistent output.
|
|
25
|
-
*
|
|
26
|
-
* @param coloredMessage - The message to display in the console with color formatting
|
|
27
|
-
* @param plainMessage - The message to write to log files in plain text format
|
|
28
|
-
* @param context - Optional logging context including PID, server name, trace ID, etc.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* logWithColor(
|
|
33
|
-
* '\x1b[32m[SUCCESS]\x1b[0m Server started',
|
|
34
|
-
* '[SUCCESS] Server started',
|
|
35
|
-
* { serverName: 'mcp-hub', pid: process.pid }
|
|
36
|
-
* );
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
export function logWithColor(coloredMessage, plainMessage, context) {
|
|
40
|
-
const coloredLogMsg = createColoredLogMessage('info', coloredMessage, context);
|
|
41
|
-
console.info(coloredLogMsg);
|
|
42
|
-
if (logger.logFileStream) {
|
|
43
|
-
const plainLogMsg = createLogMessage('info', plainMessage, context);
|
|
44
|
-
logger.logFileStream.write(plainLogMsg + '\n');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Object key sorting utilities for consistent configuration.
|
|
3
3
|
*/
|
|
4
|
-
/**
|
|
5
|
-
* Sorts object keys alphabetically using localeCompare.
|
|
6
|
-
* Returns a new object with sorted keys, preserving the original object.
|
|
7
|
-
*
|
|
8
|
-
* @param obj - Object to sort
|
|
9
|
-
* @returns New object with sorted keys
|
|
10
|
-
*/
|
|
11
|
-
export declare function sortObjectKeys<T extends Record<string, unknown>>(obj: T): T;
|
|
12
4
|
/**
|
|
13
5
|
* Sorts object keys alphabetically, case-insensitive.
|
|
14
6
|
* Returns a new object with sorted keys, preserving the original object and original key case.
|
|
@@ -17,14 +9,6 @@ export declare function sortObjectKeys<T extends Record<string, unknown>>(obj: T
|
|
|
17
9
|
* @returns New object with sorted keys (case-insensitive sort)
|
|
18
10
|
*/
|
|
19
11
|
export declare function sortObjectKeysCaseInsensitive<T extends Record<string, unknown>>(obj: T): T;
|
|
20
|
-
/**
|
|
21
|
-
* Recursively sorts all object keys in a nested structure.
|
|
22
|
-
* Returns a new object with all nested keys sorted, preserving the original object.
|
|
23
|
-
*
|
|
24
|
-
* @param obj - Object to sort recursively
|
|
25
|
-
* @returns New object with all nested keys sorted
|
|
26
|
-
*/
|
|
27
|
-
export declare function sortObjectKeysDeep<T>(obj: T): T;
|
|
28
12
|
/**
|
|
29
13
|
* Sorts env and headers objects in a server configuration.
|
|
30
14
|
* This is a convenience function for server template/instance configurations.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sort-utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/sort-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoDH;;;;;;GAMG;AACH,wBAAgB,
|
|
1
|
+
{"version":3,"file":"sort-utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/sort-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoDH;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAa1F;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,CAAC,SAAS;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC5E,MAAM,EAAE,CAAC,GAAG,CAAC,CAcd"}
|
|
@@ -49,24 +49,6 @@ function normalizeConfigUrls(obj) {
|
|
|
49
49
|
}
|
|
50
50
|
return obj;
|
|
51
51
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Sorts object keys alphabetically using localeCompare.
|
|
54
|
-
* Returns a new object with sorted keys, preserving the original object.
|
|
55
|
-
*
|
|
56
|
-
* @param obj - Object to sort
|
|
57
|
-
* @returns New object with sorted keys
|
|
58
|
-
*/
|
|
59
|
-
export function sortObjectKeys(obj) {
|
|
60
|
-
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
61
|
-
return obj;
|
|
62
|
-
}
|
|
63
|
-
const sortedObj = {};
|
|
64
|
-
const keys = Object.keys(obj).sort((a, b) => a.localeCompare(b));
|
|
65
|
-
for (const key of keys) {
|
|
66
|
-
sortedObj[key] = obj[key];
|
|
67
|
-
}
|
|
68
|
-
return sortedObj;
|
|
69
|
-
}
|
|
70
52
|
/**
|
|
71
53
|
* Sorts object keys alphabetically, case-insensitive.
|
|
72
54
|
* Returns a new object with sorted keys, preserving the original object and original key case.
|
|
@@ -85,30 +67,6 @@ export function sortObjectKeysCaseInsensitive(obj) {
|
|
|
85
67
|
}
|
|
86
68
|
return sortedObj;
|
|
87
69
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Recursively sorts all object keys in a nested structure.
|
|
90
|
-
* Returns a new object with all nested keys sorted, preserving the original object.
|
|
91
|
-
*
|
|
92
|
-
* @param obj - Object to sort recursively
|
|
93
|
-
* @returns New object with all nested keys sorted
|
|
94
|
-
*/
|
|
95
|
-
export function sortObjectKeysDeep(obj) {
|
|
96
|
-
if (!obj) {
|
|
97
|
-
return obj;
|
|
98
|
-
}
|
|
99
|
-
if (Array.isArray(obj)) {
|
|
100
|
-
return obj.map((item) => sortObjectKeysDeep(item));
|
|
101
|
-
}
|
|
102
|
-
if (typeof obj === 'object') {
|
|
103
|
-
const sortedObj = {};
|
|
104
|
-
const keys = Object.keys(obj).sort((a, b) => a.localeCompare(b));
|
|
105
|
-
for (const key of keys) {
|
|
106
|
-
sortedObj[key] = sortObjectKeysDeep(obj[key]);
|
|
107
|
-
}
|
|
108
|
-
return sortedObj;
|
|
109
|
-
}
|
|
110
|
-
return obj;
|
|
111
|
-
}
|
|
112
70
|
/**
|
|
113
71
|
* Sorts env and headers objects in a server configuration.
|
|
114
72
|
* This is a convenience function for server template/instance configurations.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../../../../tests/unit/api/search.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { filterByAggregatedTools } from '../../../src/api/web/search.js';
|
|
3
|
+
function mockConfig(aggregatedTools = []) {
|
|
4
|
+
return {
|
|
5
|
+
template: {
|
|
6
|
+
type: 'stdio',
|
|
7
|
+
command: 'test',
|
|
8
|
+
args: [],
|
|
9
|
+
env: {},
|
|
10
|
+
headers: {},
|
|
11
|
+
aggregatedTools,
|
|
12
|
+
timeout: 30000
|
|
13
|
+
},
|
|
14
|
+
instances: [],
|
|
15
|
+
tagDefinitions: []
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function mockTool(name, serverName = 'test-server') {
|
|
19
|
+
return { name, description: `Tool ${name}`, serverName };
|
|
20
|
+
}
|
|
21
|
+
describe('filterByAggregatedTools', () => {
|
|
22
|
+
it('should skip tools when aggregatedTools is empty', () => {
|
|
23
|
+
const tools = [mockTool('tool1'), mockTool('tool2')];
|
|
24
|
+
const getConfig = () => mockConfig([]);
|
|
25
|
+
const result = filterByAggregatedTools(tools, getConfig);
|
|
26
|
+
expect(result).toHaveLength(0);
|
|
27
|
+
});
|
|
28
|
+
it('should only include tools in aggregatedTools list', () => {
|
|
29
|
+
const tools = [mockTool('tool1'), mockTool('tool2'), mockTool('tool3')];
|
|
30
|
+
const getConfig = () => mockConfig(['tool1', 'tool3']);
|
|
31
|
+
const result = filterByAggregatedTools(tools, getConfig);
|
|
32
|
+
expect(result).toHaveLength(2);
|
|
33
|
+
expect(result.map((t) => t.name)).toEqual(['tool1', 'tool3']);
|
|
34
|
+
});
|
|
35
|
+
it('should skip tools when server config not found', () => {
|
|
36
|
+
const tools = [mockTool('tool1')];
|
|
37
|
+
const getConfig = () => undefined;
|
|
38
|
+
const result = filterByAggregatedTools(tools, getConfig);
|
|
39
|
+
expect(result).toHaveLength(0);
|
|
40
|
+
});
|
|
41
|
+
it('should filter tools from multiple servers independently', () => {
|
|
42
|
+
const tools = [
|
|
43
|
+
mockTool('a', 'server-a'),
|
|
44
|
+
mockTool('b', 'server-a'),
|
|
45
|
+
mockTool('x', 'server-b'),
|
|
46
|
+
mockTool('y', 'server-b')
|
|
47
|
+
];
|
|
48
|
+
const configs = {
|
|
49
|
+
'server-a': mockConfig(['a']),
|
|
50
|
+
'server-b': mockConfig([])
|
|
51
|
+
};
|
|
52
|
+
const getConfig = (name) => configs[name];
|
|
53
|
+
const result = filterByAggregatedTools(tools, getConfig);
|
|
54
|
+
expect(result).toHaveLength(1);
|
|
55
|
+
expect(result[0].name).toBe('a');
|
|
56
|
+
});
|
|
57
|
+
it('should return empty array when no tools provided', () => {
|
|
58
|
+
const result = filterByAggregatedTools([], () => mockConfig(['tool1']));
|
|
59
|
+
expect(result).toHaveLength(0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -615,7 +615,8 @@ describe('HubToolsService', () => {
|
|
|
615
615
|
expect(systemToolNames).toContain('call_tool');
|
|
616
616
|
expect(systemToolNames).toContain('update_server_description');
|
|
617
617
|
expect(systemToolNames).toContain('list_tags');
|
|
618
|
-
expect(systemToolNames).
|
|
618
|
+
expect(systemToolNames).toContain('search_tools');
|
|
619
|
+
expect(systemToolNames).toHaveLength(7);
|
|
619
620
|
// Assert server tools - should have only name and description
|
|
620
621
|
expect(allTools['Server 1'].tools).toEqual(expectedToolSummariesServer1);
|
|
621
622
|
expect(allTools['Server 2'].tools).toEqual(expectedToolSummariesServer2);
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { Logger
|
|
5
|
+
import { Logger } from '../../../src/utils/logger.js';
|
|
6
6
|
import { setJsonPrettyConfigGetter, setDevModeEnabled } from '../../../src/utils/json-utils.js';
|
|
7
7
|
import { createColoredLogMessage, createLogMessage, getCallerInfo, formatCallerInfo } from '../../../src/utils/logger/log-formatter.js';
|
|
8
8
|
describe('Logger', () => {
|
|
@@ -363,28 +363,6 @@ describe('Logger', () => {
|
|
|
363
363
|
expect(writeSpy).toHaveBeenCalledWith(expect.stringContaining('file line 3'));
|
|
364
364
|
vi.restoreAllMocks();
|
|
365
365
|
});
|
|
366
|
-
it('should handle logWithColor function with traceId and spanId', () => {
|
|
367
|
-
// Reset logger instance to avoid interference between tests
|
|
368
|
-
const consoleInfoSpy = vi.spyOn(console, 'info').mockImplementation(() => { });
|
|
369
|
-
logWithColor('colored message', 'plain message', {
|
|
370
|
-
pid: 123,
|
|
371
|
-
serverName: 'test-server',
|
|
372
|
-
traceId: '1234567890abcdef1234567890abcdef',
|
|
373
|
-
spanId: 'abcdef1234567890'
|
|
374
|
-
});
|
|
375
|
-
expect(consoleInfoSpy).toHaveBeenCalledWith(expect.stringContaining('[TID:1234567890abcdef1234567890abcdef]'));
|
|
376
|
-
expect(consoleInfoSpy).toHaveBeenCalledWith(expect.stringContaining('[SID:abcdef1234567890]'));
|
|
377
|
-
// Restore console methods
|
|
378
|
-
consoleInfoSpy.mockRestore();
|
|
379
|
-
});
|
|
380
|
-
it('should handle logWithColor function without context', () => {
|
|
381
|
-
// Reset logger instance to avoid interference between tests
|
|
382
|
-
const consoleInfoSpy = vi.spyOn(console, 'info').mockImplementation(() => { });
|
|
383
|
-
logWithColor('colored message', 'plain message');
|
|
384
|
-
expect(consoleInfoSpy).toHaveBeenCalled();
|
|
385
|
-
// Restore console methods
|
|
386
|
-
consoleInfoSpy.mockRestore();
|
|
387
|
-
});
|
|
388
366
|
describe('dev log rotation', () => {
|
|
389
367
|
it('should create devLogRotator when enableDevLog is called', () => {
|
|
390
368
|
// Mock fs modules to avoid file system operations
|
|
@@ -1,50 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { sortObjectKeysCaseInsensitive, sortServerConfigEnvHeaders } from '../../../src/utils/sort-utils.js';
|
|
3
3
|
describe('sort-utils', () => {
|
|
4
|
-
describe('sortObjectKeys', () => {
|
|
5
|
-
it('should sort object keys alphabetically using localeCompare', () => {
|
|
6
|
-
const obj = {
|
|
7
|
-
zebra: 'value3',
|
|
8
|
-
apple: 'value1',
|
|
9
|
-
banana: 'value2'
|
|
10
|
-
};
|
|
11
|
-
const result = sortObjectKeys(obj);
|
|
12
|
-
expect(Object.keys(result)).toEqual(['apple', 'banana', 'zebra']);
|
|
13
|
-
expect(result).toEqual({
|
|
14
|
-
apple: 'value1',
|
|
15
|
-
banana: 'value2',
|
|
16
|
-
zebra: 'value3'
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
it('should return the same object if input is null or undefined', () => {
|
|
20
|
-
expect(sortObjectKeys(null)).toBe(null);
|
|
21
|
-
expect(sortObjectKeys(undefined)).toBe(undefined);
|
|
22
|
-
});
|
|
23
|
-
it('should return the same object if input is an array', () => {
|
|
24
|
-
const arr = [3, 1, 2];
|
|
25
|
-
expect(sortObjectKeys(arr)).toBe(arr);
|
|
26
|
-
});
|
|
27
|
-
it('should return the same object if input is not an object', () => {
|
|
28
|
-
expect(sortObjectKeys('string')).toBe('string');
|
|
29
|
-
expect(sortObjectKeys(42)).toBe(42);
|
|
30
|
-
expect(sortObjectKeys(true)).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
it('should preserve the original object (return a new object)', () => {
|
|
33
|
-
const obj = {
|
|
34
|
-
zebra: 'value3',
|
|
35
|
-
apple: 'value1'
|
|
36
|
-
};
|
|
37
|
-
const result = sortObjectKeys(obj);
|
|
38
|
-
expect(result).not.toBe(obj);
|
|
39
|
-
expect(obj).toEqual({
|
|
40
|
-
zebra: 'value3',
|
|
41
|
-
apple: 'value1'
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
it('should handle empty object', () => {
|
|
45
|
-
expect(sortObjectKeys({})).toEqual({});
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
4
|
describe('sortObjectKeysCaseInsensitive', () => {
|
|
49
5
|
it('should sort object keys alphabetically, case-insensitive', () => {
|
|
50
6
|
const obj = {
|
|
@@ -77,53 +33,6 @@ describe('sort-utils', () => {
|
|
|
77
33
|
expect(sortObjectKeysCaseInsensitive(undefined)).toBe(undefined);
|
|
78
34
|
});
|
|
79
35
|
});
|
|
80
|
-
describe('sortObjectKeysDeep', () => {
|
|
81
|
-
it('should recursively sort all object keys in a nested structure', () => {
|
|
82
|
-
const obj = {
|
|
83
|
-
zebra: {
|
|
84
|
-
delta: 'value4',
|
|
85
|
-
alpha: 'value1'
|
|
86
|
-
},
|
|
87
|
-
apple: {
|
|
88
|
-
charlie: 'value3',
|
|
89
|
-
bravo: 'value2'
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
const result = sortObjectKeysDeep(obj);
|
|
93
|
-
expect(Object.keys(result)).toEqual(['apple', 'zebra']);
|
|
94
|
-
expect(Object.keys(result.apple)).toEqual(['bravo', 'charlie']);
|
|
95
|
-
expect(Object.keys(result.zebra)).toEqual(['alpha', 'delta']);
|
|
96
|
-
});
|
|
97
|
-
it('should handle arrays and recursively sort objects within arrays', () => {
|
|
98
|
-
const obj = {
|
|
99
|
-
items: [
|
|
100
|
-
{ zebra: 'value3', apple: 'value1' },
|
|
101
|
-
{ delta: 'value4', alpha: 'value1' }
|
|
102
|
-
]
|
|
103
|
-
};
|
|
104
|
-
const result = sortObjectKeysDeep(obj);
|
|
105
|
-
expect(Array.isArray(result.items)).toBe(true);
|
|
106
|
-
expect(Object.keys(result.items[0])).toEqual(['apple', 'zebra']);
|
|
107
|
-
expect(Object.keys(result.items[1])).toEqual(['alpha', 'delta']);
|
|
108
|
-
});
|
|
109
|
-
it('should handle mixed types in nested structure', () => {
|
|
110
|
-
const obj = {
|
|
111
|
-
number: 42,
|
|
112
|
-
string: 'hello',
|
|
113
|
-
boolean: true,
|
|
114
|
-
array: [1, 2, 3],
|
|
115
|
-
object: {
|
|
116
|
-
nested: {
|
|
117
|
-
z: 'last',
|
|
118
|
-
a: 'first'
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
const result = sortObjectKeysDeep(obj);
|
|
123
|
-
expect(Object.keys(result)).toEqual(['array', 'boolean', 'number', 'object', 'string']);
|
|
124
|
-
expect(Object.keys(result.object.nested)).toEqual(['a', 'z']);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
36
|
describe('sortServerConfigEnvHeaders', () => {
|
|
128
37
|
it('should sort env and headers objects in a server configuration', () => {
|
|
129
38
|
const config = {
|