L1.2 : route les outils par table explicite nom -> module (D22)

Le routage par préfixe de nom laissait deux outils listés dans
tools/list mais injoignables : get_entity_metadata (capté par
startsWith('get_entity_') avant sa propre branche) et list_log_files
(aucune branche : le nom contient _log_files, pas _logs).

Une table nom d'outil -> module est construite au démarrage depuis les
listTools() des 8 modules de src/tools/. tools/list est servi depuis
cette même table et le dispatch devient un lookup : un outil listé est
un outil routé, par construction. Deux modules déclarant le même nom
font échouer le serveur au démarrage avec un message nommant les deux
modules. Le wrapper d'erreur du handler tools/call est inchangé, les
23 outils gardent leurs noms.

Vérifié contre le WMS réel : get_entity_metadata renvoie 232 entités,
list_log_files renvoie 19 fichiers, tools/list expose toujours 23
outils et chaque nom listé est traité par le executeTool() de son
module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Arthur Ria
2026-08-24 16:43:40 +02:00
parent 3dad5c6088
commit e0bdc1707d
3 changed files with 81 additions and 53 deletions
+41 -42
View File
@@ -62,6 +62,38 @@ const metadataTools = require('./tools/metadata-tools.js');
const configTools = require('./tools/config-tools.js');
const profileTools = require('./tools/profile-tools.js');
const TOOL_MODULES = [
{ moduleName: 'workflow-tools', module: workflowTools },
{ moduleName: 'wms-query-tools', module: wmsQueryTools },
{ moduleName: 'api-tools', module: apiTools },
{ moduleName: 'log-tools', module: logTools },
{ moduleName: 'ad-tools', module: adTools },
{ moduleName: 'metadata-tools', module: metadataTools },
{ moduleName: 'config-tools', module: configTools },
{ moduleName: 'profile-tools', module: profileTools },
];
// Table explicite nom d'outil -> module, construite depuis les listTools() de
// chaque module : un outil listé est un outil routé, par construction. Le
// routage par préfixe de nom laissait des outils listés mais injoignables
// (get_entity_metadata capté par la mauvaise branche, list_log_files capté
// par aucune).
// Un nom déclaré par deux modules est un bug de développement : on échoue au
// démarrage, pas à l'exécution.
const toolRegistry = new Map();
for (const { moduleName, module } of TOOL_MODULES) {
for (const definition of module.listTools()) {
const existing = toolRegistry.get(definition.name);
if (existing) {
throw new Error(
`[Server] Duplicate tool name "${definition.name}" declared by both ` +
`${existing.moduleName} and ${moduleName} — rename one of them`
);
}
toolRegistry.set(definition.name, { moduleName, module, definition });
}
}
// Create MCP Server
const server = new Server(
{
@@ -138,19 +170,10 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
* List all available tools
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
const allTools = [
...workflowTools.listTools(),
...wmsQueryTools.listTools(),
...apiTools.listTools(),
...logTools.listTools(),
...adTools.listTools(),
...metadataTools.listTools(),
...configTools.listTools(),
...profileTools.listTools(),
];
// Servi depuis la table de routage : la liste exposée et le dispatch ne
// peuvent pas diverger.
return {
tools: allTools,
tools: Array.from(toolRegistry.values(), entry => entry.definition),
};
});
@@ -164,37 +187,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
console.error(`[Server] Executing tool: ${name}`);
// Route to the appropriate handler based on tool name
if (name.startsWith('search_workflows') ||
name.startsWith('get_workflow_') ||
name.startsWith('list_workflow_')) {
return await workflowTools.executeTool(name, args);
} else if (name.startsWith('query_wms_') ||
name.startsWith('count_wms_') ||
name.startsWith('get_entity_') ||
name.startsWith('search_wms_')) {
return await wmsQueryTools.executeTool(name, args);
} else if (name.startsWith('call_query_api') ||
name.startsWith('execute_command')) {
return await apiTools.executeTool(name, args);
} else if (name.includes('_logs')) {
return await logTools.executeTool(name, args);
} else if (name.startsWith('get_application_') ||
name.startsWith('get_ad_') ||
name.startsWith('search_ad_') ||
name.startsWith('list_ad_')) {
return await adTools.executeTool(name, args);
} else if (name === 'get_entity_metadata' || name === 'generic_search') {
return await metadataTools.executeTool(name, args);
} else if (name === 'get_system_parameters') {
return await configTools.executeTool(name, args);
} else if (name === 'list_wms_profiles' ||
name === 'get_current_wms_profile' ||
name === 'switch_wms_profile') {
return await profileTools.executeTool(name, args);
} else {
throw new Error(`Unknown tool: ${name}`);
const entry = toolRegistry.get(name);
if (!entry) {
throw new Error(
`Unknown tool: ${name}. Available tools: ${Array.from(toolRegistry.keys()).join(', ')}`
);
}
return await entry.module.executeTool(name, args);
} catch (error) {
console.error(`[Server] Error executing tool ${name}:`, error.message);
return {