@loop_ouroboros/mcp-hub-lite 1.0.2 → 1.1.0
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 +32 -0
- package/dist/client/assets/{HomeView-7rMg9C6t.js → HomeView-Bu2joUvW.js} +1 -1
- package/dist/client/assets/ResourceDetailView-BvrhDCD1.js +1 -0
- package/dist/client/assets/ResourceDetailView-DUJZbegl.css +1 -0
- package/dist/client/assets/ResourcesView-Cc8RHtia.css +1 -0
- package/dist/client/assets/ResourcesView-LjqioF_s.js +1 -0
- package/dist/client/assets/ServerDashboard-BfLeFDGw.css +1 -0
- package/dist/client/assets/ServerDashboard-FhHJFvUi.js +1 -0
- package/dist/client/assets/ServerDetail-BKV-M4qT.js +2 -0
- package/dist/client/assets/ServerDetail-CtnNKJGx.css +1 -0
- package/dist/client/assets/{ServerListView-Bws09jNR.css → ServerListView-B-bPljsO.css} +1 -1
- package/dist/client/assets/ServerListView-BXgtDyt3.js +36 -0
- package/dist/client/assets/ServerStatusTags.vue_vue_type_script_setup_true_lang-D-ooYNdN.js +1 -0
- package/dist/client/assets/SettingsView-CMFG91Z4.js +1 -0
- package/dist/client/assets/SettingsView-GkBOKL0V.css +1 -0
- package/dist/client/assets/ToolCallDialog-Bf4Xe4gH.js +1 -0
- package/dist/client/assets/ToolsView-DFpha1z0.js +1 -0
- package/dist/client/assets/{_baseClone-5q1b0P3O.js → _baseClone-Bp9Rjwd7.js} +1 -1
- package/dist/client/assets/el-form-item-B4LbJ6OO.css +1 -0
- package/dist/client/assets/el-form-item-DdSUWYsl.js +12 -0
- package/dist/client/assets/el-input-99gMrutP.js +1 -0
- package/dist/client/assets/el-input-BH4BZKnG.css +1 -0
- package/dist/client/assets/{el-loading-H85n3BUC.js → el-loading-CIQ5pD5u.js} +1 -1
- package/dist/client/assets/el-overlay-BVM6msGX.js +1 -0
- package/dist/client/assets/{el-select-C0U_l4IZ.css → el-overlay-CBvdpA69.css} +1 -1
- package/dist/client/assets/{ResourceDetailView-BdOaL_-o.css → el-radio-group-B0bauIRR.css} +1 -1
- package/dist/client/assets/el-radio-group-DhXWy7ry.js +1 -0
- package/dist/client/assets/el-skeleton-item-BLY1jEuR.css +1 -0
- package/dist/client/assets/el-skeleton-item-DJz-Us12.js +1 -0
- package/dist/client/assets/el-switch-BBrS-_6y.css +1 -0
- package/dist/client/assets/el-switch-Bu8AQ5uM.js +1 -0
- package/dist/client/assets/el-tab-pane-BnGMaV56.js +1 -0
- package/dist/client/assets/el-table-column-BMWOaLS_.js +1 -0
- package/dist/client/assets/el-table-column-BdvRS9Y2.css +1 -0
- package/dist/client/assets/index-C2V-ZGji.js +1 -0
- package/dist/client/assets/{index-BsDWtoIl.css → index-DpH6ZSbs.css} +1 -1
- package/dist/client/assets/index-vhkqgpmN.js +2 -0
- package/dist/client/assets/{omit-DPsOVNIJ.js → omit-CqPQN3XP.js} +1 -1
- package/dist/client/assets/{raf-DY5mgbuB.js → raf-C2wXzaVU.js} +1 -1
- package/dist/client/assets/{vue-vendor-6ny5zj9i.js → vue-vendor-BLHLXXJK.js} +1 -1
- package/dist/client/index.html +3 -3
- package/dist/server/shared/models/resource.model.d.ts +2 -1
- package/dist/server/shared/models/resource.model.d.ts.map +1 -1
- package/dist/server/shared/models/server.model.d.ts +362 -5
- package/dist/server/shared/models/server.model.d.ts.map +1 -1
- package/dist/server/shared/models/server.model.js +220 -1
- package/dist/server/shared/models/session.model.d.ts +1 -57
- package/dist/server/shared/models/session.model.d.ts.map +1 -1
- package/dist/server/shared/models/session.model.js +1 -55
- package/dist/server/shared/models/tool.model.d.ts +4 -2
- package/dist/server/shared/models/tool.model.d.ts.map +1 -1
- package/dist/server/shared/types/session-context.types.d.ts +0 -2
- package/dist/server/shared/types/session-context.types.d.ts.map +1 -1
- package/dist/server/shared/types/websocket.types.d.ts +17 -10
- package/dist/server/shared/types/websocket.types.d.ts.map +1 -1
- package/dist/server/src/api/mcp/gateway.d.ts +1 -3
- package/dist/server/src/api/mcp/gateway.d.ts.map +1 -1
- package/dist/server/src/api/mcp/gateway.js +19 -50
- package/dist/server/src/api/mcp/session-context-extractor.d.ts.map +1 -1
- package/dist/server/src/api/mcp/session-context-extractor.js +5 -14
- package/dist/server/src/api/web/hub-tools.d.ts +2 -2
- package/dist/server/src/api/web/hub-tools.d.ts.map +1 -1
- package/dist/server/src/api/web/hub-tools.js +3 -54
- package/dist/server/src/api/web/mcp-status.d.ts +1 -1
- package/dist/server/src/api/web/mcp-status.d.ts.map +1 -1
- package/dist/server/src/api/web/mcp-status.js +71 -30
- package/dist/server/src/api/web/resources.d.ts.map +1 -1
- package/dist/server/src/api/web/resources.js +28 -22
- 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 +24 -45
- package/dist/server/src/api/web/servers.d.ts +3 -3
- package/dist/server/src/api/web/servers.d.ts.map +1 -1
- package/dist/server/src/api/web/servers.js +70 -25
- package/dist/server/src/api/ws/ws-handler.d.ts.map +1 -1
- package/dist/server/src/api/ws/ws-handler.js +4 -2
- package/dist/server/src/app.d.ts.map +1 -1
- package/dist/server/src/app.js +0 -2
- package/dist/server/src/cli/commands/list.js +2 -2
- package/dist/server/src/cli/commands/status.d.ts.map +1 -1
- package/dist/server/src/cli/commands/status.js +41 -30
- package/dist/server/src/cli/index.d.ts.map +1 -1
- package/dist/server/src/cli/index.js +2 -1
- package/dist/server/src/cli/server.d.ts +11 -2
- package/dist/server/src/cli/server.d.ts.map +1 -1
- package/dist/server/src/config/config-change-logger.d.ts +22 -0
- package/dist/server/src/config/config-change-logger.d.ts.map +1 -1
- package/dist/server/src/config/config-change-logger.js +135 -11
- package/dist/server/src/config/config-loader.d.ts +6 -5
- package/dist/server/src/config/config-loader.d.ts.map +1 -1
- package/dist/server/src/config/config-loader.js +54 -16
- package/dist/server/src/config/config-manager.d.ts +51 -43
- package/dist/server/src/config/config-manager.d.ts.map +1 -1
- package/dist/server/src/config/config-manager.js +84 -66
- package/dist/server/src/config/config-migrator.d.ts +82 -0
- package/dist/server/src/config/config-migrator.d.ts.map +1 -0
- package/dist/server/src/config/config-migrator.js +348 -0
- package/dist/server/src/config/config-saver.d.ts +2 -0
- package/dist/server/src/config/config-saver.d.ts.map +1 -1
- package/dist/server/src/config/config-saver.js +8 -2
- package/dist/server/src/config/config.schema.d.ts +10 -104
- package/dist/server/src/config/config.schema.d.ts.map +1 -1
- package/dist/server/src/config/config.schema.js +15 -99
- package/dist/server/src/config/path-validator.d.ts +27 -0
- package/dist/server/src/config/path-validator.d.ts.map +1 -0
- package/dist/server/src/config/path-validator.js +53 -0
- package/dist/server/src/config/server-config-manager.d.ts +37 -31
- package/dist/server/src/config/server-config-manager.d.ts.map +1 -1
- package/dist/server/src/config/server-config-manager.js +222 -66
- package/dist/server/src/config/type-converter.d.ts.map +1 -1
- package/dist/server/src/config/type-converter.js +3 -2
- package/dist/server/src/models/event.model.d.ts +17 -10
- package/dist/server/src/models/event.model.d.ts.map +1 -1
- package/dist/server/src/models/server.model.d.ts +17 -3
- package/dist/server/src/models/server.model.d.ts.map +1 -1
- package/dist/server/src/models/server.model.js +2 -1
- package/dist/server/src/models/system-tools.constants.d.ts +10 -27
- package/dist/server/src/models/system-tools.constants.d.ts.map +1 -1
- package/dist/server/src/models/system-tools.constants.js +4 -8
- package/dist/server/src/pid/manager.d.ts.map +1 -1
- package/dist/server/src/pid/manager.js +2 -1
- package/dist/server/src/server/dev-server.js +34 -20
- package/dist/server/src/server/runner.d.ts.map +1 -1
- package/dist/server/src/server/runner.js +41 -26
- package/dist/server/src/services/connection/connection-manager.d.ts +85 -103
- package/dist/server/src/services/connection/connection-manager.d.ts.map +1 -1
- package/dist/server/src/services/connection/connection-manager.js +286 -241
- package/dist/server/src/services/connection/tool-cache.d.ts +27 -25
- package/dist/server/src/services/connection/tool-cache.d.ts.map +1 -1
- package/dist/server/src/services/connection/tool-cache.js +50 -55
- package/dist/server/src/services/gateway/gateway.service.d.ts +2 -0
- package/dist/server/src/services/gateway/gateway.service.d.ts.map +1 -1
- package/dist/server/src/services/gateway/gateway.service.js +15 -19
- package/dist/server/src/services/gateway/global-transport.d.ts +10 -0
- package/dist/server/src/services/gateway/global-transport.d.ts.map +1 -0
- package/dist/server/src/services/gateway/global-transport.js +42 -0
- 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 +67 -65
- package/dist/server/src/services/gateway/request-handlers/index.d.ts +1 -1
- package/dist/server/src/services/gateway/request-handlers/index.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/index.js +1 -1
- package/dist/server/src/services/gateway/request-handlers/initialize-handler.d.ts +1 -4
- 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 +10 -81
- package/dist/server/src/services/gateway/request-handlers/initialize.constants.d.ts +35 -0
- package/dist/server/src/services/gateway/request-handlers/initialize.constants.d.ts.map +1 -0
- package/dist/server/src/services/gateway/request-handlers/initialize.constants.js +44 -0
- 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 +5 -4
- 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 +32 -77
- package/dist/server/src/services/gateway/request-handlers/tools-handler.d.ts.map +1 -1
- package/dist/server/src/services/gateway/request-handlers/tools-handler.js +4 -3
- package/dist/server/src/services/gateway/tool-list-generator.d.ts.map +1 -1
- package/dist/server/src/services/gateway/tool-list-generator.js +37 -16
- package/dist/server/src/services/gateway/types.d.ts +2 -1
- package/dist/server/src/services/gateway/types.d.ts.map +1 -1
- package/dist/server/src/services/hub-manager.service.d.ts +32 -238
- package/dist/server/src/services/hub-manager.service.d.ts.map +1 -1
- package/dist/server/src/services/hub-manager.service.js +89 -267
- package/dist/server/src/services/hub-tools/index.d.ts +1 -3
- package/dist/server/src/services/hub-tools/index.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/index.js +1 -2
- package/dist/server/src/services/hub-tools/instance-matcher.d.ts +62 -0
- package/dist/server/src/services/hub-tools/instance-matcher.d.ts.map +1 -0
- package/dist/server/src/services/hub-tools/instance-matcher.js +132 -0
- package/dist/server/src/services/hub-tools/instance-selector.d.ts +29 -0
- package/dist/server/src/services/hub-tools/instance-selector.d.ts.map +1 -0
- package/dist/server/src/services/hub-tools/instance-selector.js +103 -0
- package/dist/server/src/services/hub-tools/resource-generator.d.ts +24 -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 +259 -39
- package/dist/server/src/services/hub-tools/server-selector.d.ts +26 -13
- package/dist/server/src/services/hub-tools/server-selector.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/server-selector.js +44 -37
- package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts +1 -4
- 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 +17 -80
- package/dist/server/src/services/hub-tools/tool-search.d.ts +7 -7
- package/dist/server/src/services/hub-tools/tool-search.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools/tool-search.js +10 -4
- package/dist/server/src/services/hub-tools/types.d.ts +2 -2
- package/dist/server/src/services/hub-tools/types.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools.service.d.ts +43 -72
- package/dist/server/src/services/hub-tools.service.d.ts.map +1 -1
- package/dist/server/src/services/hub-tools.service.js +185 -110
- package/dist/server/src/services/search/search-core.service.d.ts +5 -5
- package/dist/server/src/services/search/search-core.service.js +11 -11
- package/dist/server/src/services/session/session-manager.d.ts +12 -256
- package/dist/server/src/services/session/session-manager.d.ts.map +1 -1
- package/dist/server/src/services/session/session-manager.js +23 -585
- package/dist/server/src/services/session-tracker.service.d.ts +2 -10
- package/dist/server/src/services/session-tracker.service.d.ts.map +1 -1
- package/dist/server/src/services/session-tracker.service.js +2 -53
- package/dist/server/src/services/system-tool-handler.d.ts.map +1 -1
- package/dist/server/src/services/system-tool-handler.js +7 -17
- package/dist/server/src/utils/composite-key.d.ts +29 -0
- package/dist/server/src/utils/composite-key.d.ts.map +1 -0
- package/dist/server/src/utils/composite-key.js +39 -0
- package/dist/server/src/utils/error-handler.d.ts.map +1 -1
- package/dist/server/src/utils/error-handler.js +3 -2
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/src/utils/index.js +2 -0
- package/dist/server/src/utils/instance-id.d.ts +22 -0
- package/dist/server/src/utils/instance-id.d.ts.map +1 -0
- package/dist/server/src/utils/instance-id.js +59 -0
- package/dist/server/src/utils/json-utils.d.ts +4 -4
- package/dist/server/src/utils/json-utils.d.ts.map +1 -1
- package/dist/server/src/utils/json-utils.js +4 -4
- package/dist/server/src/utils/logger/dev-logger.d.ts +2 -1
- package/dist/server/src/utils/logger/dev-logger.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 +82 -5
- package/dist/server/src/utils/logger/log-modules.d.ts +15 -9
- package/dist/server/src/utils/logger/log-modules.d.ts.map +1 -1
- package/dist/server/src/utils/logger/log-modules.js +8 -6
- package/dist/server/src/utils/parameter-validator.d.ts +10 -0
- package/dist/server/src/utils/parameter-validator.d.ts.map +1 -0
- package/dist/server/src/utils/parameter-validator.js +53 -0
- package/dist/server/src/utils/process-tree.d.ts +49 -0
- package/dist/server/src/utils/process-tree.d.ts.map +1 -0
- package/dist/server/src/utils/process-tree.js +285 -0
- package/dist/server/src/utils/request-context.d.ts +0 -18
- package/dist/server/src/utils/request-context.d.ts.map +1 -1
- package/dist/server/src/utils/request-context.js +0 -20
- package/dist/server/src/utils/sort-utils.d.ts +40 -0
- package/dist/server/src/utils/sort-utils.d.ts.map +1 -0
- package/dist/server/src/utils/sort-utils.js +131 -0
- package/dist/server/src/utils/transports/sse-transport.d.ts +16 -1
- package/dist/server/src/utils/transports/sse-transport.d.ts.map +1 -1
- package/dist/server/src/utils/transports/sse-transport.js +55 -9
- package/dist/server/src/utils/transports/stdio-transport.d.ts +24 -53
- package/dist/server/src/utils/transports/stdio-transport.d.ts.map +1 -1
- package/dist/server/src/utils/transports/stdio-transport.js +66 -247
- package/dist/server/src/utils/transports/streamable-http-transport.d.ts +24 -1
- package/dist/server/src/utils/transports/streamable-http-transport.d.ts.map +1 -1
- package/dist/server/src/utils/transports/streamable-http-transport.js +68 -8
- package/dist/server/src/utils/transports/transport-factory.d.ts +9 -4
- package/dist/server/src/utils/transports/transport-factory.d.ts.map +1 -1
- package/dist/server/src/utils/transports/transport-factory.js +31 -11
- package/dist/server/src/utils/transports/transport.interface.d.ts +6 -0
- package/dist/server/src/utils/transports/transport.interface.d.ts.map +1 -1
- package/dist/server/src/utils/version.d.ts +11 -0
- package/dist/server/src/utils/version.d.ts.map +1 -0
- package/dist/server/src/utils/version.js +57 -0
- package/dist/server/tests/contract/mcp-protocol/initialize.test.js +24 -24
- package/dist/server/tests/contract/mcp-protocol/tools-call.test.js +49 -45
- package/dist/server/tests/contract/mcp-protocol/tools-list.test.js +35 -36
- package/dist/server/tests/evaluation/evaluation.test.js +10 -9
- package/dist/server/tests/integration/api/gateway.test.js +2 -2
- package/dist/server/tests/integration/gateway/fault-tolerance.test.js +65 -25
- package/dist/server/tests/integration/gateway/mcp-connection.test.js +53 -61
- package/dist/server/tests/server.test.js +27 -16
- package/dist/server/tests/temp/temp-run-docling.d.ts +2 -0
- package/dist/server/tests/temp/temp-run-docling.d.ts.map +1 -0
- package/dist/server/tests/temp/temp-run-docling.js +53 -0
- package/dist/server/tests/types/test-helpers.d.ts +1 -2
- package/dist/server/tests/types/test-helpers.d.ts.map +1 -1
- package/dist/server/tests/unit/config/config-loader-automatic-migration.test.d.ts +2 -0
- package/dist/server/tests/unit/config/config-loader-automatic-migration.test.d.ts.map +1 -0
- package/dist/server/tests/unit/config/config-loader-automatic-migration.test.js +199 -0
- package/dist/server/tests/unit/config/config-migrator.test.d.ts +2 -0
- package/dist/server/tests/unit/config/config-migrator.test.d.ts.map +1 -0
- package/dist/server/tests/unit/config/config-migrator.test.js +316 -0
- package/dist/server/tests/unit/config/config-saver.test.d.ts +2 -0
- package/dist/server/tests/unit/config/config-saver.test.d.ts.map +1 -0
- package/dist/server/tests/unit/config/config-saver.test.js +200 -0
- package/dist/server/tests/unit/config/config.schema.test.d.ts +2 -0
- package/dist/server/tests/unit/config/config.schema.test.d.ts.map +1 -0
- package/dist/server/tests/unit/config/config.schema.test.js +347 -0
- package/dist/server/tests/unit/server/runner.test.js +86 -62
- package/dist/server/tests/unit/services/connection/connection-manager.test.d.ts +2 -0
- package/dist/server/tests/unit/services/connection/connection-manager.test.d.ts.map +1 -0
- package/dist/server/tests/unit/services/connection/connection-manager.test.js +112 -0
- package/dist/server/tests/unit/services/hub-manager-service.test.js +112 -46
- package/dist/server/tests/unit/services/hub-manager.test.js +25 -15
- package/dist/server/tests/unit/services/hub-tools/instance-selector.test.d.ts +2 -0
- package/dist/server/tests/unit/services/hub-tools/instance-selector.test.d.ts.map +1 -0
- package/dist/server/tests/unit/services/hub-tools/instance-selector.test.js +195 -0
- package/dist/server/tests/unit/services/hub-tools/server-selector.test.d.ts +2 -0
- package/dist/server/tests/unit/services/hub-tools/server-selector.test.d.ts.map +1 -0
- package/dist/server/tests/unit/services/hub-tools/server-selector.test.js +190 -0
- package/dist/server/tests/unit/services/hub-tools.service.test.js +560 -320
- package/dist/server/tests/unit/services/instance-matcher.test.d.ts +2 -0
- package/dist/server/tests/unit/services/instance-matcher.test.d.ts.map +1 -0
- package/dist/server/tests/unit/services/instance-matcher.test.js +256 -0
- package/dist/server/tests/unit/services/session-manager.test.js +26 -436
- package/dist/server/tests/unit/utils/config.test.js +88 -186
- package/dist/server/tests/unit/utils/json-utils.test.js +18 -18
- package/dist/server/tests/unit/utils/logger-formatter.test.d.ts +2 -0
- package/dist/server/tests/unit/utils/logger-formatter.test.d.ts.map +1 -0
- package/dist/server/tests/unit/utils/logger-formatter.test.js +66 -0
- package/dist/server/tests/unit/utils/parameter-validator.test.d.ts +2 -0
- package/dist/server/tests/unit/utils/parameter-validator.test.d.ts.map +1 -0
- package/dist/server/tests/unit/utils/parameter-validator.test.js +63 -0
- package/dist/server/tests/unit/utils/process-tree.test.d.ts +2 -0
- package/dist/server/tests/unit/utils/process-tree.test.d.ts.map +1 -0
- package/dist/server/tests/unit/utils/process-tree.test.js +129 -0
- package/dist/server/tests/unit/utils/request-context.test.js +5 -24
- package/dist/server/tests/unit/utils/sort-utils.test.d.ts +2 -0
- package/dist/server/tests/unit/utils/sort-utils.test.d.ts.map +1 -0
- package/dist/server/tests/unit/utils/sort-utils.test.js +220 -0
- package/dist/server/tests/unit/utils/transport-factory.test.d.ts +2 -0
- package/dist/server/tests/unit/utils/transport-factory.test.d.ts.map +1 -0
- package/dist/server/tests/unit/utils/transport-factory.test.js +55 -0
- package/package.json +1 -1
- package/dist/client/assets/ResourceDetailView-Bf-1ffbk.js +0 -1
- package/dist/client/assets/ResourcesView-CjMklkyv.css +0 -1
- package/dist/client/assets/ResourcesView-g5x4xCPh.js +0 -1
- package/dist/client/assets/ServerDashboard-Chpne8Q0.css +0 -1
- package/dist/client/assets/ServerDashboard-G8Wmp4hF.js +0 -2
- package/dist/client/assets/ServerListView-dV2XrPjo.js +0 -32
- package/dist/client/assets/ServerStatusTags.vue_vue_type_script_setup_true_lang-BHiTFM7-.js +0 -1
- package/dist/client/assets/SessionsView-Ckd38lj1.js +0 -1
- package/dist/client/assets/SettingsView-BJUdepEQ.js +0 -1
- package/dist/client/assets/ToolCallDialog-C_bTCpHC.js +0 -1
- package/dist/client/assets/ToolsView-0c2eputu.js +0 -1
- package/dist/client/assets/el-form-item-BVMLpmVC.css +0 -1
- package/dist/client/assets/el-form-item-ClFnj49k.js +0 -12
- package/dist/client/assets/el-input-CDnuSKVZ.js +0 -1
- package/dist/client/assets/el-input-CmuHb8HS.css +0 -1
- package/dist/client/assets/el-overlay-B2ZKM6Up.css +0 -1
- package/dist/client/assets/el-overlay-CzMkXyYy.js +0 -1
- package/dist/client/assets/el-select-DvjGddk_.js +0 -1
- package/dist/client/assets/el-tab-pane-C_DQMcwe.js +0 -1
- package/dist/client/assets/el-table-column-CASRIbZM.js +0 -1
- package/dist/client/assets/el-table-column-T_mV9jNw.css +0 -1
- package/dist/client/assets/el-tag-DjxZVOpb.css +0 -1
- package/dist/client/assets/el-tag-npbwux4f.js +0 -1
- package/dist/client/assets/index-CCnAxNF8.js +0 -2
- package/dist/client/assets/index-d1DZeSfz.js +0 -1
- package/dist/client/assets/vnode-CHomNjgN.js +0 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instance Matcher Service
|
|
3
|
+
* Provides intelligent instance selection based on tags and connection status.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Calculates the match score between an instance and requested tags.
|
|
7
|
+
*
|
|
8
|
+
* The score ranges from 0.0 (no match) to 1.0 (perfect match).
|
|
9
|
+
* Calculated as the ratio of matching tags to total requested tags.
|
|
10
|
+
*
|
|
11
|
+
* @param instance - The service instance to evaluate
|
|
12
|
+
* @param requestedTags - The tags to match against
|
|
13
|
+
* @returns Match score between 0.0 and 1.0
|
|
14
|
+
*/
|
|
15
|
+
export function calculateMatchScore(instance, requestedTags) {
|
|
16
|
+
// If no tags requested, perfect match
|
|
17
|
+
if (!requestedTags || Object.keys(requestedTags).length === 0) {
|
|
18
|
+
return 1.0;
|
|
19
|
+
}
|
|
20
|
+
const instanceTags = instance.tags || {};
|
|
21
|
+
const requestedTagKeys = Object.keys(requestedTags);
|
|
22
|
+
if (requestedTagKeys.length === 0) {
|
|
23
|
+
return 1.0;
|
|
24
|
+
}
|
|
25
|
+
// Count matching tags
|
|
26
|
+
let matchCount = 0;
|
|
27
|
+
for (const [key, value] of Object.entries(requestedTags)) {
|
|
28
|
+
if (instanceTags[key] === value) {
|
|
29
|
+
matchCount++;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return matchCount / requestedTagKeys.length;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Filters instances to only include enabled ones.
|
|
36
|
+
*
|
|
37
|
+
* @param instances - Array of instances to filter
|
|
38
|
+
* @returns Array of enabled instances
|
|
39
|
+
*/
|
|
40
|
+
export function filterEnabledInstances(instances) {
|
|
41
|
+
return instances.filter((instance) => instance.enabled !== false);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sorts instances by connection status (connected first).
|
|
45
|
+
*
|
|
46
|
+
* Preserves the original order within each status group.
|
|
47
|
+
*
|
|
48
|
+
* @param instances - Array of instances with connection status
|
|
49
|
+
* @returns Sorted array with connected instances first
|
|
50
|
+
*/
|
|
51
|
+
export function sortByConnectionStatus(instances) {
|
|
52
|
+
const connected = [];
|
|
53
|
+
const disconnected = [];
|
|
54
|
+
for (const instance of instances) {
|
|
55
|
+
if (instance.connected === true) {
|
|
56
|
+
connected.push(instance);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
disconnected.push(instance);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return [...connected, ...disconnected];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Selects the best matching instance based on tags and connection status.
|
|
66
|
+
*
|
|
67
|
+
* Selection priority:
|
|
68
|
+
* 1. Only consider enabled instances
|
|
69
|
+
* 2. Prioritize connected instances over disconnected ones (ALWAYS prefer connected if available)
|
|
70
|
+
* 3. Among connected instances, select the one with highest tag match score
|
|
71
|
+
* 4. Among disconnected instances, select the one with highest tag match score
|
|
72
|
+
* 5. Only return undefined if NO connected instances AND tags requested but no match
|
|
73
|
+
*
|
|
74
|
+
* @param instances - Array of instances to choose from
|
|
75
|
+
* @param requestedTags - Optional tags to match
|
|
76
|
+
* @returns The best matching instance, or undefined if no suitable instance found
|
|
77
|
+
*/
|
|
78
|
+
export function selectBestMatch(instances, requestedTags) {
|
|
79
|
+
if (instances.length === 0) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
// Filter to only enabled instances
|
|
83
|
+
const enabledInstances = filterEnabledInstances(instances);
|
|
84
|
+
if (enabledInstances.length === 0) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
// Check if tags are requested
|
|
88
|
+
const hasRequestedTags = !!(requestedTags && Object.keys(requestedTags).length > 0);
|
|
89
|
+
// Sort by connection status (connected first)
|
|
90
|
+
const sortedInstances = sortByConnectionStatus(enabledInstances);
|
|
91
|
+
// Group by connection status
|
|
92
|
+
const connectedInstances = sortedInstances.filter((i) => i.connected === true);
|
|
93
|
+
const disconnectedInstances = sortedInstances.filter((i) => i.connected !== true);
|
|
94
|
+
// Always prefer connected instances if available, even with 0 tag match
|
|
95
|
+
if (connectedInstances.length > 0) {
|
|
96
|
+
// For connected instances, always return the best one (never undefined for connected)
|
|
97
|
+
return findBestMatchingInstance(connectedInstances, requestedTags, false);
|
|
98
|
+
}
|
|
99
|
+
// Only for disconnected instances, return undefined if tags requested but no match
|
|
100
|
+
if (disconnectedInstances.length > 0) {
|
|
101
|
+
return findBestMatchingInstance(disconnectedInstances, requestedTags, hasRequestedTags);
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Helper to find the instance with the highest match score.
|
|
107
|
+
*
|
|
108
|
+
* @param instances - Array of instances to evaluate
|
|
109
|
+
* @param requestedTags - Tags to match against
|
|
110
|
+
* @param hasRequestedTags - Whether tags were explicitly requested
|
|
111
|
+
* @returns The instance with highest score, or undefined if no match found and tags requested
|
|
112
|
+
*/
|
|
113
|
+
function findBestMatchingInstance(instances, requestedTags, hasRequestedTags) {
|
|
114
|
+
if (instances.length === 0) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
let bestInstance;
|
|
118
|
+
let bestScore = -1;
|
|
119
|
+
for (const instance of instances) {
|
|
120
|
+
const score = calculateMatchScore(instance, requestedTags);
|
|
121
|
+
if (score > bestScore) {
|
|
122
|
+
bestScore = score;
|
|
123
|
+
bestInstance = instance;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// If tags were requested and best score is 0 (no match), return undefined
|
|
127
|
+
if (hasRequestedTags && bestScore === 0) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
// Otherwise return the best instance (or first instance if all scores equal)
|
|
131
|
+
return bestInstance || instances[0];
|
|
132
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ServerConfig, ServerInstance } from '../../../shared/models/server.model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when tag-match-unique instance selection fails.
|
|
4
|
+
* Passes raw data so the error class itself can format the message.
|
|
5
|
+
*/
|
|
6
|
+
export declare class TagMatchUniqueError extends Error {
|
|
7
|
+
readonly instanceCount: number;
|
|
8
|
+
readonly requestTags?: Record<string, string> | undefined;
|
|
9
|
+
readonly availableInstances?: Record<number, Record<string, string>> | undefined;
|
|
10
|
+
constructor(instanceCount: number, requestTags?: Record<string, string> | undefined, availableInstances?: Record<number, Record<string, string>> | undefined);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Instance selector for multi-instance servers
|
|
14
|
+
* Implements three selection strategies: random, round-robin, and tag-match-unique
|
|
15
|
+
*/
|
|
16
|
+
export declare class InstanceSelector {
|
|
17
|
+
private static roundRobinCounters;
|
|
18
|
+
/**
|
|
19
|
+
* Select best instance based on configured strategy
|
|
20
|
+
*/
|
|
21
|
+
static selectInstance(serverName: string, serverConfig: ServerConfig, requestOptions?: {
|
|
22
|
+
sessionId?: string;
|
|
23
|
+
tags?: Record<string, string>;
|
|
24
|
+
}): ServerInstance | undefined;
|
|
25
|
+
private static selectRandomInstance;
|
|
26
|
+
private static selectRoundRobinInstance;
|
|
27
|
+
private static selectTagMatchUniqueInstance;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=instance-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-selector.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/instance-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGnF;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,aAAa,EAAE,MAAM;aACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;aACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAF3D,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,YAAA;CAmB9E;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAA6B;IAE9D;;OAEG;IACH,MAAM,CAAC,cAAc,CACnB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,YAAY,EAC1B,cAAc,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GACrE,cAAc,GAAG,SAAS;IA+B7B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAKnC,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAUvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;CAiC5C"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { InstanceSelectionStrategy } from '../../models/server.model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when tag-match-unique instance selection fails.
|
|
4
|
+
* Passes raw data so the error class itself can format the message.
|
|
5
|
+
*/
|
|
6
|
+
export class TagMatchUniqueError extends Error {
|
|
7
|
+
instanceCount;
|
|
8
|
+
requestTags;
|
|
9
|
+
availableInstances;
|
|
10
|
+
constructor(instanceCount, requestTags, availableInstances) {
|
|
11
|
+
let message;
|
|
12
|
+
if (!requestTags) {
|
|
13
|
+
// Format available instances as index:tags for easy selection
|
|
14
|
+
const instancesStr = availableInstances
|
|
15
|
+
? Object.entries(availableInstances)
|
|
16
|
+
.map(([index, tags]) => `${index}:${JSON.stringify(tags)}`)
|
|
17
|
+
.join(', ')
|
|
18
|
+
: '';
|
|
19
|
+
message = `No tags provided for tag-match-unique strategy with ${instanceCount} instances. Available: [${instancesStr}]. Pass matching tags to select.`;
|
|
20
|
+
}
|
|
21
|
+
else if (instanceCount === 0) {
|
|
22
|
+
message = `No instance found matching tags: ${JSON.stringify(requestTags)}`;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
message = `Multiple instances match tags: ${JSON.stringify(requestTags)}. Expected unique match.`;
|
|
26
|
+
}
|
|
27
|
+
super(message);
|
|
28
|
+
this.instanceCount = instanceCount;
|
|
29
|
+
this.requestTags = requestTags;
|
|
30
|
+
this.availableInstances = availableInstances;
|
|
31
|
+
this.name = 'TagMatchUniqueError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Instance selector for multi-instance servers
|
|
36
|
+
* Implements three selection strategies: random, round-robin, and tag-match-unique
|
|
37
|
+
*/
|
|
38
|
+
export class InstanceSelector {
|
|
39
|
+
static roundRobinCounters = new Map();
|
|
40
|
+
/**
|
|
41
|
+
* Select best instance based on configured strategy
|
|
42
|
+
*/
|
|
43
|
+
static selectInstance(serverName, serverConfig, requestOptions) {
|
|
44
|
+
const { instances } = serverConfig;
|
|
45
|
+
const instanceSelectionStrategy = serverConfig.template.instanceSelectionStrategy || InstanceSelectionStrategy.RANDOM;
|
|
46
|
+
// Filter enabled instances
|
|
47
|
+
const enabledInstances = instances.filter((instance) => instance.enabled !== false);
|
|
48
|
+
if (enabledInstances.length === 0) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
// Single instance case - return directly
|
|
52
|
+
if (enabledInstances.length === 1) {
|
|
53
|
+
return enabledInstances[0];
|
|
54
|
+
}
|
|
55
|
+
switch (instanceSelectionStrategy) {
|
|
56
|
+
case InstanceSelectionStrategy.RANDOM:
|
|
57
|
+
return this.selectRandomInstance(enabledInstances);
|
|
58
|
+
case InstanceSelectionStrategy.ROUND_ROBIN:
|
|
59
|
+
return this.selectRoundRobinInstance(serverName, enabledInstances);
|
|
60
|
+
case InstanceSelectionStrategy.TAG_MATCH_UNIQUE:
|
|
61
|
+
return this.selectTagMatchUniqueInstance(enabledInstances, requestOptions?.tags);
|
|
62
|
+
default:
|
|
63
|
+
return enabledInstances[0]; // Fallback to first instance
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
static selectRandomInstance(instances) {
|
|
67
|
+
const randomIndex = Math.floor(Math.random() * instances.length);
|
|
68
|
+
return instances[randomIndex];
|
|
69
|
+
}
|
|
70
|
+
static selectRoundRobinInstance(serverName, instances) {
|
|
71
|
+
const counter = this.roundRobinCounters.get(serverName) || 0;
|
|
72
|
+
const selectedInstance = instances[counter % instances.length];
|
|
73
|
+
this.roundRobinCounters.set(serverName, (counter + 1) % instances.length);
|
|
74
|
+
return selectedInstance;
|
|
75
|
+
}
|
|
76
|
+
static selectTagMatchUniqueInstance(instances, requestTags) {
|
|
77
|
+
// Build available instances map with index:tags format
|
|
78
|
+
const availableInstances = {};
|
|
79
|
+
instances.forEach((instance, index) => {
|
|
80
|
+
availableInstances[index] = instance.tags || {};
|
|
81
|
+
});
|
|
82
|
+
if (!requestTags || Object.keys(requestTags).length === 0) {
|
|
83
|
+
// If no request tags provided, must have exactly one instance to avoid ambiguity
|
|
84
|
+
if (instances.length === 1) {
|
|
85
|
+
return instances[0];
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
throw new TagMatchUniqueError(instances.length, undefined, availableInstances);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const matchingInstances = instances.filter((instance) => {
|
|
92
|
+
if (!instance.tags)
|
|
93
|
+
return false;
|
|
94
|
+
// Check if all request tags match instance tags
|
|
95
|
+
return Object.entries(requestTags).every(([key, value]) => instance.tags[key] === value);
|
|
96
|
+
});
|
|
97
|
+
// Must have exactly one matching instance
|
|
98
|
+
if (matchingInstances.length !== 1) {
|
|
99
|
+
throw new TagMatchUniqueError(matchingInstances.length, requestTags, availableInstances);
|
|
100
|
+
}
|
|
101
|
+
return matchingInstances[0];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import type { Resource } from '../../../shared/models/resource.model.js';
|
|
2
2
|
import type { ServerStatus } from '../../../shared/types/common.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Clears the Hub to MCP URI mapping.
|
|
5
|
+
* Should be called before regenerating resources.
|
|
6
|
+
*/
|
|
7
|
+
export declare function clearHubToMcpUriMap(): void;
|
|
8
|
+
/**
|
|
9
|
+
* URI for the use guide resource.
|
|
10
|
+
*/
|
|
11
|
+
export declare const USE_GUIDE_URI = "hub://use-guide";
|
|
12
|
+
/**
|
|
13
|
+
* Name of the use guide resource.
|
|
14
|
+
*/
|
|
15
|
+
export declare const USE_GUIDE_NAME = "MCP Hub Lite Use Guide";
|
|
16
|
+
/**
|
|
17
|
+
* Description of the use guide resource.
|
|
18
|
+
*/
|
|
19
|
+
export declare const USE_GUIDE_DESCRIPTION = "Comprehensive guide to using MCP Hub Lite gateway and its features";
|
|
20
|
+
/**
|
|
21
|
+
* MIME type for the use guide resource.
|
|
22
|
+
*/
|
|
23
|
+
export declare const USE_GUIDE_MIME_TYPE = "text/markdown";
|
|
3
24
|
/**
|
|
4
25
|
* Server metadata resource content.
|
|
5
26
|
*/
|
|
@@ -7,10 +28,12 @@ export interface ServerMetadata {
|
|
|
7
28
|
name: string;
|
|
8
29
|
status: ServerStatus;
|
|
9
30
|
toolsCount: number;
|
|
31
|
+
tools: Record<string, string>;
|
|
10
32
|
resourcesCount: number;
|
|
11
33
|
tags: Record<string, string>;
|
|
12
34
|
lastHeartbeat: number;
|
|
13
35
|
uptime: number;
|
|
36
|
+
description: string;
|
|
14
37
|
}
|
|
15
38
|
/**
|
|
16
39
|
* Generates dynamic Hub resources based on currently connected MCP servers.
|
|
@@ -56,5 +79,5 @@ export declare function generateDynamicResources(): Resource[];
|
|
|
56
79
|
* const tools = await readResource('hub://servers/my-mcp-server/tools');
|
|
57
80
|
* ```
|
|
58
81
|
*/
|
|
59
|
-
export declare function readResource(uri: string): Promise<ServerMetadata | Resource[]>;
|
|
82
|
+
export declare function readResource(uri: string): Promise<ServerMetadata | Resource[] | string | unknown>;
|
|
60
83
|
//# sourceMappingURL=resource-generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-generator.d.ts","sourceRoot":"","sources":["../../../../../src/services/hub-tools/resource-generator.ts"],"names":[],"mappings":"
|
|
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;AAUlE;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AA6ID;;GAEG;AACH,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,qBAAqB,uEACoC,CAAC;AAEvE;;GAEG;AACH,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AAEnD;;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,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,IAAI,QAAQ,EAAE,CAgErD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,cAAc,GAAG,QAAQ,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,CAyGzD"}
|
|
@@ -1,6 +1,149 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
1
4
|
import { hubManager } from '../hub-manager.service.js';
|
|
2
5
|
import { mcpConnectionManager } from '../mcp-connection-manager.js';
|
|
3
|
-
import { hasValidId, selectBestInstance } from './server-selector.js';
|
|
6
|
+
import { hasValidId, selectBestInstance, getServerDescription } from './server-selector.js';
|
|
7
|
+
/**
|
|
8
|
+
* Maps Hub URI to original MCP URI for resource forwarding.
|
|
9
|
+
* Key: Hub URI (e.g., "hub://servers/exa-ai/0/tools/list")
|
|
10
|
+
* Value: Original MCP URI (e.g., "exa://tools/list")
|
|
11
|
+
*/
|
|
12
|
+
const hubToMcpUriMap = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* Clears the Hub to MCP URI mapping.
|
|
15
|
+
* Should be called before regenerating resources.
|
|
16
|
+
*/
|
|
17
|
+
export function clearHubToMcpUriMap() {
|
|
18
|
+
hubToMcpUriMap.clear();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Maps an MCP native URI to hub format.
|
|
22
|
+
* Example: "exa://tools/list" -> "hub://servers/exa-ai/0/tools/list"
|
|
23
|
+
* Also registers the mapping in hubToMcpUriMap for reverse lookup.
|
|
24
|
+
*
|
|
25
|
+
* @param serverName - The server name
|
|
26
|
+
* @param instanceIndex - The instance index
|
|
27
|
+
* @param mcpUri - The native MCP URI (e.g., "exa://tools/list")
|
|
28
|
+
* @returns The hub-formatted URI
|
|
29
|
+
*/
|
|
30
|
+
function getMcpPathFromUri(mcpUri) {
|
|
31
|
+
return mcpUri.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:(\/\/)?/, '');
|
|
32
|
+
}
|
|
33
|
+
function mapMcpUriToHub(serverName, instanceIndex, mcpUri) {
|
|
34
|
+
// Remove the scheme prefix (e.g., "exa://" or "exa:")
|
|
35
|
+
const mcpPath = getMcpPathFromUri(mcpUri);
|
|
36
|
+
const hubUri = `hub://servers/${serverName}/${instanceIndex}/${mcpPath}`;
|
|
37
|
+
// Register mapping for reverse lookup in readResource
|
|
38
|
+
hubToMcpUriMap.set(hubUri, mcpUri);
|
|
39
|
+
return hubUri;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Restores a missing Hub -> MCP URI mapping by scanning the current instance resources.
|
|
43
|
+
*/
|
|
44
|
+
function restoreMcpUriMapping(serverName, instanceIndex, _instanceId, mcpPath) {
|
|
45
|
+
const instanceResources = mcpConnectionManager.getResources(serverName, instanceIndex);
|
|
46
|
+
for (const resource of instanceResources) {
|
|
47
|
+
const originalUri = resource.uri;
|
|
48
|
+
if (typeof originalUri !== 'string') {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (getMcpPathFromUri(originalUri) !== mcpPath) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const hubUri = `hub://servers/${serverName}/${instanceIndex}/${mcpPath}`;
|
|
55
|
+
hubToMcpUriMap.set(hubUri, originalUri);
|
|
56
|
+
return originalUri;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parses a hub URI and extracts components.
|
|
62
|
+
* Supports:
|
|
63
|
+
* - hub://servers/{name} - Server metadata
|
|
64
|
+
* - hub://servers/{name}/tools - Tools list
|
|
65
|
+
* - hub://servers/{name}/resources - Resources list
|
|
66
|
+
* - hub://servers/{name}/{instanceIndex}/{mcpPath} - MCP native resource forwarding
|
|
67
|
+
*
|
|
68
|
+
* @param uri - The hub URI to parse
|
|
69
|
+
* @returns Parsed components, 'unknown' if format is valid but resource type is unknown, or null if format is invalid
|
|
70
|
+
*/
|
|
71
|
+
function parseHubUri(uri) {
|
|
72
|
+
if (!uri.startsWith('hub://')) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const parts = uri.replace('hub://', '').split('/');
|
|
76
|
+
if (parts.length < 2 || parts[0] !== 'servers') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const serverName = parts[1];
|
|
80
|
+
// hub://servers/{name} - no instance index
|
|
81
|
+
if (parts.length === 2) {
|
|
82
|
+
return { serverName };
|
|
83
|
+
}
|
|
84
|
+
// hub://servers/{name}/tools or hub://servers/{name}/resources
|
|
85
|
+
// These are list requests, not MCP forwarding
|
|
86
|
+
if (parts.length === 3) {
|
|
87
|
+
const resourceType = parts[2];
|
|
88
|
+
if (resourceType === 'tools' || resourceType === 'resources') {
|
|
89
|
+
return { serverName, listType: resourceType };
|
|
90
|
+
}
|
|
91
|
+
// Unknown resource type but valid URI format
|
|
92
|
+
return 'unknown';
|
|
93
|
+
}
|
|
94
|
+
// hub://servers/{name}/{instanceIndex}/{mcpPath}
|
|
95
|
+
const instanceIndex = parseInt(parts[2], 10);
|
|
96
|
+
if (isNaN(instanceIndex) || parts.length < 4) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const mcpPath = parts.slice(3).join('/');
|
|
100
|
+
return { serverName, instanceIndex, mcpPath };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Path to the use guide Markdown file.
|
|
104
|
+
*/
|
|
105
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
106
|
+
const __dirname = dirname(__filename);
|
|
107
|
+
const USE_GUIDE_PATH = join(__dirname, 'use-guide.md');
|
|
108
|
+
/**
|
|
109
|
+
* Loads the use guide content from the Markdown file.
|
|
110
|
+
*
|
|
111
|
+
* @returns {string} Markdown formatted use guide content
|
|
112
|
+
*/
|
|
113
|
+
function loadUseGuideContent() {
|
|
114
|
+
try {
|
|
115
|
+
return fs.readFileSync(USE_GUIDE_PATH, 'utf-8');
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Fallback in case the file can't be read
|
|
119
|
+
return `# MCP Hub Lite Use Guide
|
|
120
|
+
|
|
121
|
+
## Overview
|
|
122
|
+
|
|
123
|
+
MCP Hub Lite is a lightweight MCP (Model Context Protocol) gateway that acts as a unified interface between AI assistants and multiple backend MCP servers.
|
|
124
|
+
|
|
125
|
+
## Note
|
|
126
|
+
|
|
127
|
+
The complete use guide is currently unavailable. Please check the MCP Hub Lite documentation at https://github.com/your-org/mcp-hub-lite for more information.
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* URI for the use guide resource.
|
|
133
|
+
*/
|
|
134
|
+
export const USE_GUIDE_URI = 'hub://use-guide';
|
|
135
|
+
/**
|
|
136
|
+
* Name of the use guide resource.
|
|
137
|
+
*/
|
|
138
|
+
export const USE_GUIDE_NAME = 'MCP Hub Lite Use Guide';
|
|
139
|
+
/**
|
|
140
|
+
* Description of the use guide resource.
|
|
141
|
+
*/
|
|
142
|
+
export const USE_GUIDE_DESCRIPTION = 'Comprehensive guide to using MCP Hub Lite gateway and its features';
|
|
143
|
+
/**
|
|
144
|
+
* MIME type for the use guide resource.
|
|
145
|
+
*/
|
|
146
|
+
export const USE_GUIDE_MIME_TYPE = 'text/markdown';
|
|
4
147
|
/**
|
|
5
148
|
* Generates dynamic Hub resources based on currently connected MCP servers.
|
|
6
149
|
*
|
|
@@ -20,25 +163,58 @@ import { hasValidId, selectBestInstance } from './server-selector.js';
|
|
|
20
163
|
*/
|
|
21
164
|
export function generateDynamicResources() {
|
|
22
165
|
const resources = [];
|
|
166
|
+
clearHubToMcpUriMap();
|
|
167
|
+
// Add use-guide resource first - it's always available
|
|
168
|
+
resources.push({
|
|
169
|
+
uri: USE_GUIDE_URI,
|
|
170
|
+
name: USE_GUIDE_NAME,
|
|
171
|
+
description: USE_GUIDE_DESCRIPTION,
|
|
172
|
+
mimeType: USE_GUIDE_MIME_TYPE
|
|
173
|
+
// System resources don't have serverName/serverIndex
|
|
174
|
+
});
|
|
23
175
|
// Use the same access pattern as tools - directly access manager cache
|
|
24
176
|
const servers = hubManager.getAllServers();
|
|
25
177
|
for (const server of servers) {
|
|
26
|
-
if (!hasValidId(server)
|
|
178
|
+
if (!hasValidId(server)) {
|
|
27
179
|
continue;
|
|
28
180
|
}
|
|
29
|
-
|
|
30
|
-
|
|
181
|
+
// Check if any instance is enabled
|
|
182
|
+
const hasEnabledInstance = server.config.instances.some((i) => i.enabled !== false);
|
|
183
|
+
if (!hasEnabledInstance) {
|
|
31
184
|
continue;
|
|
32
185
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
186
|
+
// Iterate over all instances to expose each instance's resources
|
|
187
|
+
for (const instance of server.config.instances) {
|
|
188
|
+
if (instance.enabled === false) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const instanceIndex = instance.index;
|
|
192
|
+
// Server metadata resource (one per server, not per instance)
|
|
193
|
+
if (instanceIndex === 0) {
|
|
194
|
+
resources.push({
|
|
195
|
+
uri: `hub://servers/${server.name}`,
|
|
196
|
+
name: `Server: ${server.name}`,
|
|
197
|
+
description: getServerDescription(server.config, server.name),
|
|
198
|
+
mimeType: 'application/json',
|
|
199
|
+
serverName: server.name,
|
|
200
|
+
serverIndex: instanceIndex
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// Get MCP native resources and map to hub format
|
|
204
|
+
const instanceIdx = instanceIndex ?? 0;
|
|
205
|
+
const mcpResources = mcpConnectionManager.getResources(server.name, instanceIdx);
|
|
206
|
+
for (const res of mcpResources) {
|
|
207
|
+
// Format: Resource: {ServerName} - {Index}: {Native Name}
|
|
208
|
+
const displayName = `Resource:${server.name} - ${instanceIdx}:${res.name}`;
|
|
209
|
+
resources.push({
|
|
210
|
+
uri: mapMcpUriToHub(server.name, instanceIdx, res.uri),
|
|
211
|
+
name: displayName,
|
|
212
|
+
description: res.description,
|
|
213
|
+
mimeType: res.mimeType
|
|
214
|
+
// No serverId - instanceIndex is embedded in the URI
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
42
218
|
}
|
|
43
219
|
return resources;
|
|
44
220
|
}
|
|
@@ -73,44 +249,88 @@ export async function readResource(uri) {
|
|
|
73
249
|
if (!uri.startsWith('hub://')) {
|
|
74
250
|
throw new Error(`Invalid Hub resource URI: ${uri}. Must start with 'hub://'`);
|
|
75
251
|
}
|
|
252
|
+
// Check for use-guide resource first
|
|
253
|
+
if (uri === USE_GUIDE_URI) {
|
|
254
|
+
return loadUseGuideContent();
|
|
255
|
+
}
|
|
76
256
|
// Parse URI
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
257
|
+
const parsed = parseHubUri(uri);
|
|
258
|
+
if (!parsed) {
|
|
79
259
|
throw new Error(`Invalid Hub resource URI format: ${uri}`);
|
|
80
260
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
261
|
+
// Handle unknown resource type
|
|
262
|
+
if (parsed === 'unknown') {
|
|
263
|
+
const parts = uri.replace('hub://', '').split('/');
|
|
264
|
+
const resourceType = parts[2];
|
|
265
|
+
throw new Error(`Unknown resource type: ${resourceType}`);
|
|
266
|
+
}
|
|
267
|
+
const { serverName, instanceIndex, mcpPath, listType } = parsed;
|
|
268
|
+
// Find server config
|
|
269
|
+
const serverConfig = hubManager.getServerByName(serverName);
|
|
270
|
+
if (!serverConfig) {
|
|
271
|
+
throw new Error(`Server not found: ${serverName}`);
|
|
272
|
+
}
|
|
273
|
+
// If no instanceIndex, check if this is a list request or metadata request
|
|
274
|
+
if (instanceIndex === undefined) {
|
|
275
|
+
// Handle list requests first
|
|
276
|
+
if (listType) {
|
|
277
|
+
// Use selectBestInstance to get an instance for the list
|
|
278
|
+
const serverInfo = selectBestInstance(serverName);
|
|
279
|
+
if (!serverInfo) {
|
|
280
|
+
throw new Error(`Server not found or not connected: ${serverName}`);
|
|
281
|
+
}
|
|
282
|
+
const instanceIndex = serverInfo.instance.index;
|
|
283
|
+
if (listType === 'tools') {
|
|
284
|
+
return mcpConnectionManager.getTools(serverName, instanceIndex);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
return mcpConnectionManager.getResources(serverName, instanceIndex);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Server metadata request - use selectBestInstance to get runtime properties
|
|
291
|
+
const serverInfo = selectBestInstance(serverName);
|
|
292
|
+
if (!serverInfo) {
|
|
293
|
+
throw new Error(`Server not found or not connected: ${serverName}`);
|
|
294
|
+
}
|
|
295
|
+
const instanceIndex = serverInfo.instance.index;
|
|
296
|
+
const tools = mcpConnectionManager.getTools(serverName, instanceIndex);
|
|
297
|
+
const resources = mcpConnectionManager.getResources(serverName, instanceIndex);
|
|
298
|
+
// Build tool name to description map
|
|
299
|
+
const toolsMap = {};
|
|
300
|
+
for (const tool of tools) {
|
|
301
|
+
toolsMap[tool.name] = tool.description || '';
|
|
302
|
+
}
|
|
95
303
|
return {
|
|
96
304
|
name: serverName,
|
|
97
305
|
status: serverInfo.instance.status,
|
|
98
306
|
toolsCount: tools.length,
|
|
307
|
+
tools: toolsMap,
|
|
99
308
|
resourcesCount: resources.length,
|
|
100
|
-
tags:
|
|
309
|
+
tags: serverInfo.instance.tags || {},
|
|
101
310
|
lastHeartbeat: serverInfo.instance.lastHeartbeat,
|
|
102
|
-
uptime: serverInfo.instance.uptime
|
|
311
|
+
uptime: serverInfo.instance.uptime,
|
|
312
|
+
description: getServerDescription(serverConfig, serverName)
|
|
103
313
|
};
|
|
104
314
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
315
|
+
// MCP native resource forwarding: hub://servers/{name}/{instanceIndex}/{mcpPath}
|
|
316
|
+
// Find the specific instance by index
|
|
317
|
+
const targetInstance = serverConfig.instances.find((i) => i.index === instanceIndex && i.enabled !== false);
|
|
318
|
+
if (!targetInstance) {
|
|
319
|
+
throw new Error(`Instance ${instanceIndex} not found or not enabled for server: ${serverName}`);
|
|
108
320
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
321
|
+
const instanceId = targetInstance.id;
|
|
322
|
+
// If mcpPath is empty, return instance-level info
|
|
323
|
+
if (!mcpPath) {
|
|
324
|
+
return mcpConnectionManager.getResources(serverName, instanceIndex);
|
|
112
325
|
}
|
|
113
|
-
|
|
114
|
-
|
|
326
|
+
// Forward to MCP server for actual resource read
|
|
327
|
+
// Use the mapping to get the original MCP URI (hub://servers/exa-ai/0/tools/list -> exa://tools/list)
|
|
328
|
+
let originalMcpUri = hubToMcpUriMap.get(uri);
|
|
329
|
+
if (!originalMcpUri) {
|
|
330
|
+
originalMcpUri = restoreMcpUriMapping(serverName, instanceIndex, instanceId, mcpPath);
|
|
331
|
+
}
|
|
332
|
+
if (!originalMcpUri) {
|
|
333
|
+
throw new Error(`MCP URI not found in mapping for: ${uri}`);
|
|
115
334
|
}
|
|
335
|
+
return mcpConnectionManager.readResource(serverName, instanceIndex, originalMcpUri);
|
|
116
336
|
}
|