From a1acb781b052d62159425f724f747beebd7dfba5 Mon Sep 17 00:00:00 2001 From: Arthur Ria Date: Tue, 25 Aug 2026 15:16:28 +0200 Subject: [PATCH] L5.3 : champ tool dans toutes les enveloppes d'erreur locales Convention 3 impose { success: false, error, tool }, mais le champ tool n'etait ajoute que par le wrapper de src/index.js. Les enveloppes construites localement dans src/tools/ ne le portaient pas : call_query_api avec query_type: 7 repondait { success: false, error: "query_type invalide : ..." }, sans tool. Le contrat etait donc respecte ou non selon le chemin d'erreur -- alors qu'il est lu par Claude, pas par un humain. Balayage des 8 modules : 15 enveloppes success:false au total, 11 y ont gagne le champ tool (ad-tools, config-tools, wms-query-tools et workflow-tools l'avaient deja via le catch de leur executeTool). Les champs additionnels sont conserves et passent apres tool : warning de resolution d'api-tools, availableCount + hint de get_entity_metadata, listes de profils de profile-tools. Grep de controle -- 15 enveloppes, 0 sans tool : src/tools/ad-tools.js:140 tool: name src/tools/api-tools.js:170 tool: 'call_query_api' src/tools/api-tools.js:217 tool: 'execute_command' src/tools/config-tools.js:179 tool: 'get_system_parameters' src/tools/log-tools.js:116 tool: 'list_log_files' src/tools/log-tools.js:157 tool: 'read_recent_logs' src/tools/log-tools.js:227 tool: 'search_logs' src/tools/metadata-tools.js:110 tool: 'get_entity_metadata' src/tools/metadata-tools.js:154 tool: 'get_entity_metadata' src/tools/metadata-tools.js:197 tool: 'generic_search' src/tools/profile-tools.js:111 tool: 'get_current_wms_profile' src/tools/profile-tools.js:129 tool: 'switch_wms_profile' src/tools/profile-tools.js:159 tool: 'switch_wms_profile' src/tools/wms-query-tools.js:169 tool: name src/tools/workflow-tools.js:117 tool: name Verifie en execution (protocole, LIMAGRAIN sauf mention) -- 11 enveloppes declenchees, toutes avec tool : call_query_api query_type: 7 -> tool: call_query_api get_entity_metadata entite inconnue -> tool + availableCount + hint query_wms_entities entite inconnue -> tool: query_wms_entities get_workflow_details id inconnu -> tool: get_workflow_details get_ad_elements type inconnu -> tool: get_ad_elements switch_wms_profile profil inconnu -> tool + profiles read_recent_logs fichier inexistant -> tool: read_recent_logs list_log_files / search_logs / read_recent_logs sur EUROTRAFIC -> tool, garde SaaS D9 (sans reseau) Trois catch restent couverts statiquement, faute de declencheur : celui d'execute_command (interdit d'appel, il ecrit dans le WMS), celui de generic_search (l'API tolere categorie inexistante comme limit negative : elle repond success), et celui de get_current_wms_profile (inatteignable tant qu'un profil par defaut se charge au demarrage). Baseline preservee : 23 outils, 6 resources. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 6 +++++- ROADMAP.md | 6 ------ src/tools/api-tools.js | 2 ++ src/tools/log-tools.js | 4 +++- src/tools/metadata-tools.js | 5 +++-- src/tools/profile-tools.js | 3 +++ 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d930553..240573c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -106,7 +106,11 @@ serveur au démarrage. [MONITORING.md](MONITORING.md) §2. 3. **Un outil ne plante jamais le serveur.** Toute erreur revient en réponse structurée `{ success: false, error, tool }` avec `isError: true` — le - wrapper est dans le handler `tools/call` de `src/index.js`. + wrapper est dans le handler `tools/call` de `src/index.js`. Les enveloppes + construites **localement** dans `src/tools/` portent le champ `tool` elles + aussi : le wrapper ne les voit pas, et une erreur sans `tool` sort du + contrat. Les champs supplémentaires utiles (`warning` de résolution, + `profiles`, `hint`…) viennent après. 4. **Messages d'erreur actionnables.** Ils sont lus par Claude, pas par un humain : dire quoi faire ensuite (« appelez `switch_wms_profile` », « profils disponibles : … »). diff --git a/ROADMAP.md b/ROADMAP.md index df15cfc..6adfd18 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -96,12 +96,6 @@ rejet client (~70 000 caractères, D24) : | `get_workflow_details(CST_SendRejectContainersToPK)` | ~101 800 | | `call_query_api("Products", query_type: 1, limit: 1)` — **une seule ligne** Writing | **95 288** | -### L5.3 — Champ `tool` absent des erreurs construites localement - -Les `catch` locaux d'`api-tools.js` renvoient `{ success: false, error }` sans -le champ `tool` du contrat (convention 3) — antérieur au lot 4. Balayer tous -les modules d'outils pour le même motif. - ### L5.4 — Rendre les autres applications découvrables depuis `search_workflows` Cas réel (25/08/2026, session Cowork sur LIMAGRAIN) : une session cherchant diff --git a/src/tools/api-tools.js b/src/tools/api-tools.js index d289504..14b6521 100644 --- a/src/tools/api-tools.js +++ b/src/tools/api-tools.js @@ -169,6 +169,7 @@ async function callQueryAPI(args) { { success: false, error: err.message, + tool: 'call_query_api', ...(resolution?.warning ? { warning: resolution.warning } : {}), }, null, @@ -215,6 +216,7 @@ async function executeCommand(args) { { success: false, error: err.message, + tool: 'execute_command', }, null, 2 diff --git a/src/tools/log-tools.js b/src/tools/log-tools.js index c151347..7cc88e5 100644 --- a/src/tools/log-tools.js +++ b/src/tools/log-tools.js @@ -113,7 +113,7 @@ async function listLogFiles() { return { content: [{ type: 'text', - text: JSON.stringify({ success: false, error: err.message }, null, 2), + text: JSON.stringify({ success: false, error: err.message, tool: 'list_log_files' }, null, 2), }], isError: true, }; @@ -156,6 +156,7 @@ async function readRecentLogs(args) { { success: false, error: err.message, + tool: 'read_recent_logs', }, null, 2 @@ -225,6 +226,7 @@ async function searchLogs(args) { { success: false, error: err.message, + tool: 'search_logs', }, null, 2 diff --git a/src/tools/metadata-tools.js b/src/tools/metadata-tools.js index 7fe7d38..9ae1131 100644 --- a/src/tools/metadata-tools.js +++ b/src/tools/metadata-tools.js @@ -109,6 +109,7 @@ async function getEntityMetadata(args) { text: JSON.stringify({ success: false, error: `No entity matching "${entity_name}" found`, + tool: 'get_entity_metadata', availableCount: Array.isArray(entities) ? entities.length : '?', hint: 'Call get_entity_metadata without entity_name to see all entities', }, null, 2), @@ -150,7 +151,7 @@ async function getEntityMetadata(args) { return { content: [{ type: 'text', - text: JSON.stringify({ success: false, error: err.message }, null, 2), + text: JSON.stringify({ success: false, error: err.message, tool: 'get_entity_metadata' }, null, 2), }], isError: true, }; @@ -193,7 +194,7 @@ async function genericSearch(args) { return { content: [{ type: 'text', - text: JSON.stringify({ success: false, error: err.message }, null, 2), + text: JSON.stringify({ success: false, error: err.message, tool: 'generic_search' }, null, 2), }], isError: true, }; diff --git a/src/tools/profile-tools.js b/src/tools/profile-tools.js index 4cd8466..9d0013f 100644 --- a/src/tools/profile-tools.js +++ b/src/tools/profile-tools.js @@ -110,6 +110,7 @@ function getCurrentProfileTool() { text: JSON.stringify({ success: false, error: err.message, + tool: 'get_current_wms_profile', profiles: profileManager.listProfiles(), }, null, 2), }], @@ -127,6 +128,7 @@ function switchProfileTool(args) { text: JSON.stringify({ success: false, error: 'Missing "profile" argument', + tool: 'switch_wms_profile', profiles: profileManager.listProfiles(), }, null, 2), }], @@ -156,6 +158,7 @@ function switchProfileTool(args) { text: JSON.stringify({ success: false, error: err.message, + tool: 'switch_wms_profile', profiles: profileManager.listProfiles(), }, null, 2), }],