Skip to content

Commit 4e17081

Browse files
committed
refactor: add "lazy-loading" logic since the full tool list breaks LLMs
1 parent 17d354d commit 4e17081

File tree

6 files changed

+589
-35
lines changed

6 files changed

+589
-35
lines changed

libraries/hermes/src/MCP/MCPController.ts

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* - Admin authentication required
1111
* - Origin validation for security
1212
* - Automatic admin route to tool conversion
13+
* - Module-based tool organization with lazy loading
1314
*/
1415

1516
import { NextFunction, Request, Response } from 'express';
@@ -18,11 +19,12 @@ import { ConduitGrpcSdk } from '@conduitplatform/grpc-sdk';
1819
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1920
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2021
import { MCPConfig, MCPToolDefinition } from './types.js';
21-
import { ToolRegistry } from './ToolRegistry.js';
22+
import { CORE_MODULE, ToolRegistry } from './ToolRegistry.js';
2223
import { MCP_ERROR_CODES } from './MCPErrors.js';
2324
import { convertConduitRouteToMCPTool } from './RouteToTool.js';
2425
import { ConduitRoute } from '../classes/index.js';
2526
import { isNil } from 'lodash-es';
27+
import { registerMetaTools } from './MetaTools.js';
2628

2729
export class MCPController extends ConduitRouter {
2830
private _mcpServer: McpServer;
@@ -54,6 +56,9 @@ export class MCPController extends ConduitRouter {
5456
this._toolRegistry = new ToolRegistry(grpcSdk);
5557
this._toolRegistry.setMcpServer(this._mcpServer);
5658

59+
// Register meta-tools for module discovery (always enabled)
60+
registerMetaTools(this._toolRegistry);
61+
5762
this.initializeRouter();
5863
}
5964

@@ -137,16 +142,52 @@ export class MCPController extends ConduitRouter {
137142
* Health check endpoint
138143
*/
139144
private handleHealthCheck(req: Request, res: Response) {
145+
const modules = this._toolRegistry.getModules();
140146
res.json({
141147
status: 'healthy',
142148
protocol: 'mcp',
143149
version: this._config.protocolVersion,
144150
serverInfo: this._config.serverInfo,
145-
tools: this._toolRegistry.getToolCount(),
151+
tools: {
152+
total: this._toolRegistry.getToolCount(),
153+
enabled: this._toolRegistry.getEnabledToolCount(),
154+
},
155+
modules: {
156+
total: modules.length,
157+
loaded: this._toolRegistry.getLoadedModules().length,
158+
list: modules,
159+
},
146160
uptime: process.uptime(),
147161
});
148162
}
149163

164+
/**
165+
* Extract module name from route path
166+
* e.g., "/authentication/local/login" -> "authentication"
167+
* e.g., "/storage/file/:id" -> "storage"
168+
* e.g., "/config" -> "core"
169+
*/
170+
private extractModuleFromPath(path: string): string {
171+
// Remove leading slash and split by /
172+
const segments = path.replace(/^\//, '').split('/');
173+
174+
// First segment is the module name
175+
const firstSegment = segments[0];
176+
177+
// If path is just a single segment or empty, use core module
178+
if (!firstSegment || segments.length === 1) {
179+
return CORE_MODULE;
180+
}
181+
182+
// Skip common prefixes that aren't module names
183+
if (firstSegment === 'admin' || firstSegment === 'hook') {
184+
// Use second segment as module
185+
return segments[1] || CORE_MODULE;
186+
}
187+
188+
return firstSegment;
189+
}
190+
150191
/**
151192
* Register a tool with the MCP server
152193
*/
@@ -182,22 +223,26 @@ export class MCPController extends ConduitRouter {
182223
* Register a Conduit admin route as an MCP tool
183224
*/
184225
registerRouteAsTool(route: ConduitRoute): void {
185-
const tool = convertConduitRouteToMCPTool(route, this);
226+
// Extract module from route path
227+
const moduleName = this.extractModuleFromPath(route.input.path);
228+
229+
// Convert route to tool with module info
230+
const tool = convertConduitRouteToMCPTool(route, this, { module: moduleName });
186231
this.registerTool(tool);
187232
}
188233

189234
// Override ConduitRouter methods
190235
protected _refreshRouter() {
191-
// Don't recreate server, just unregister and re-register tools
192-
this._toolRegistry.clearAllTools(); // This now properly removes via handles
236+
// Clear module tools but preserve meta-tools
237+
this._toolRegistry.clearModuleTools();
193238

194239
// Re-register all tools from registered routes
195240
this._registeredRoutes.forEach(route => {
196241
this.registerRouteAsTool(route);
197242
});
198243

199244
ConduitGrpcSdk.Logger.log(
200-
`MCP tools refreshed: ${this._registeredRoutes.size} tools`,
245+
`MCP tools refreshed: ${this._registeredRoutes.size} module tools + meta-tools`,
201246
);
202247
}
203248

@@ -221,6 +266,13 @@ export class MCPController extends ConduitRouter {
221266
this.refreshRouter(); // Triggers _refreshRouter after delay
222267
}
223268

269+
/**
270+
* Get the tool registry (for testing/debugging)
271+
*/
272+
getToolRegistry(): ToolRegistry {
273+
return this._toolRegistry;
274+
}
275+
224276
shutDown() {
225277
ConduitGrpcSdk.Logger.log('Shutting down MCP controller');
226278
super.shutDown();

libraries/hermes/src/MCP/RouteToTool.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ export class RouteToToolConverter {
2626
): MCPToolDefinition {
2727
const {
2828
prefix = 'admin',
29-
includeModule = false,
3029
adminOnly = true,
3130
customDescriptions = {},
31+
module,
3232
} = options;
3333

3434
const toolName = this.generateToolName(routeInfo, prefix);
@@ -45,7 +45,9 @@ export class RouteToToolConverter {
4545
outputSchema,
4646
handler,
4747
adminOnly,
48-
// module: includeModule ? routeInfo.module : undefined,
48+
module,
49+
// Module tools start disabled (lazy loading)
50+
initiallyDisabled: true,
4951
};
5052
}
5153

0 commit comments

Comments
 (0)