@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
package/dist/client/index.html
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Server Manager</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DwhULJXZ.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-ClSvefnQ.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-UtsV0Cvh.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="app"></div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** MCP Hub Lite gateway server name — used for system tool identification and gateway naming */
|
|
2
|
+
export declare const MCP_HUB_LITE_SERVER = "mcp-hub-lite";
|
|
3
|
+
/** Session mode constants for gateway transport selection */
|
|
4
|
+
export declare const SESSION_MODE_STATELESS: "stateless";
|
|
5
|
+
export declare const SESSION_MODE_STATEFUL: "stateful";
|
|
6
|
+
export declare const SESSION_MODE_VALUES: readonly ["stateless", "stateful"];
|
|
7
|
+
export type SessionMode = (typeof SESSION_MODE_VALUES)[number];
|
|
8
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../shared/models/constants.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,6DAA6D;AAC7D,eAAO,MAAM,sBAAsB,EAAG,WAAoB,CAAC;AAC3D,eAAO,MAAM,qBAAqB,EAAG,UAAmB,CAAC;AACzD,eAAO,MAAM,mBAAmB,oCAA2D,CAAC;AAC5F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** MCP Hub Lite gateway server name — used for system tool identification and gateway naming */
|
|
2
|
+
export const MCP_HUB_LITE_SERVER = 'mcp-hub-lite';
|
|
3
|
+
/** Session mode constants for gateway transport selection */
|
|
4
|
+
export const SESSION_MODE_STATELESS = 'stateless';
|
|
5
|
+
export const SESSION_MODE_STATEFUL = 'stateful';
|
|
6
|
+
export const SESSION_MODE_VALUES = [SESSION_MODE_STATELESS, SESSION_MODE_STATEFUL];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../shared/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../shared/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
|
|
@@ -232,6 +232,7 @@ export declare const LoggingConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
232
232
|
mcpCommDebug: z.ZodDefault<z.ZodBoolean>;
|
|
233
233
|
apiDebug: z.ZodDefault<z.ZodBoolean>;
|
|
234
234
|
gatewayDebug: z.ZodDefault<z.ZodBoolean>;
|
|
235
|
+
showTraceContext: z.ZodDefault<z.ZodBoolean>;
|
|
235
236
|
}, z.core.$strip>>;
|
|
236
237
|
export type LoggingConfig = z.infer<typeof LoggingConfigSchema>;
|
|
237
238
|
/**
|
|
@@ -280,6 +281,7 @@ export declare const SystemConfigSchema: z.ZodObject<{
|
|
|
280
281
|
mcpCommDebug: z.ZodDefault<z.ZodBoolean>;
|
|
281
282
|
apiDebug: z.ZodDefault<z.ZodBoolean>;
|
|
282
283
|
gatewayDebug: z.ZodDefault<z.ZodBoolean>;
|
|
284
|
+
showTraceContext: z.ZodDefault<z.ZodBoolean>;
|
|
283
285
|
}, z.core.$strip>>;
|
|
284
286
|
startup: z.ZodOptional<z.ZodDefault<z.ZodObject<{
|
|
285
287
|
startupDelay: z.ZodDefault<z.ZodNumber>;
|
|
@@ -287,6 +289,16 @@ export declare const SystemConfigSchema: z.ZodObject<{
|
|
|
287
289
|
maxConnectRetries: z.ZodDefault<z.ZodNumber>;
|
|
288
290
|
connectRetryDelay: z.ZodDefault<z.ZodNumber>;
|
|
289
291
|
}, z.core.$strip>>>;
|
|
292
|
+
session: z.ZodOptional<z.ZodObject<{
|
|
293
|
+
sessionModeRules: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
294
|
+
stateful: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
295
|
+
stateless: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
296
|
+
}, z.core.$strip>>>;
|
|
297
|
+
defaultSessionMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
298
|
+
stateless: "stateless";
|
|
299
|
+
stateful: "stateful";
|
|
300
|
+
}>>>;
|
|
301
|
+
}, z.core.$strip>>;
|
|
290
302
|
}, z.core.$strip>>;
|
|
291
303
|
security: z.ZodDefault<z.ZodObject<{
|
|
292
304
|
allowedNetworks: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -362,6 +374,8 @@ export declare const SystemConfigSchema: z.ZodObject<{
|
|
|
362
374
|
}, z.core.$strip>>>;
|
|
363
375
|
}, z.core.$strip>;
|
|
364
376
|
export type SystemConfig = z.infer<typeof SystemConfigSchema>;
|
|
377
|
+
/** Session configuration extracted from SystemConfig.system.session */
|
|
378
|
+
export type GatewayConfig = SystemConfig['system']['session'];
|
|
365
379
|
/**
|
|
366
380
|
* Server instance runtime configuration (for connection manager)
|
|
367
381
|
* Contains runtime-specific fields like timestamp, pid, etc.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.model.d.ts","sourceRoot":"","sources":["../../../../shared/models/server.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.model.d.ts","sourceRoot":"","sources":["../../../../shared/models/server.model.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { SESSION_MODE_VALUES, SESSION_MODE_STATEFUL } from './constants.js';
|
|
2
3
|
// ====== v1.1 Configuration Schema (Server Template + Instance Model) ======
|
|
3
4
|
/**
|
|
4
5
|
* Instance selection strategy constants for multi-instance servers
|
|
@@ -155,7 +156,8 @@ export const LoggingConfigSchema = z
|
|
|
155
156
|
jsonPretty: z.boolean().default(true),
|
|
156
157
|
mcpCommDebug: z.boolean().default(false),
|
|
157
158
|
apiDebug: z.boolean().default(false),
|
|
158
|
-
gatewayDebug: z.boolean().default(false)
|
|
159
|
+
gatewayDebug: z.boolean().default(false),
|
|
160
|
+
showTraceContext: z.boolean().default(true)
|
|
159
161
|
})
|
|
160
162
|
.default({
|
|
161
163
|
level: 'info',
|
|
@@ -163,7 +165,8 @@ export const LoggingConfigSchema = z
|
|
|
163
165
|
jsonPretty: true,
|
|
164
166
|
mcpCommDebug: false,
|
|
165
167
|
apiDebug: false,
|
|
166
|
-
gatewayDebug: false
|
|
168
|
+
gatewayDebug: false,
|
|
169
|
+
showTraceContext: true
|
|
167
170
|
});
|
|
168
171
|
// ====== Security Configuration Schema ======
|
|
169
172
|
/**
|
|
@@ -222,7 +225,19 @@ export const SystemConfigSchema = z.object({
|
|
|
222
225
|
theme: z.enum(['light', 'dark', 'system']).default('system'),
|
|
223
226
|
logging: LoggingConfigSchema,
|
|
224
227
|
// Using .optional() without .default() - code should use startup ?? {defaults} for default value
|
|
225
|
-
startup: StartupConfigSchema.optional()
|
|
228
|
+
startup: StartupConfigSchema.optional(),
|
|
229
|
+
session: z
|
|
230
|
+
.object({
|
|
231
|
+
sessionModeRules: z
|
|
232
|
+
.object({
|
|
233
|
+
stateful: z.array(z.string()).optional().default([]),
|
|
234
|
+
stateless: z.array(z.string()).optional().default([])
|
|
235
|
+
})
|
|
236
|
+
.optional()
|
|
237
|
+
.default({ stateful: [], stateless: [] }),
|
|
238
|
+
defaultSessionMode: z.enum(SESSION_MODE_VALUES).optional().default(SESSION_MODE_STATEFUL)
|
|
239
|
+
})
|
|
240
|
+
.optional()
|
|
226
241
|
})
|
|
227
242
|
.default({
|
|
228
243
|
host: 'localhost',
|
|
@@ -235,13 +250,21 @@ export const SystemConfigSchema = z.object({
|
|
|
235
250
|
jsonPretty: true,
|
|
236
251
|
mcpCommDebug: false,
|
|
237
252
|
apiDebug: false,
|
|
238
|
-
gatewayDebug: false
|
|
253
|
+
gatewayDebug: false,
|
|
254
|
+
showTraceContext: true
|
|
239
255
|
},
|
|
240
256
|
startup: {
|
|
241
257
|
startupDelay: 3000,
|
|
242
258
|
readyTimeout: 120000,
|
|
243
259
|
maxConnectRetries: 3,
|
|
244
260
|
connectRetryDelay: 5000
|
|
261
|
+
},
|
|
262
|
+
session: {
|
|
263
|
+
sessionModeRules: {
|
|
264
|
+
stateful: [],
|
|
265
|
+
stateless: []
|
|
266
|
+
},
|
|
267
|
+
defaultSessionMode: SESSION_MODE_STATEFUL
|
|
245
268
|
}
|
|
246
269
|
}),
|
|
247
270
|
security: SecurityConfigSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../shared/types/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../shared/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Debug Response Wrapper for MCP Gateway.
|
|
3
3
|
* Wraps Fastify reply to capture and log response content in development mode.
|
|
4
4
|
*/
|
|
5
|
-
import { logger, LOG_MODULES, isToolsListResponse, simplifyToolsListResponse, hasImageContent, simplifyImageContent } from '../../utils/logger.js';
|
|
5
|
+
import { logger, LOG_MODULES, isToolsListResponse, simplifyToolsListResponse, hasImageContent, simplifyImageContent } from '../../utils/logger/index.js';
|
|
6
6
|
import { stringifyForLogging, getMcpCommDebugSetting } from '../../utils/json-utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* Wraps Fastify reply to capture and log response content in development mode.
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Gateway endpoint using Streamable HTTP Transport
|
|
3
|
-
*
|
|
2
|
+
* MCP Gateway endpoint using stateful Streamable HTTP Transport.
|
|
3
|
+
*
|
|
4
|
+
* Each client session gets its own transport+server pair, identified by mcp-session-id header.
|
|
5
|
+
* POST without sessionId creates a new session.
|
|
6
|
+
* POST/GET/DELETE with sessionId routes to the existing session transport.
|
|
4
7
|
*/
|
|
5
|
-
import type { FastifyInstance } from 'fastify';
|
|
8
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
9
|
+
import type { SessionMode } from '../../../shared/models/constants.js';
|
|
6
10
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param fastify - Fastify instance to register routes on
|
|
11
|
+
* Resolves the effective session mode for a request.
|
|
12
|
+
* Priority: request header > UA keyword match > default (SESSION_MODE_STATEFUL).
|
|
10
13
|
*/
|
|
14
|
+
export declare function resolveSessionMode(request: FastifyRequest): SessionMode;
|
|
11
15
|
export declare function mcpGatewayRoutes(fastify: FastifyInstance): Promise<void>;
|
|
12
16
|
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../../../../src/api/mcp/gateway.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../../../../src/api/mcp/gateway.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAgB,MAAM,SAAS,CAAC;AAmB7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAK/D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,CAsBvE;AAgBD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,iBAiP9D"}
|
|
@@ -1,111 +1,259 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Gateway endpoint using Streamable HTTP Transport
|
|
3
|
-
*
|
|
2
|
+
* MCP Gateway endpoint using stateful Streamable HTTP Transport.
|
|
3
|
+
*
|
|
4
|
+
* Each client session gets its own transport+server pair, identified by mcp-session-id header.
|
|
5
|
+
* POST without sessionId creates a new session.
|
|
6
|
+
* POST/GET/DELETE with sessionId routes to the existing session transport.
|
|
4
7
|
*/
|
|
8
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
5
10
|
import { logger, LOG_MODULES } from '../../utils/logger/index.js';
|
|
6
11
|
import { stringifyForLogging, getMcpCommDebugSetting, getGatewayDebugSetting } from '../../utils/json-utils.js';
|
|
7
12
|
import { wrapReplyForDebug } from './debug-response-wrapper.js';
|
|
8
|
-
import {
|
|
13
|
+
import { setupTransportLogging, createPerRequestTransport } from '../../services/gateway/global-transport.js';
|
|
14
|
+
import { sessionManager } from '../../services/gateway/session-manager.js';
|
|
15
|
+
import { gateway } from '../../services/gateway/gateway.service.js';
|
|
16
|
+
import { runWithRequestContext } from '../../utils/request-context.js';
|
|
17
|
+
import { configManager } from '../../config/config-manager.js';
|
|
18
|
+
import { SESSION_MODE_STATEFUL, SESSION_MODE_STATELESS } from '../../../shared/models/constants.js';
|
|
19
|
+
const MCP_SESSION_ID = 'mcp-session-id';
|
|
20
|
+
const MCP_SESSION_MODE = 'x-mcp-session-mode';
|
|
9
21
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
22
|
+
* Resolves the effective session mode for a request.
|
|
23
|
+
* Priority: request header > UA keyword match > default (SESSION_MODE_STATEFUL).
|
|
24
|
+
*/
|
|
25
|
+
export function resolveSessionMode(request) {
|
|
26
|
+
// 1. Request header override (highest priority)
|
|
27
|
+
const header = request.headers[MCP_SESSION_MODE];
|
|
28
|
+
if (header === SESSION_MODE_STATELESS || header === SESSION_MODE_STATEFUL)
|
|
29
|
+
return header;
|
|
30
|
+
// 2. UA keyword matching (case-insensitive)
|
|
31
|
+
const ua = request.headers['user-agent'] || '';
|
|
32
|
+
const config = configManager.getConfig();
|
|
33
|
+
const rules = config?.system?.session?.sessionModeRules;
|
|
34
|
+
if (rules && ua) {
|
|
35
|
+
const uaLower = ua.toLowerCase();
|
|
36
|
+
for (const pattern of rules.stateless || []) {
|
|
37
|
+
if (uaLower.includes(pattern.toLowerCase()))
|
|
38
|
+
return SESSION_MODE_STATELESS;
|
|
39
|
+
}
|
|
40
|
+
for (const pattern of rules.stateful || []) {
|
|
41
|
+
if (uaLower.includes(pattern.toLowerCase()))
|
|
42
|
+
return SESSION_MODE_STATEFUL;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 3. Default fallback
|
|
46
|
+
return config?.system?.session?.defaultSessionMode ?? SESSION_MODE_STATEFUL;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Inject trace context into transport so that deferred send/onmessage wrappers
|
|
50
|
+
* can read it when ALS context is lost (MCP SDK defers send() calls internally).
|
|
13
51
|
*/
|
|
52
|
+
function injectTransportTrace(transport, sessionId, traceId) {
|
|
53
|
+
const ctx = transport;
|
|
54
|
+
ctx._traceSessionId = sessionId || undefined;
|
|
55
|
+
ctx._traceId = traceId;
|
|
56
|
+
}
|
|
14
57
|
export async function mcpGatewayRoutes(fastify) {
|
|
15
|
-
const
|
|
16
|
-
// First, log that we received the request
|
|
58
|
+
const logRequest = (request) => {
|
|
17
59
|
if (getMcpCommDebugSetting()) {
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
initialLogMsg += `\n Request headers: ${stringifyForLogging(request.headers)}`;
|
|
60
|
+
let msg = `MCP Gateway ${request.method} ${request.url}`;
|
|
61
|
+
msg += `\nRequest headers: ${stringifyForLogging(request.headers)}`;
|
|
21
62
|
if (request.body) {
|
|
22
63
|
try {
|
|
23
|
-
|
|
24
|
-
initialLogMsg += `\n Body: ${preview}`;
|
|
64
|
+
msg += `\nBody: ${stringifyForLogging(request.body)}`;
|
|
25
65
|
}
|
|
26
66
|
catch {
|
|
27
|
-
|
|
67
|
+
msg += '\nBody: [Unserializable]';
|
|
28
68
|
}
|
|
29
69
|
}
|
|
30
|
-
logger.debug(
|
|
70
|
+
logger.debug(msg, LOG_MODULES.COMMUNICATION);
|
|
31
71
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
reply.raw.writeHead(
|
|
36
|
-
reply.raw.end(JSON.stringify({
|
|
37
|
-
jsonrpc: '2.0',
|
|
38
|
-
error: { code: -32000, message: 'Bad Request: Accept: text/event-stream required' },
|
|
39
|
-
id: null
|
|
40
|
-
}));
|
|
41
|
-
return;
|
|
72
|
+
};
|
|
73
|
+
const sendError = (reply, statusCode, code, message, id = null) => {
|
|
74
|
+
if (!reply.raw.headersSent) {
|
|
75
|
+
reply.raw.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
76
|
+
reply.raw.end(JSON.stringify({ jsonrpc: '2.0', error: { code, message }, id }));
|
|
42
77
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
};
|
|
79
|
+
fastify.all('/mcp', {
|
|
80
|
+
bodyLimit: 10 * 1024 * 1024,
|
|
81
|
+
handler: async (request, reply) => {
|
|
82
|
+
const sessionId = request.headers[MCP_SESSION_ID] || '';
|
|
83
|
+
const traceId = randomUUID();
|
|
84
|
+
await runWithRequestContext({ sessionId: sessionId || undefined, traceId }, async () => {
|
|
85
|
+
logRequest(request);
|
|
86
|
+
reply.header('Content-Type', 'application/json');
|
|
87
|
+
wrapReplyForDebug(reply, sessionId);
|
|
88
|
+
reply.hijack();
|
|
89
|
+
const sessionMode = resolveSessionMode(request);
|
|
90
|
+
if (sessionMode === SESSION_MODE_STATELESS) {
|
|
91
|
+
// Stateless mode: per-request transport, no session tracking, no SSE
|
|
92
|
+
if (request.method !== 'POST') {
|
|
93
|
+
sendError(reply, 405, -32000, 'GET/DELETE not supported in stateless session mode');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const { transport } = await createPerRequestTransport();
|
|
98
|
+
injectTransportTrace(transport, undefined, traceId);
|
|
99
|
+
await transport.handleRequest(request.raw, reply.raw, request.body);
|
|
100
|
+
if (getGatewayDebugSetting()) {
|
|
101
|
+
logger.debug('Handled MCP request in stateless mode', LOG_MODULES.GATEWAY);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
106
|
+
const stack = error instanceof Error ? error.stack : 'No stack available';
|
|
107
|
+
logger.error(`Stateless MCP error: ${msg}`, LOG_MODULES.GATEWAY);
|
|
108
|
+
logger.error(`Stack: ${stack}`, LOG_MODULES.GATEWAY);
|
|
109
|
+
if (!reply.raw.headersSent) {
|
|
110
|
+
reply.raw.writeHead(500, { 'Content-Type': 'application/json' });
|
|
111
|
+
reply.raw.end(JSON.stringify({
|
|
112
|
+
jsonrpc: '2.0',
|
|
113
|
+
error: { code: -32000, message: 'Internal Server Error' },
|
|
114
|
+
id: null
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
58
119
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
120
|
+
try {
|
|
121
|
+
// Existing session — route to its transport
|
|
122
|
+
if (sessionId) {
|
|
123
|
+
const session = sessionManager.getSession(sessionId);
|
|
124
|
+
if (!session) {
|
|
125
|
+
sendError(reply, 404, -32001, 'Session not found');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
sessionManager.updateSessionMethod(sessionId, request.method);
|
|
129
|
+
// Track SSE stream to prevent stale cleanup while GET is active
|
|
130
|
+
if (request.method === 'GET') {
|
|
131
|
+
sessionManager.markSseOpened(sessionId);
|
|
132
|
+
}
|
|
133
|
+
injectTransportTrace(session.transport, sessionId, traceId);
|
|
134
|
+
await session.transport.handleRequest(request.raw, reply.raw, request.method === 'POST' ? request.body : undefined);
|
|
135
|
+
if (request.method === 'GET') {
|
|
136
|
+
sessionManager.markSseClosed(sessionId);
|
|
137
|
+
}
|
|
138
|
+
if (getGatewayDebugSetting()) {
|
|
139
|
+
logger.debug(`Handled MCP ${request.method} for session ${sessionId}`, LOG_MODULES.GATEWAY);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// New session — POST only (initialize)
|
|
144
|
+
if (request.method !== 'POST') {
|
|
145
|
+
sendError(reply, 400, -32000, 'Missing mcp-session-id header');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Create new stateful transport for this session
|
|
149
|
+
const transport = new StreamableHTTPServerTransport({
|
|
150
|
+
sessionIdGenerator: () => randomUUID(),
|
|
151
|
+
onsessionclosed: (id) => {
|
|
152
|
+
sessionManager.removeSession(id);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
setupTransportLogging(transport);
|
|
156
|
+
const server = gateway.createConnectionServer();
|
|
157
|
+
await server.connect(transport);
|
|
158
|
+
injectTransportTrace(transport, sessionId || undefined, traceId);
|
|
159
|
+
await transport.handleRequest(request.raw, reply.raw, request.body);
|
|
160
|
+
// After handleRequest completes, sessionId is available if init succeeded
|
|
161
|
+
const newSessionId = transport.sessionId;
|
|
162
|
+
if (newSessionId) {
|
|
163
|
+
sessionManager.addSession(newSessionId, transport, server);
|
|
164
|
+
}
|
|
165
|
+
if (getGatewayDebugSetting()) {
|
|
166
|
+
logger.debug(`Created new session ${newSessionId} via MCP POST`, LOG_MODULES.GATEWAY);
|
|
167
|
+
}
|
|
65
168
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
id: null
|
|
83
|
-
}));
|
|
84
|
-
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
171
|
+
const errorStack = error instanceof Error ? error.stack : 'No stack available';
|
|
172
|
+
logger.error(`Error handling MCP request: ${errorMessage}`, LOG_MODULES.GATEWAY);
|
|
173
|
+
logger.error(`Full error stack: ${errorStack}`, LOG_MODULES.GATEWAY);
|
|
174
|
+
logger.error(`Request body: ${stringifyForLogging(request.body)}`, LOG_MODULES.GATEWAY);
|
|
175
|
+
if (!reply.raw.headersSent) {
|
|
176
|
+
reply.raw.writeHead(500, { 'Content-Type': 'application/json' });
|
|
177
|
+
reply.raw.end(JSON.stringify({
|
|
178
|
+
jsonrpc: '2.0',
|
|
179
|
+
error: { code: -32000, message: 'Internal Server Error' },
|
|
180
|
+
id: null
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
85
185
|
}
|
|
86
|
-
};
|
|
87
|
-
// Handle root /mcp endpoint (GET for SSE, POST for messages)
|
|
88
|
-
fastify.all('/mcp', {
|
|
89
|
-
bodyLimit: 10 * 1024 * 1024, // 10MB limit
|
|
90
|
-
preHandler: (request, _reply, done) => {
|
|
91
|
-
// Ensure we don't parse the body for SSE (GET) requests
|
|
92
|
-
if (request.method === 'GET') {
|
|
93
|
-
request.body = null;
|
|
94
|
-
}
|
|
95
|
-
done();
|
|
96
|
-
},
|
|
97
|
-
handler: handleMcpRequest
|
|
98
186
|
});
|
|
99
|
-
//
|
|
187
|
+
// Subpath fallback
|
|
100
188
|
fastify.all('/mcp/*', {
|
|
101
|
-
bodyLimit: 10 * 1024 * 1024,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
189
|
+
bodyLimit: 10 * 1024 * 1024,
|
|
190
|
+
handler: async (request, reply) => {
|
|
191
|
+
const sessionId = request.headers[MCP_SESSION_ID] || '';
|
|
192
|
+
const traceId = randomUUID();
|
|
193
|
+
await runWithRequestContext({ sessionId: sessionId || undefined, traceId }, async () => {
|
|
194
|
+
logRequest(request);
|
|
195
|
+
reply.header('Content-Type', 'application/json');
|
|
196
|
+
wrapReplyForDebug(reply, sessionId);
|
|
197
|
+
reply.hijack();
|
|
198
|
+
const sessionMode = resolveSessionMode(request);
|
|
199
|
+
if (sessionMode === SESSION_MODE_STATELESS) {
|
|
200
|
+
if (request.method !== 'POST') {
|
|
201
|
+
sendError(reply, 405, -32000, 'GET/DELETE not supported in stateless session mode');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const { transport } = await createPerRequestTransport();
|
|
206
|
+
injectTransportTrace(transport, undefined, traceId);
|
|
207
|
+
await transport.handleRequest(request.raw, reply.raw, request.body);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
211
|
+
logger.error(`Stateless MCP subpath error: ${msg}`, LOG_MODULES.GATEWAY);
|
|
212
|
+
sendError(reply, 500, -32000, 'Internal Server Error');
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!sessionId) {
|
|
217
|
+
if (!reply.raw.headersSent) {
|
|
218
|
+
reply.raw.writeHead(400, { 'Content-Type': 'application/json' });
|
|
219
|
+
reply.raw.end(JSON.stringify({
|
|
220
|
+
jsonrpc: '2.0',
|
|
221
|
+
error: { code: -32000, message: 'Missing mcp-session-id header' },
|
|
222
|
+
id: null
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const session = sessionManager.getSession(sessionId);
|
|
228
|
+
if (!session) {
|
|
229
|
+
if (!reply.raw.headersSent) {
|
|
230
|
+
reply.raw.writeHead(404, { 'Content-Type': 'application/json' });
|
|
231
|
+
reply.raw.end(JSON.stringify({
|
|
232
|
+
jsonrpc: '2.0',
|
|
233
|
+
error: { code: -32001, message: 'Session not found' },
|
|
234
|
+
id: null
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
sessionManager.updateSessionMethod(sessionId, request.method);
|
|
241
|
+
injectTransportTrace(session.transport, sessionId, traceId);
|
|
242
|
+
await session.transport.handleRequest(request.raw, reply.raw, request.method === 'POST' ? request.body : undefined);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
246
|
+
logger.error(`Error handling MCP subpath request: ${errorMessage}`, LOG_MODULES.GATEWAY);
|
|
247
|
+
if (!reply.raw.headersSent) {
|
|
248
|
+
reply.raw.writeHead(500, { 'Content-Type': 'application/json' });
|
|
249
|
+
reply.raw.end(JSON.stringify({
|
|
250
|
+
jsonrpc: '2.0',
|
|
251
|
+
error: { code: -32000, message: 'Internal Server Error' },
|
|
252
|
+
id: null
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
110
258
|
});
|
|
111
259
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-tools.d.ts","sourceRoot":"","sources":["../../../../../src/api/web/hub-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAqB1C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"hub-tools.d.ts","sourceRoot":"","sources":["../../../../../src/api/web/hub-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAqB1C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,eAAe,iBAmL/D"}
|
|
@@ -166,4 +166,15 @@ export async function webHubToolsRoutes(fastify) {
|
|
|
166
166
|
const allTools = await hubToolsService.listAllTools();
|
|
167
167
|
return allTools;
|
|
168
168
|
});
|
|
169
|
+
// GET /web/hub-tools/search - Search tools across all connected servers
|
|
170
|
+
fastify.get('/web/hub-tools/search', async (request, reply) => {
|
|
171
|
+
const { q } = request.query;
|
|
172
|
+
if (!q || typeof q !== 'string' || q.trim().length === 0) {
|
|
173
|
+
return reply.code(400).send({
|
|
174
|
+
error: 'Bad Request',
|
|
175
|
+
message: 'Query parameter "q" is required'
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return hubToolsService.searchTools(q.trim());
|
|
179
|
+
});
|
|
169
180
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hubManager } from '../../services/hub-manager.service.js';
|
|
2
|
-
import { mcpConnectionManager } from '../../services/
|
|
3
|
-
import { logger } from '../../utils/logger.js';
|
|
2
|
+
import { mcpConnectionManager } from '../../services/connection/index.js';
|
|
3
|
+
import { logger } from '../../utils/logger/index.js';
|
|
4
4
|
import { LOG_MODULES } from '../../utils/logger/log-modules.js';
|
|
5
5
|
import { resolveInstanceConfig } from '../../config/config-migrator.js';
|
|
6
6
|
/**
|