@loop_ouroboros/mcp-hub-lite 1.2.9 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/README.md +405 -331
- package/dist/client/assets/{HomeView-CGezWc0j.js → HomeView-DplI3V-h.js} +1 -1
- package/dist/client/assets/{ResourceDetailView-CDmWGdAK.css → ResourceDetailView-BkTSg91z.css} +1 -1
- package/dist/client/assets/ResourceDetailView-CeHPn99Y.js +1 -0
- package/dist/client/assets/ResourcesView-C1ObRhYS.js +1 -0
- package/dist/client/assets/ResourcesView-zgV8Nq7w.css +1 -0
- package/dist/client/assets/{ServerDashboard-g5p4VC_-.js → ServerDashboard-D7wG4Gvt.js} +1 -1
- package/dist/client/assets/{ServerDetail-DCQH8HIb.css → ServerDetail-CPNAFBPM.css} +1 -1
- package/dist/client/assets/ServerDetail-G23phOcJ.js +2 -0
- package/dist/client/assets/{ServerListView-DZsy2gaQ.js → ServerListView-BFiZLtPO.js} +1 -1
- package/dist/client/assets/{ServerStatusTags.vue_vue_type_script_setup_true_lang-DmGg4uuV.js → ServerStatusTags.vue_vue_type_script_setup_true_lang-Deb_SbFw.js} +1 -1
- package/dist/client/assets/SettingsView-QBFLZ6fP.js +1 -0
- package/dist/client/assets/ToolCallDialog-BQ9UJZ_-.css +1 -0
- package/dist/client/assets/ToolCallDialog-DYS-ADCL.js +1 -0
- package/dist/client/assets/ToolsView-DYwgtm7W.js +1 -0
- package/dist/client/assets/ToolsView-cO61nMNr.css +1 -0
- package/dist/client/assets/_baseClone-DQno9YO3.js +1 -0
- package/dist/client/assets/{el-form-item-CTsVV8sm.js → el-form-item-DF0zzQdH.js} +2 -2
- package/dist/client/assets/el-input-C_p2Qw42.js +1 -0
- package/dist/client/assets/el-loading-BaenpNzU.js +1 -0
- package/dist/client/assets/el-overlay-MbIUXSQ7.js +1 -0
- package/dist/client/assets/el-radio-group-COnCjCcz.js +1 -0
- package/dist/client/assets/el-skeleton-item-qj0eQP4s.js +1 -0
- package/dist/client/assets/el-switch-BZbXqB3_.js +1 -0
- package/dist/client/assets/el-tab-pane-w7RltRLd.js +1 -0
- package/dist/client/assets/el-table-column-OD8zhFcD.js +1 -0
- package/dist/client/assets/index-DwhULJXZ.js +2 -0
- package/dist/client/assets/{index-BNmwPGMT.css → index-UtsV0Cvh.css} +1 -1
- package/dist/client/assets/{omit-Btci9mp3.js → omit-BAJQlviJ.js} +1 -1
- package/dist/client/assets/raf-B1Ry7ruA.js +1 -0
- package/dist/client/assets/{vue-vendor-Dwcr0jep.js → vue-vendor-ClSvefnQ.js} +1 -1
- package/dist/client/index.html +3 -3
- package/dist/server/shared/models/constants.d.ts +8 -0
- package/dist/server/shared/models/constants.d.ts.map +1 -0
- package/dist/server/shared/models/constants.js +6 -0
- package/dist/server/shared/models/index.d.ts +1 -0
- package/dist/server/shared/models/index.d.ts.map +1 -1
- package/dist/server/shared/models/index.js +1 -0
- package/dist/server/shared/models/server.model.d.ts +14 -0
- package/dist/server/shared/models/server.model.d.ts.map +1 -1
- package/dist/server/shared/models/server.model.js +27 -4
- package/dist/server/shared/types/index.d.ts +0 -1
- package/dist/server/shared/types/index.d.ts.map +1 -1
- package/dist/server/shared/types/index.js +0 -1
- package/dist/server/src/api/mcp/debug-response-wrapper.js +1 -1
- package/dist/server/src/api/mcp/gateway.d.ts +10 -6
- package/dist/server/src/api/mcp/gateway.d.ts.map +1 -1
- package/dist/server/src/api/mcp/gateway.js +235 -87
- package/dist/server/src/api/web/hub-tools.d.ts.map +1 -1
- package/dist/server/src/api/web/hub-tools.js +11 -0
- package/dist/server/src/api/web/mcp-status.js +2 -2
- package/dist/server/src/api/web/search.d.ts +2 -16
- package/dist/server/src/api/web/search.d.ts.map +1 -1
- package/dist/server/src/api/web/search.js +22 -30
- package/dist/server/src/api/web/servers.js +1 -1
- package/dist/server/src/api/web/sessions.d.ts +1 -27
- package/dist/server/src/api/web/sessions.d.ts.map +1 -1
- package/dist/server/src/api/web/sessions.js +8 -97
- package/dist/server/src/api/ws/events.js +1 -1
- package/dist/server/src/api/ws/ws-handler.js +1 -1
- package/dist/server/src/app.d.ts.map +1 -1
- package/dist/server/src/app.js +6 -1
- package/dist/server/src/cli/commands/status.js +39 -1
- package/dist/server/src/cli/commands/tool-use.d.ts +10 -3
- package/dist/server/src/cli/commands/tool-use.d.ts.map +1 -1
- package/dist/server/src/cli/commands/tool-use.js +69 -30
- package/dist/server/src/cli/commands/use-guide.d.ts +0 -8
- package/dist/server/src/cli/commands/use-guide.d.ts.map +1 -1
- package/dist/server/src/cli/commands/use-guide.js +28 -170
- package/dist/server/src/cli/server.d.ts +10 -0
- package/dist/server/src/cli/server.d.ts.map +1 -1
- package/dist/server/src/cli/server.js +31 -1
- package/dist/server/src/config/config-change-logger.js +1 -1
- package/dist/server/src/config/config-loader.js +1 -1
- package/dist/server/src/config/config-manager.js +1 -1
- package/dist/server/src/config/config-migrator.d.ts +4 -48
- package/dist/server/src/config/config-migrator.d.ts.map +1 -1
- package/dist/server/src/config/config-migrator.js +2 -103
- package/dist/server/src/config/config-saver.js +1 -1
- package/dist/server/src/config/server-config-manager.js +1 -1
- package/dist/server/src/models/system-tools.constants.d.ts +2 -1
- package/dist/server/src/models/system-tools.constants.d.ts.map +1 -1
- package/dist/server/src/models/system-tools.constants.js +2 -1
- package/dist/server/src/pid/manager.js +1 -1
- package/dist/server/src/server/dev-server.js +4 -2
- package/dist/server/src/server/runner.d.ts.map +1 -1
- package/dist/server/src/server/runner.js +4 -2
- package/dist/server/src/server/startup.js +2 -2
- package/dist/server/src/services/connection/connection-manager.d.ts +2 -0
- package/dist/server/src/services/connection/connection-manager.d.ts.map +1 -1
- package/dist/server/src/services/connection/connection-manager.js +27 -25
- package/dist/server/src/services/connection/tool-cache.d.ts.map +1 -1
- package/dist/server/src/services/connection/tool-cache.js +10 -8
- package/dist/server/src/services/event-bus.service.d.ts +3 -1
- package/dist/server/src/services/event-bus.service.d.ts.map +1 -1
- package/dist/server/src/services/event-bus.service.js +1 -0
- package/dist/server/src/services/gateway/gateway.service.d.ts +14 -0
- package/dist/server/src/services/gateway/gateway.service.d.ts.map +1 -1
- package/dist/server/src/services/gateway/gateway.service.js +101 -7
- package/dist/server/src/services/gateway/global-transport.d.ts +20 -10
- package/dist/server/src/services/gateway/global-transport.d.ts.map +1 -1
- package/dist/server/src/services/gateway/global-transport.js +50 -34
- package/dist/server/src/services/gateway/request-handlers/call-tool-handler.d.ts +1 -2
- package/dist/server/src/services/gateway/request-handlers/call-tool-handler.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/call-tool-handler.js +24 -13
- package/dist/server/src/services/gateway/request-handlers/initialize-handler.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/initialize-handler.js +22 -6
- package/dist/server/src/services/gateway/request-handlers/resources-handler.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/resources-handler.js +12 -4
- package/dist/server/src/services/gateway/session-manager.d.ts +101 -0
- package/dist/server/src/services/gateway/session-manager.d.ts.map +1 -0
- package/dist/server/src/services/gateway/session-manager.js +256 -0
- package/dist/server/src/services/gateway/tool-list-generator.d.ts +14 -19
- package/dist/server/src/services/gateway/tool-list-generator.d.ts.map +1 -1
- package/dist/server/src/services/gateway/tool-list-generator.js +221 -80
- package/dist/server/src/services/hub-manager.service.d.ts.map +1 -1
- package/dist/server/src/services/hub-manager.service.js +15 -2
- package/dist/server/src/services/hub-tools/instance-selector.js +1 -1
- package/dist/server/src/services/hub-tools/resource-generator.d.ts +1 -22
- package/dist/server/src/services/hub-tools/resource-generator.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/resource-generator.js +24 -22
- package/dist/server/src/services/hub-tools/server-selector.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 +18 -13
- package/dist/server/src/services/log-storage.service.js +1 -1
- package/dist/server/src/services/system-tool-handler.js +1 -1
- package/dist/server/src/utils/error-handler.js +1 -1
- package/dist/server/src/utils/index.d.ts +1 -1
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/src/utils/index.js +1 -1
- package/dist/server/src/utils/json-utils.d.ts +9 -0
- package/dist/server/src/utils/json-utils.d.ts.map +1 -1
- package/dist/server/src/utils/json-utils.js +23 -4
- package/dist/server/src/utils/log-rotator.d.ts +0 -15
- package/dist/server/src/utils/log-rotator.d.ts.map +1 -1
- package/dist/server/src/utils/log-rotator.js +0 -18
- package/dist/server/src/utils/logger/index.d.ts +1 -1
- package/dist/server/src/utils/logger/index.d.ts.map +1 -1
- package/dist/server/src/utils/logger/index.js +1 -1
- package/dist/server/src/utils/logger/log-context.d.ts +1 -0
- package/dist/server/src/utils/logger/log-context.d.ts.map +1 -1
- package/dist/server/src/utils/logger/log-formatter.d.ts.map +1 -1
- package/dist/server/src/utils/logger/log-formatter.js +25 -11
- package/dist/server/src/utils/logger/log-output.d.ts +17 -1
- package/dist/server/src/utils/logger/log-output.d.ts.map +1 -1
- package/dist/server/src/utils/logger/log-output.js +46 -40
- package/dist/server/src/utils/logger/logger.d.ts.map +1 -1
- package/dist/server/src/utils/logger/logger.js +18 -2
- package/dist/server/src/utils/port-checker.js +1 -1
- package/dist/server/src/utils/request-context.d.ts +8 -70
- package/dist/server/src/utils/request-context.d.ts.map +1 -1
- package/dist/server/src/utils/request-context.js +11 -70
- package/dist/server/src/utils/transports/stdio-transport.js +1 -1
- package/dist/server/src/utils/transports/streamable-http-transport.js +1 -1
- package/dist/server/src/utils/transports/transport-factory.d.ts.map +1 -1
- package/dist/server/src/utils/transports/transport-factory.js +26 -3
- package/dist/server/tests/contract/mcp-protocol/initialize.test.js +1 -1
- package/dist/server/tests/contract/mcp-protocol/tools-call.test.js +1 -1
- package/dist/server/tests/contract/mcp-protocol/tools-list.test.js +1 -1
- package/dist/server/tests/integration/gateway/fault-tolerance.test.js +1 -1
- package/dist/server/tests/integration/gateway/mcp-connection.test.js +1 -1
- package/dist/server/tests/types/logger-test-helpers.d.ts +1 -1
- package/dist/server/tests/types/logger-test-helpers.d.ts.map +1 -1
- package/dist/server/tests/unit/config/config-migrator.test.js +45 -105
- package/dist/server/tests/unit/config/config-saver.test.js +1 -1
- package/dist/server/tests/unit/config/config.schema.test.js +2 -1
- package/dist/server/tests/unit/server/runner.test.js +19 -13
- package/dist/server/tests/unit/services/gateway-logging.test.js +1 -1
- package/dist/server/tests/unit/services/gateway-session-mode.test.d.ts +2 -0
- package/dist/server/tests/unit/services/gateway-session-mode.test.d.ts.map +1 -0
- package/dist/server/tests/unit/services/gateway-session-mode.test.js +174 -0
- package/dist/server/tests/unit/services/hub-manager-service.test.js +4 -5
- package/dist/server/tests/unit/services/hub-tools.service.test.js +82 -6
- package/dist/server/tests/unit/utils/config.test.js +14 -7
- package/dist/server/tests/unit/utils/log-output.test.d.ts +2 -0
- package/dist/server/tests/unit/utils/log-output.test.d.ts.map +1 -0
- package/dist/server/tests/unit/utils/log-output.test.js +198 -0
- package/dist/server/tests/unit/utils/log-rotator.test.js +1 -15
- package/dist/server/tests/unit/utils/logger.test.js +1 -1
- package/dist/server/vitest.config.d.ts.map +1 -1
- package/dist/server/vitest.config.js +0 -2
- package/package.json +1 -3
- package/dist/client/assets/ResourceDetailView-Bi5UsbFq.js +0 -1
- package/dist/client/assets/ResourcesView-B9anSm85.js +0 -1
- package/dist/client/assets/ResourcesView-Cc8RHtia.css +0 -1
- package/dist/client/assets/ServerDetail-DMoFqWCp.js +0 -2
- package/dist/client/assets/SettingsView-DQSWb9xM.js +0 -1
- package/dist/client/assets/ToolCallDialog-BEyRp_J3.js +0 -1
- package/dist/client/assets/ToolCallDialog-BhdPX-Kf.css +0 -1
- package/dist/client/assets/ToolsView-BU7PKJwt.js +0 -1
- package/dist/client/assets/ToolsView-BkrQLjH9.css +0 -1
- package/dist/client/assets/_baseClone-DsVtZfPm.js +0 -1
- package/dist/client/assets/el-input-Bh1VGJTU.js +0 -1
- package/dist/client/assets/el-loading-huOeK9cW.js +0 -1
- package/dist/client/assets/el-overlay-CR_KVhLU.js +0 -1
- package/dist/client/assets/el-radio-group-BSbtAW4k.js +0 -1
- package/dist/client/assets/el-skeleton-item-BSxOLPFM.js +0 -1
- package/dist/client/assets/el-switch-BaQUQWTL.js +0 -1
- package/dist/client/assets/el-tab-pane-9JxLgdS7.js +0 -1
- package/dist/client/assets/el-table-column-Du1l9Ww3.js +0 -1
- package/dist/client/assets/index-CsZoFRv1.js +0 -2
- package/dist/client/assets/raf-tUu4BwZS.js +0 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { SESSION_MODE_STATEFUL, SESSION_MODE_STATELESS } from '../../../shared/models/constants.js';
|
|
3
|
+
// Mock configManager before importing the module under test
|
|
4
|
+
const mockGetConfig = vi.fn();
|
|
5
|
+
vi.mock('@config/config-manager.js', () => ({
|
|
6
|
+
configManager: {
|
|
7
|
+
getConfig: () => mockGetConfig()
|
|
8
|
+
}
|
|
9
|
+
}));
|
|
10
|
+
// We need to import resolveSessionMode from gateway.ts
|
|
11
|
+
// It's exported from the module, but the module has side-effects (Fastify routes)
|
|
12
|
+
// Use dynamic import after mocks are set up
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
let resolveSessionMode;
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
vi.resetModules();
|
|
17
|
+
mockGetConfig.mockReset();
|
|
18
|
+
const mod = await import('../../../src/api/mcp/gateway.js');
|
|
19
|
+
resolveSessionMode = mod.resolveSessionMode;
|
|
20
|
+
});
|
|
21
|
+
function makeRequest(headers = {}) {
|
|
22
|
+
return { headers };
|
|
23
|
+
}
|
|
24
|
+
describe('resolveSessionMode', () => {
|
|
25
|
+
describe('request header override (highest priority)', () => {
|
|
26
|
+
test('x-mcp-session-mode: stateless overrides UA match', () => {
|
|
27
|
+
mockGetConfig.mockReturnValue({
|
|
28
|
+
system: {
|
|
29
|
+
session: {
|
|
30
|
+
sessionModeRules: { stateful: ['claude-code'], stateless: [] },
|
|
31
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const request = makeRequest({
|
|
36
|
+
'x-mcp-session-mode': SESSION_MODE_STATELESS,
|
|
37
|
+
'user-agent': 'claude-code/2.1.140 (cli)'
|
|
38
|
+
});
|
|
39
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATELESS);
|
|
40
|
+
});
|
|
41
|
+
test('x-mcp-session-mode: stateful overrides UA match', () => {
|
|
42
|
+
mockGetConfig.mockReturnValue({
|
|
43
|
+
system: {
|
|
44
|
+
session: {
|
|
45
|
+
sessionModeRules: { stateful: [], stateless: ['cherrystudio'] },
|
|
46
|
+
defaultSessionMode: SESSION_MODE_STATELESS
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const request = makeRequest({
|
|
51
|
+
'x-mcp-session-mode': SESSION_MODE_STATEFUL,
|
|
52
|
+
'user-agent': 'CherryStudio/1.9.7'
|
|
53
|
+
});
|
|
54
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATEFUL);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('UA keyword matching', () => {
|
|
58
|
+
test('matches stateless pattern (case-insensitive)', () => {
|
|
59
|
+
mockGetConfig.mockReturnValue({
|
|
60
|
+
system: {
|
|
61
|
+
session: {
|
|
62
|
+
sessionModeRules: { stateful: [], stateless: ['cherrystudio'] },
|
|
63
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const request = makeRequest({
|
|
68
|
+
'user-agent': 'Mozilla/5.0 ... CherryStudio/1.9.7 Chrome/146.0.7680.188 Electron/41.2.1'
|
|
69
|
+
});
|
|
70
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATELESS);
|
|
71
|
+
});
|
|
72
|
+
test('matches stateless pattern with different casing in UA', () => {
|
|
73
|
+
mockGetConfig.mockReturnValue({
|
|
74
|
+
system: {
|
|
75
|
+
session: {
|
|
76
|
+
sessionModeRules: { stateful: [], stateless: ['cherrystudio'] },
|
|
77
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const request = makeRequest({ 'user-agent': 'CHERRYSTUDIO/1.9.7' });
|
|
82
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATELESS);
|
|
83
|
+
});
|
|
84
|
+
test('matches stateless pattern with different casing in rule', () => {
|
|
85
|
+
mockGetConfig.mockReturnValue({
|
|
86
|
+
system: {
|
|
87
|
+
session: {
|
|
88
|
+
sessionModeRules: { stateful: [], stateless: ['CherryStudio'] },
|
|
89
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const request = makeRequest({ 'user-agent': 'cherrystudio/1.9.7' });
|
|
94
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATELESS);
|
|
95
|
+
});
|
|
96
|
+
test('matches stateful pattern', () => {
|
|
97
|
+
mockGetConfig.mockReturnValue({
|
|
98
|
+
system: {
|
|
99
|
+
session: {
|
|
100
|
+
sessionModeRules: { stateful: ['claude-code'], stateless: [] },
|
|
101
|
+
defaultSessionMode: SESSION_MODE_STATELESS
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const request = makeRequest({ 'user-agent': 'claude-code/2.1.140 (cli)' });
|
|
106
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATEFUL);
|
|
107
|
+
});
|
|
108
|
+
test('stateless rules checked before stateful (stateless wins on conflict)', () => {
|
|
109
|
+
mockGetConfig.mockReturnValue({
|
|
110
|
+
system: {
|
|
111
|
+
session: {
|
|
112
|
+
sessionModeRules: { stateful: ['claude'], stateless: ['claude-code'] },
|
|
113
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// "claude-code" matches both "claude" and "claude-code", but stateless checked first
|
|
118
|
+
const request = makeRequest({ 'user-agent': 'claude-code/2.1.140' });
|
|
119
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATELESS);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('default fallback', () => {
|
|
123
|
+
test('no matching UA falls back to defaultSessionMode', () => {
|
|
124
|
+
mockGetConfig.mockReturnValue({
|
|
125
|
+
system: {
|
|
126
|
+
session: {
|
|
127
|
+
sessionModeRules: { stateful: ['claude-code'], stateless: ['cherrystudio'] },
|
|
128
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const request = makeRequest({ 'user-agent': 'SomeUnknownClient/1.0' });
|
|
133
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATEFUL);
|
|
134
|
+
});
|
|
135
|
+
test('empty UA falls back to defaultSessionMode', () => {
|
|
136
|
+
mockGetConfig.mockReturnValue({
|
|
137
|
+
system: {
|
|
138
|
+
session: {
|
|
139
|
+
sessionModeRules: { stateful: ['claude-code'], stateless: [] },
|
|
140
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
const request = makeRequest({});
|
|
145
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATEFUL);
|
|
146
|
+
});
|
|
147
|
+
test('no gateway config falls back to stateful (hardcoded default)', () => {
|
|
148
|
+
mockGetConfig.mockReturnValue({
|
|
149
|
+
system: {}
|
|
150
|
+
});
|
|
151
|
+
const request = makeRequest({ 'user-agent': 'SomeClient/1.0' });
|
|
152
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATEFUL);
|
|
153
|
+
});
|
|
154
|
+
test('no config at all falls back to stateful', () => {
|
|
155
|
+
mockGetConfig.mockReturnValue(null);
|
|
156
|
+
const request = makeRequest({ 'user-agent': 'SomeClient/1.0' });
|
|
157
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATEFUL);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('empty rules arrays', () => {
|
|
161
|
+
test('empty stateful and stateless arrays use default', () => {
|
|
162
|
+
mockGetConfig.mockReturnValue({
|
|
163
|
+
system: {
|
|
164
|
+
session: {
|
|
165
|
+
sessionModeRules: { stateful: [], stateless: [] },
|
|
166
|
+
defaultSessionMode: SESSION_MODE_STATELESS
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
const request = makeRequest({ 'user-agent': 'SomeClient/1.0' });
|
|
171
|
+
expect(resolveSessionMode(request)).toBe(SESSION_MODE_STATELESS);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
2
|
import { HubManagerService } from '../../../src/services/hub-manager.service.js';
|
|
3
|
-
import { mcpConnectionManager } from '../../../src/services/
|
|
3
|
+
import { mcpConnectionManager } from '../../../src/services/connection/index.js';
|
|
4
4
|
import { configManager } from '../../../src/config/config-manager.js';
|
|
5
5
|
// Mock resolveInstanceConfig to return a valid resolved config
|
|
6
6
|
vi.mock('@config/config-migrator.js', () => ({
|
|
7
|
-
resolveInstanceConfig: vi.fn()
|
|
8
|
-
getEnabledInstances: vi.fn()
|
|
7
|
+
resolveInstanceConfig: vi.fn()
|
|
9
8
|
}));
|
|
10
9
|
// Mock dependencies
|
|
11
10
|
vi.mock('@config/config-manager.js', () => ({
|
|
@@ -23,13 +22,13 @@ vi.mock('@config/config-manager.js', () => ({
|
|
|
23
22
|
removeServerInstance: vi.fn()
|
|
24
23
|
}
|
|
25
24
|
}));
|
|
26
|
-
vi.mock('@services/
|
|
25
|
+
vi.mock('@services/connection/index.js', () => ({
|
|
27
26
|
mcpConnectionManager: {
|
|
28
27
|
connect: vi.fn(),
|
|
29
28
|
disconnect: vi.fn(() => Promise.resolve())
|
|
30
29
|
}
|
|
31
30
|
}));
|
|
32
|
-
vi.mock('@utils/logger.js', () => ({
|
|
31
|
+
vi.mock('@utils/logger/index.js', () => ({
|
|
33
32
|
logger: {
|
|
34
33
|
info: vi.fn(),
|
|
35
34
|
error: vi.fn(),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import { HubToolsService } from '../../../src/services/hub-tools.service.js';
|
|
3
3
|
import { hubManager } from '../../../src/services/hub-manager.service.js';
|
|
4
|
-
import { mcpConnectionManager } from '../../../src/services/
|
|
4
|
+
import { mcpConnectionManager } from '../../../src/services/connection/index.js';
|
|
5
5
|
// Mock dependencies
|
|
6
6
|
vi.mock('@services/hub-manager.service.js');
|
|
7
|
-
vi.mock('@services/
|
|
7
|
+
vi.mock('@services/connection/index.js');
|
|
8
8
|
describe('HubToolsService', () => {
|
|
9
9
|
let hubToolsService;
|
|
10
10
|
beforeEach(() => {
|
|
@@ -320,6 +320,82 @@ describe('HubToolsService', () => {
|
|
|
320
320
|
});
|
|
321
321
|
expect(servers).not.toHaveProperty('Disconnected Server');
|
|
322
322
|
});
|
|
323
|
+
it('should include tag-match-unique servers with multiple connected instances', async () => {
|
|
324
|
+
// Arrange: 4-instance server with tag-match-unique strategy, all connected
|
|
325
|
+
const mockServers = [
|
|
326
|
+
{
|
|
327
|
+
name: 'multi-instance-server',
|
|
328
|
+
config: {
|
|
329
|
+
template: {
|
|
330
|
+
type: 'stdio',
|
|
331
|
+
command: 'test-command',
|
|
332
|
+
args: [],
|
|
333
|
+
env: {},
|
|
334
|
+
headers: {},
|
|
335
|
+
aggregatedTools: [],
|
|
336
|
+
timeout: 30000,
|
|
337
|
+
description: 'Multi-instance test server',
|
|
338
|
+
tags: {}
|
|
339
|
+
},
|
|
340
|
+
instances: [
|
|
341
|
+
{
|
|
342
|
+
id: 'inst-0',
|
|
343
|
+
index: 0,
|
|
344
|
+
enabled: false,
|
|
345
|
+
args: [],
|
|
346
|
+
env: {},
|
|
347
|
+
headers: {},
|
|
348
|
+
tags: { Env: 'dev' }
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
id: 'inst-1',
|
|
352
|
+
index: 1,
|
|
353
|
+
enabled: false,
|
|
354
|
+
args: [],
|
|
355
|
+
env: {},
|
|
356
|
+
headers: {},
|
|
357
|
+
tags: { Env: 'test' }
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
id: 'inst-2',
|
|
361
|
+
index: 2,
|
|
362
|
+
enabled: false,
|
|
363
|
+
args: [],
|
|
364
|
+
env: {},
|
|
365
|
+
headers: {},
|
|
366
|
+
tags: { Env: 'prod' }
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
id: 'inst-3',
|
|
370
|
+
index: 3,
|
|
371
|
+
enabled: false,
|
|
372
|
+
args: [],
|
|
373
|
+
env: {},
|
|
374
|
+
headers: {},
|
|
375
|
+
tags: { Env: 'staging' }
|
|
376
|
+
}
|
|
377
|
+
],
|
|
378
|
+
tagDefinitions: [],
|
|
379
|
+
instanceSelectionStrategy: 'tag-match-unique'
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
];
|
|
383
|
+
vi.mocked(hubManager.getAllServers).mockReturnValue(mockServers);
|
|
384
|
+
vi.mocked(hubManager.getServerInstancesByName).mockReturnValue(mockServers[0].config.instances);
|
|
385
|
+
vi.mocked(hubManager.getServerByName).mockReturnValue(mockServers[0].config);
|
|
386
|
+
vi.mocked(mcpConnectionManager.getConnectedIndexes).mockReturnValue([0, 1, 2, 3]);
|
|
387
|
+
vi.mocked(mcpConnectionManager.getStatus).mockReturnValue({
|
|
388
|
+
connected: true,
|
|
389
|
+
lastCheck: Date.now(),
|
|
390
|
+
toolsCount: 1,
|
|
391
|
+
resourcesCount: 0
|
|
392
|
+
});
|
|
393
|
+
// Act
|
|
394
|
+
const servers = await hubToolsService.listServers();
|
|
395
|
+
// Assert: tag-match-unique server with multiple connected instances must be included
|
|
396
|
+
expect(servers).toHaveProperty('multi-instance-server');
|
|
397
|
+
expect(servers['multi-instance-server']).toBe('Multi-instance test server');
|
|
398
|
+
});
|
|
323
399
|
});
|
|
324
400
|
describe('listToolsInServer', () => {
|
|
325
401
|
it('should return tool summaries from a specific server (without inputSchema)', async () => {
|
|
@@ -700,10 +776,10 @@ describe('HubToolsService', () => {
|
|
|
700
776
|
const result = await hubToolsService.readResource('hub://use-guide');
|
|
701
777
|
// Assert
|
|
702
778
|
expect(typeof result).toBe('string');
|
|
703
|
-
expect(result).toContain('# MCP Hub Lite
|
|
704
|
-
expect(result).toContain('
|
|
705
|
-
expect(result).toContain('
|
|
706
|
-
expect(result).toContain('
|
|
779
|
+
expect(result).toContain('# MCP Hub Lite');
|
|
780
|
+
expect(result).toContain('快速上手');
|
|
781
|
+
expect(result).toContain('渐进式发现工作流');
|
|
782
|
+
expect(result).toContain('系统工具参考');
|
|
707
783
|
});
|
|
708
784
|
it('should throw error for invalid URI format', async () => {
|
|
709
785
|
// Act & Assert
|
|
@@ -81,7 +81,8 @@ describe('ConfigManager', () => {
|
|
|
81
81
|
jsonPretty: true,
|
|
82
82
|
mcpCommDebug: false,
|
|
83
83
|
apiDebug: false,
|
|
84
|
-
gatewayDebug: false
|
|
84
|
+
gatewayDebug: false,
|
|
85
|
+
showTraceContext: true
|
|
85
86
|
}
|
|
86
87
|
},
|
|
87
88
|
security: {
|
|
@@ -151,7 +152,8 @@ describe('ConfigManager', () => {
|
|
|
151
152
|
jsonPretty: true,
|
|
152
153
|
mcpCommDebug: false,
|
|
153
154
|
apiDebug: false,
|
|
154
|
-
gatewayDebug: false
|
|
155
|
+
gatewayDebug: false,
|
|
156
|
+
showTraceContext: true
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
});
|
|
@@ -196,7 +198,8 @@ describe('ConfigManager', () => {
|
|
|
196
198
|
jsonPretty: true,
|
|
197
199
|
mcpCommDebug: false,
|
|
198
200
|
apiDebug: false,
|
|
199
|
-
gatewayDebug: false
|
|
201
|
+
gatewayDebug: false,
|
|
202
|
+
showTraceContext: true
|
|
200
203
|
}
|
|
201
204
|
}
|
|
202
205
|
});
|
|
@@ -239,7 +242,8 @@ describe('ConfigManager', () => {
|
|
|
239
242
|
jsonPretty: true,
|
|
240
243
|
mcpCommDebug: false,
|
|
241
244
|
apiDebug: false,
|
|
242
|
-
gatewayDebug: false
|
|
245
|
+
gatewayDebug: false,
|
|
246
|
+
showTraceContext: true
|
|
243
247
|
}
|
|
244
248
|
}
|
|
245
249
|
};
|
|
@@ -409,7 +413,8 @@ describe('ConfigManager', () => {
|
|
|
409
413
|
jsonPretty: true,
|
|
410
414
|
mcpCommDebug: false,
|
|
411
415
|
apiDebug: false,
|
|
412
|
-
gatewayDebug: false
|
|
416
|
+
gatewayDebug: false,
|
|
417
|
+
showTraceContext: true
|
|
413
418
|
}
|
|
414
419
|
},
|
|
415
420
|
security: {
|
|
@@ -463,7 +468,8 @@ describe('ConfigManager', () => {
|
|
|
463
468
|
jsonPretty: true,
|
|
464
469
|
mcpCommDebug: false,
|
|
465
470
|
apiDebug: false,
|
|
466
|
-
gatewayDebug: false
|
|
471
|
+
gatewayDebug: false,
|
|
472
|
+
showTraceContext: true
|
|
467
473
|
}
|
|
468
474
|
}
|
|
469
475
|
});
|
|
@@ -481,7 +487,8 @@ describe('ConfigManager', () => {
|
|
|
481
487
|
jsonPretty: true,
|
|
482
488
|
mcpCommDebug: false,
|
|
483
489
|
apiDebug: false,
|
|
484
|
-
gatewayDebug: false
|
|
490
|
+
gatewayDebug: false,
|
|
491
|
+
showTraceContext: true
|
|
485
492
|
}
|
|
486
493
|
},
|
|
487
494
|
security: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-output.test.d.ts","sourceRoot":"","sources":["../../../../../tests/unit/utils/log-output.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { hasDataUriImage, simplifyDataUriImages, simplifyImageContent, isToolsListResponse, formatMcpMessageForLogging } from '../../../src/utils/logger/log-output.js';
|
|
3
|
+
import { setJsonPrettyConfigGetter, setDevModeEnabled } from '../../../src/utils/json-utils.js';
|
|
4
|
+
describe('hasDataUriImage', () => {
|
|
5
|
+
it('should detect data:image/png;base64 in JSON', () => {
|
|
6
|
+
const json = JSON.stringify({
|
|
7
|
+
icons: [{ src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg==' }]
|
|
8
|
+
});
|
|
9
|
+
expect(hasDataUriImage(json)).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
it('should detect data:image/jpeg;base64', () => {
|
|
12
|
+
const json = JSON.stringify({
|
|
13
|
+
icon: 'data:image/jpeg;base64,/9j/4AAQSkZJRg=='
|
|
14
|
+
});
|
|
15
|
+
expect(hasDataUriImage(json)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it('should detect data:image/svg+xml;base64', () => {
|
|
18
|
+
const json = JSON.stringify({
|
|
19
|
+
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0i'
|
|
20
|
+
});
|
|
21
|
+
expect(hasDataUriImage(json)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('should return false for JSON without data:image', () => {
|
|
24
|
+
const json = JSON.stringify({ name: 'test', value: 123 });
|
|
25
|
+
expect(hasDataUriImage(json)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it('should return false for non-base64 data URIs', () => {
|
|
28
|
+
const json = JSON.stringify({ icon: 'https://example.com/icon.png' });
|
|
29
|
+
expect(hasDataUriImage(json)).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('simplifyDataUriImages', () => {
|
|
33
|
+
it('should replace base64 payload with [Truncated]', () => {
|
|
34
|
+
const input = JSON.stringify({
|
|
35
|
+
icons: [{ src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg==' }]
|
|
36
|
+
});
|
|
37
|
+
const result = simplifyDataUriImages(input);
|
|
38
|
+
expect(result).toContain('data:image/png;base64,[Truncated]');
|
|
39
|
+
expect(result).not.toContain('iVBORw0KGgoAAAANSUhEUg==');
|
|
40
|
+
});
|
|
41
|
+
it('should handle multiple data URIs in one JSON', () => {
|
|
42
|
+
const input = JSON.stringify({
|
|
43
|
+
icons: [{ src: 'data:image/png;base64,AAAA' }, { src: 'data:image/png;base64,BBBB' }]
|
|
44
|
+
});
|
|
45
|
+
const result = simplifyDataUriImages(input);
|
|
46
|
+
expect(result).toContain('data:image/png;base64,[Truncated]');
|
|
47
|
+
expect(result).not.toContain('AAAA');
|
|
48
|
+
expect(result).not.toContain('BBBB');
|
|
49
|
+
});
|
|
50
|
+
it('should leave non-data-URI strings unchanged', () => {
|
|
51
|
+
const input = JSON.stringify({ name: 'test', url: 'https://example.com/img.png' });
|
|
52
|
+
const result = simplifyDataUriImages(input);
|
|
53
|
+
expect(result).toBe(input);
|
|
54
|
+
});
|
|
55
|
+
it('should produce valid JSON after simplification', () => {
|
|
56
|
+
const input = JSON.stringify({
|
|
57
|
+
icons: [{ src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg==' }]
|
|
58
|
+
});
|
|
59
|
+
const result = simplifyDataUriImages(input);
|
|
60
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('simplifyImageContent', () => {
|
|
64
|
+
it('should replace MCP image content data with [Truncated]', () => {
|
|
65
|
+
const message = {
|
|
66
|
+
jsonrpc: '2.0',
|
|
67
|
+
id: 1,
|
|
68
|
+
result: {
|
|
69
|
+
content: [{ type: 'image', data: 'base64payload', mimeType: 'image/png' }]
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const json = JSON.stringify(message);
|
|
73
|
+
const result = simplifyImageContent(json);
|
|
74
|
+
expect(result).toContain('"[Truncated]"');
|
|
75
|
+
expect(result).not.toContain('base64payload');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('isToolsListResponse', () => {
|
|
79
|
+
it('should return true for tools/list response', () => {
|
|
80
|
+
const json = JSON.stringify({
|
|
81
|
+
jsonrpc: '2.0',
|
|
82
|
+
id: 1,
|
|
83
|
+
result: { tools: [{ name: 'tool1' }] }
|
|
84
|
+
});
|
|
85
|
+
expect(isToolsListResponse(json)).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it('should return true for resources/list response', () => {
|
|
88
|
+
const json = JSON.stringify({
|
|
89
|
+
jsonrpc: '2.0',
|
|
90
|
+
id: 1,
|
|
91
|
+
result: { resources: [{ uri: 'res1' }] }
|
|
92
|
+
});
|
|
93
|
+
expect(isToolsListResponse(json)).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it('should return false for initialize response with capabilities (not result.tools)', () => {
|
|
96
|
+
const json = JSON.stringify({
|
|
97
|
+
jsonrpc: '2.0',
|
|
98
|
+
id: 0,
|
|
99
|
+
result: {
|
|
100
|
+
capabilities: { tools: {}, resources: {} },
|
|
101
|
+
serverInfo: { name: 'test', version: '1.0' }
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
expect(isToolsListResponse(json)).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe('formatMcpMessageForLogging', () => {
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
setJsonPrettyConfigGetter(null);
|
|
110
|
+
setDevModeEnabled(false);
|
|
111
|
+
});
|
|
112
|
+
it('should truncate data:image URIs in initialize response with icons', () => {
|
|
113
|
+
const message = {
|
|
114
|
+
jsonrpc: '2.0',
|
|
115
|
+
id: 0,
|
|
116
|
+
result: {
|
|
117
|
+
capabilities: {
|
|
118
|
+
tools: {},
|
|
119
|
+
resources: {}
|
|
120
|
+
},
|
|
121
|
+
protocolVersion: '2025-11-25',
|
|
122
|
+
serverInfo: {
|
|
123
|
+
name: 'github-mcp-server',
|
|
124
|
+
title: 'GitHub MCP Server',
|
|
125
|
+
version: 'github-mcp-server/remote-abc123',
|
|
126
|
+
icons: [
|
|
127
|
+
{
|
|
128
|
+
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAADK0lEQVR',
|
|
129
|
+
mimeType: 'image/png',
|
|
130
|
+
theme: 'light'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAB8ElEQVR',
|
|
134
|
+
mimeType: 'image/png',
|
|
135
|
+
theme: 'dark'
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const result = formatMcpMessageForLogging(message);
|
|
142
|
+
// Must contain [Truncated] placeholder
|
|
143
|
+
expect(result).toContain('[Truncated]');
|
|
144
|
+
// Must NOT contain the raw base64 payload
|
|
145
|
+
expect(result).not.toContain('iVBORw0KGgoAAAANSUhEUg');
|
|
146
|
+
// Must still contain server info
|
|
147
|
+
expect(result).toContain('github-mcp-server');
|
|
148
|
+
});
|
|
149
|
+
it('should handle initialize response without icons normally', () => {
|
|
150
|
+
const message = {
|
|
151
|
+
jsonrpc: '2.0',
|
|
152
|
+
id: 0,
|
|
153
|
+
result: {
|
|
154
|
+
capabilities: { tools: {} },
|
|
155
|
+
serverInfo: { name: 'test-server', version: '1.0' }
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const result = formatMcpMessageForLogging(message);
|
|
159
|
+
// Should be valid JSON (since isToolsListResponse returns null for empty capabilities,
|
|
160
|
+
// but now hasDataUriImage should be checked first and return false, then
|
|
161
|
+
// isToolsListResponse returns true and returns null, falling to stringifyForLogging)
|
|
162
|
+
expect(result).toContain('test-server');
|
|
163
|
+
});
|
|
164
|
+
it('should simplify tools/list response to count', () => {
|
|
165
|
+
const message = {
|
|
166
|
+
jsonrpc: '2.0',
|
|
167
|
+
id: 1,
|
|
168
|
+
result: { tools: [{ name: 't1' }, { name: 't2' }, { name: 't3' }] }
|
|
169
|
+
};
|
|
170
|
+
const result = formatMcpMessageForLogging(message);
|
|
171
|
+
expect(result).toBe('Returned 3 tools');
|
|
172
|
+
});
|
|
173
|
+
it('should simplify resources/list response to count', () => {
|
|
174
|
+
const message = {
|
|
175
|
+
jsonrpc: '2.0',
|
|
176
|
+
id: 1,
|
|
177
|
+
result: { resources: [{ uri: 'r1' }, { uri: 'r2' }] }
|
|
178
|
+
};
|
|
179
|
+
const result = formatMcpMessageForLogging(message);
|
|
180
|
+
expect(result).toBe('Returned 2 resources');
|
|
181
|
+
});
|
|
182
|
+
it('should not simplify capabilities response (output full JSON)', () => {
|
|
183
|
+
const message = {
|
|
184
|
+
jsonrpc: '2.0',
|
|
185
|
+
id: 1,
|
|
186
|
+
result: {
|
|
187
|
+
capabilities: {
|
|
188
|
+
tools: { list: {}, call: {} },
|
|
189
|
+
resources: { list: {} }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const result = formatMcpMessageForLogging(message);
|
|
194
|
+
// Capabilities are no longer simplified; expect full JSON output
|
|
195
|
+
expect(result).toContain('capabilities');
|
|
196
|
+
expect(result).toContain('list');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -79,21 +79,7 @@ describe('LogRotator', () => {
|
|
|
79
79
|
const latest = logRotator.getLatestLogFilePath();
|
|
80
80
|
expect(latest).toBeNull();
|
|
81
81
|
});
|
|
82
|
-
it('should
|
|
83
|
-
logRotator = new LogRotator(tempLogDir, 'mcp-hub', undefined, () => originalConfig);
|
|
84
|
-
// When no files exist, getCurrentLogFilePath will create a new path (but not the file)
|
|
85
|
-
const path1 = logRotator.getCurrentLogFilePath();
|
|
86
|
-
expect(path.basename(path1)).toMatch(/^mcp-hub\.\d{8}_\d{9}\.log$/);
|
|
87
|
-
// Actually create the file on disk
|
|
88
|
-
fs.writeFileSync(path1, 'test content 1');
|
|
89
|
-
// Create an older file
|
|
90
|
-
const oldFile = path.join(tempLogDir, 'mcp-hub.20260301_100000000.log');
|
|
91
|
-
fs.writeFileSync(oldFile, 'test content 2');
|
|
92
|
-
// When files exist, getCurrentLogFilePath should return the latest one
|
|
93
|
-
const path2 = logRotator.getCurrentLogFilePath();
|
|
94
|
-
expect(path2).toBe(path1); // Should return the newer one we just created
|
|
95
|
-
});
|
|
96
|
-
it('should get current log file path with custom base name', () => {
|
|
82
|
+
it('should create new log file path with custom base name', () => {
|
|
97
83
|
logRotator = new LogRotator(tempLogDir, 'custom-log', undefined, () => originalConfig);
|
|
98
84
|
const currentPath = logRotator.createNewLogFilePath();
|
|
99
85
|
const basename = path.basename(currentPath);
|
|
@@ -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 } from '../../../src/utils/logger.js';
|
|
5
|
+
import { Logger } from '../../../src/utils/logger/index.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', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../../vitest.config.ts"],"names":[],"mappings":";AAUA,
|
|
1
|
+
{"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../../vitest.config.ts"],"names":[],"mappings":";AAUA,wBA4CG"}
|
|
@@ -44,8 +44,6 @@ export default defineConfig({
|
|
|
44
44
|
'tests/unit/**/*.test.ts',
|
|
45
45
|
'tests/integration/**/*.test.ts',
|
|
46
46
|
'tests/contract/**/*.test.ts',
|
|
47
|
-
'tests/evaluation/**/*.test.ts',
|
|
48
|
-
'src/**/*.test.ts',
|
|
49
47
|
'tests/temp/**/*.test.ts'
|
|
50
48
|
],
|
|
51
49
|
exclude: ['tests/unit/frontend/**/*']
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loop_ouroboros/mcp-hub-lite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "A lightweight MCP management platform designed for independent developers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "loop_ouroboros",
|
|
@@ -68,7 +68,6 @@
|
|
|
68
68
|
"element-plus": "^2.13.0",
|
|
69
69
|
"eventsource": "^4.1.0",
|
|
70
70
|
"fastify": "^5.6.2",
|
|
71
|
-
"open": "^11.0.0",
|
|
72
71
|
"pinia": "^3.0.4",
|
|
73
72
|
"undici": "^8.1.0",
|
|
74
73
|
"vue": "^3.5.26",
|
|
@@ -96,7 +95,6 @@
|
|
|
96
95
|
"npm-run-all": "^4.1.5",
|
|
97
96
|
"postcss": "^8.5.6",
|
|
98
97
|
"prettier": "^3.7.4",
|
|
99
|
-
"rimraf": "^6.1.2",
|
|
100
98
|
"tailwindcss": "^3.4.19",
|
|
101
99
|
"tsc-alias": "^1.8.16",
|
|
102
100
|
"tsx": "^4.21.0",
|