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 <noreply@anthropic.com>
This commit is contained in:
Arthur Ria
2026-08-25 15:16:28 +02:00
parent 54a6849563
commit a1acb781b0
6 changed files with 16 additions and 10 deletions
+5 -1
View File
@@ -106,7 +106,11 @@ serveur au démarrage.
[MONITORING.md](MONITORING.md) §2. [MONITORING.md](MONITORING.md) §2.
3. **Un outil ne plante jamais le serveur.** Toute erreur revient en réponse 3. **Un outil ne plante jamais le serveur.** Toute erreur revient en réponse
structurée `{ success: false, error, tool }` avec `isError: true` — le 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 4. **Messages d'erreur actionnables.** Ils sont lus par Claude, pas par un
humain : dire quoi faire ensuite (« appelez `switch_wms_profile` », « profils humain : dire quoi faire ensuite (« appelez `switch_wms_profile` », « profils
disponibles : … »). disponibles : … »).
-6
View File
@@ -96,12 +96,6 @@ rejet client (~70 000 caractères, D24) :
| `get_workflow_details(CST_SendRejectContainersToPK)` | ~101 800 | | `get_workflow_details(CST_SendRejectContainersToPK)` | ~101 800 |
| `call_query_api("Products", query_type: 1, limit: 1)`**une seule ligne** Writing | **95 288** | | `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` ### L5.4 — Rendre les autres applications découvrables depuis `search_workflows`
Cas réel (25/08/2026, session Cowork sur LIMAGRAIN) : une session cherchant Cas réel (25/08/2026, session Cowork sur LIMAGRAIN) : une session cherchant
+2
View File
@@ -169,6 +169,7 @@ async function callQueryAPI(args) {
{ {
success: false, success: false,
error: err.message, error: err.message,
tool: 'call_query_api',
...(resolution?.warning ? { warning: resolution.warning } : {}), ...(resolution?.warning ? { warning: resolution.warning } : {}),
}, },
null, null,
@@ -215,6 +216,7 @@ async function executeCommand(args) {
{ {
success: false, success: false,
error: err.message, error: err.message,
tool: 'execute_command',
}, },
null, null,
2 2
+3 -1
View File
@@ -113,7 +113,7 @@ async function listLogFiles() {
return { return {
content: [{ content: [{
type: 'text', 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, isError: true,
}; };
@@ -156,6 +156,7 @@ async function readRecentLogs(args) {
{ {
success: false, success: false,
error: err.message, error: err.message,
tool: 'read_recent_logs',
}, },
null, null,
2 2
@@ -225,6 +226,7 @@ async function searchLogs(args) {
{ {
success: false, success: false,
error: err.message, error: err.message,
tool: 'search_logs',
}, },
null, null,
2 2
+3 -2
View File
@@ -109,6 +109,7 @@ async function getEntityMetadata(args) {
text: JSON.stringify({ text: JSON.stringify({
success: false, success: false,
error: `No entity matching "${entity_name}" found`, error: `No entity matching "${entity_name}" found`,
tool: 'get_entity_metadata',
availableCount: Array.isArray(entities) ? entities.length : '?', availableCount: Array.isArray(entities) ? entities.length : '?',
hint: 'Call get_entity_metadata without entity_name to see all entities', hint: 'Call get_entity_metadata without entity_name to see all entities',
}, null, 2), }, null, 2),
@@ -150,7 +151,7 @@ async function getEntityMetadata(args) {
return { return {
content: [{ content: [{
type: 'text', 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, isError: true,
}; };
@@ -193,7 +194,7 @@ async function genericSearch(args) {
return { return {
content: [{ content: [{
type: 'text', 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, isError: true,
}; };
+3
View File
@@ -110,6 +110,7 @@ function getCurrentProfileTool() {
text: JSON.stringify({ text: JSON.stringify({
success: false, success: false,
error: err.message, error: err.message,
tool: 'get_current_wms_profile',
profiles: profileManager.listProfiles(), profiles: profileManager.listProfiles(),
}, null, 2), }, null, 2),
}], }],
@@ -127,6 +128,7 @@ function switchProfileTool(args) {
text: JSON.stringify({ text: JSON.stringify({
success: false, success: false,
error: 'Missing "profile" argument', error: 'Missing "profile" argument',
tool: 'switch_wms_profile',
profiles: profileManager.listProfiles(), profiles: profileManager.listProfiles(),
}, null, 2), }, null, 2),
}], }],
@@ -156,6 +158,7 @@ function switchProfileTool(args) {
text: JSON.stringify({ text: JSON.stringify({
success: false, success: false,
error: err.message, error: err.message,
tool: 'switch_wms_profile',
profiles: profileManager.listProfiles(), profiles: profileManager.listProfiles(),
}, null, 2), }, null, 2),
}], }],