Mesures rafraîchies du 25/08/2026 : Writing (QueryType 1) répond 1 ligne sur Context.Products, Metrics (3) existe sans Products, CustomApp renvoie ses workflows CST_ sous la clé entities de GetByApplication. Découverte consignée en L4.0 : la resource api://catalog documente un exemple QueryType: 1 (le piège D3), un Select dans l'expression et l'entité fantôme Aliases. D25 (rapport D3/query_type) et D26 (clés de cache par application) réservées. L4.3-L4.5 et l'exploration Metrics explicitement hors périmètre. Le point délicat resolver/Writing est cadré : nom inconnu du Reading transmis tel quel avec warning quand query_type != 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
13 KiB
Passation — lot 4 (L4.0, L4.1, L4.2)
Tu travailles sur mcp-wms-api : un serveur MCP (Node.js, CommonJS, stdio) qui
donne à Claude un accès en lecture à un WMS EasyWMS (Mecalux) via ses API REST.
Lis ../CLAUDE.md et ../DECISIONS.md avant de
toucher au code.
Mission : trois blocs de ../ROADMAP.md —
corriger la resource api://catalog qui enseigne le piège D3 (L4.0),
exposer query_type sur les outils de requête (L4.1), et rendre les
applications autres qu'EasyWMS accessibles via un paramètre application
sur les outils AD et workflow (L4.2).
Hors périmètre :
- L4.3 (identifier le MCP dans les logs) : dépend d'une configuration côté WMS, hors de portée d'une session de codage.
- L4.4 (API WorkflowLog) et l'exploration du contexte Metrics : ce sont des investigations, elles feront l'objet d'une phase séparée dont le livrable sera un rapport, pas du code. N'y touche pas.
- L4.5 (
Parameters,QueryExecuteStream…) : consigné, pas ce lot. - Aucun nouvel outil : le compte reste à 23. Les deux correctifs sont des paramètres sur des outils existants.
- Ne pousse rien (
git pushinterdit), ne touche pas au.env, n'appelle jamaisexecute_command(il écrit dans le WMS).
Contexte matériel
- Profil de travail :
LIMAGRAIN(par défaut), host10.255.255.2, tenantLIMAGRAI2512. - Le profil
ADest cassé et c'est diagnostiqué — ne le réinvestigue pas (tenant introuvable côté STS, point ouvert de la ROADMAP).npm test -- --alléchoue sur AD ; la baseline se mesure avecnpm test(profil par défaut), attendu 4/4, code de sortie 0. - Baseline protocolaire à préserver : 23 outils, 6 resources, aucune écriture sur stdout hors JSON-RPC.
Handshake + comptages :
printf '%s\n%s\n%s\n%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' '{"jsonrpc":"2.0","method":"notifications/initialized"}' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' '{"jsonrpc":"2.0","id":3,"method":"resources/list"}' | node src/index.js 2>/dev/null | node -e "let b='';process.stdin.on('data',d=>b+=d).on('end',()=>{for(const l of b.split('\n').filter(Boolean)){const m=JSON.parse(l);if(m.id===2)console.log('tools:',m.result.tools.length);if(m.id===3)console.log('resources:',m.result.resources.length);}});"
Appeler un outil via le protocole (la seule preuve qu'un outil marche) :
printf '%s\n%s\n%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' '{"jsonrpc":"2.0","method":"notifications/initialized"}' '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"NOM","arguments":{}}}' | node src/index.js 2>/dev/null
Sonder le WMS directement (court-circuite les outils) :
node -e "
require('dotenv').config();
const pm=require('./src/config/profile-manager'); pm.loadProfiles();
const api=require('./src/services/api-service').getInstance();
(async()=>{ /* … */ })();"
Contraintes non négociables
console.error()uniquement — une écriture sur stdout casse Claude Desktop (D6).- Un outil ne plante jamais le serveur : erreurs en réponse structurée
{ success: false, error, tool },isError: true(wrapper desrc/index.js). - Messages d'erreur actionnables.
- Aucun accès base de données (D1).
- D23 : le wrapper valide les noms de paramètres et les requis
contre les schémas — déclare
query_typeetapplicationdans lesinputSchema, sinon le wrapper les rejettera. Le wrapper ne valide pas les valeurs : les gardes de valeur (query_typehors 0-3, etc.) vivent dans le code de l'outil. - D8 : tout état lié au tenant s'invalide par
profileManager.onSwitch(), jamais à la main depuis un autre module. Les caches modifiés en L4.2 restent abonnés. - D24 : toute sortie potentiellement volumineuse est bornée et signalée
(
truncated/hint). Les pages de workflows CustomApp (153) tiennent largement ; ne l'oublie pas si tu exposes des listes plus larges.
Numéros de décision réservés : D25 = exposition de query_type (son
rapport à D3), D26 = paramètre application et clés de cache. Vérifie que
D24 est bien la dernière décision avant d'écrire.
Phase 0 — Confirmer les mesures
Toutes rejouées le 25/08/2026 (LIMAGRAI2512) par la sonde directe. À confirmer, pas à réinvestiguer :
| Sonde | Constaté le 25/08 |
|---|---|
POST /QueryExecute {Application:'EasyWMS', QueryType:1, Expression:'Context.Products.OrderBy(z => z.Id)', Take:1} |
OK, 1 ligne — le modèle Writing répond |
Même appel avec QueryType:3 |
HTTP 500 : 'ApplicationMetricDataContext' ne contient pas de définition pour 'Products' — le contexte Metrics existe, son modèle est autre (ne l'explore pas, hors périmètre) |
QueryType:2 |
non configuré sur ce tenant (Could not resolve serviceType 'IDataWarehouse…', mesure du 24/08) |
POST /Workflow/GetByApplication payload ['CustomApp', tenant, 5, 0] (API AD) |
5 workflows sous la clé entities de la réponse : CST_SendRejectContainersToPK, Helper_ContainerByCode, … |
Points déjà tranchés, ne les réinvestigue pas (mesures des 24-25/08 dans la ROADMAP) :
- Le champ
ApplicationdeQueryExecutene partitionne rien (contexte commun au tenant) : inutile de le paramétrer côté requêtes. - Les 11 entités
CustomAppne sont requêtables dans aucun contexte — l'API AD est le seul accès au spécifique client. ClientModuleest sans effet (L4.3).
L4.0 — Corriger la resource api://catalog
Problème. src/resources/apis.js (resource lue par les sessions Claude)
documente un exemple QueryExecute avec "QueryType": 1 — exactement ce que
D3 interdit de recopier —, un .Select(z => z) dans l'expression (contraire à
la répartition expression/options), et l'entité fantôme Aliases (corrigée
partout ailleurs au lot 2).
À faire. Exemple avec QueryType: 0 et expression sans Select,
Aliases → Alias, renvoi vers get_entity_metadata comme source de vérité
sur les entités. Relis toute la resource pendant que tu y es — signale (sans
forcément corriger) toute autre affirmation contredite par DECISIONS.md.
Vérification attendue : lire api://catalog via resources/read en
protocole ; la sortie ne contient plus ni "QueryType": 1 ni Aliases.
L4.1 — Exposer query_type sur les outils de requête
Problème. QueryType est figé à 0 en dur à deux endroits :
src/services/api-service.js:333 (executeQuery) et :370
(executeScalarQuery). Le modèle Writing — opérationnel, mesuré — est
inatteignable.
À faire.
- Paramètre
query_type(entier, défaut0) sur trois outils :call_query_api,query_wms_entities,count_wms_entities. Pas surget_entity_schemanisearch_wms_data— ils restent des raccourcis Reading. - Garde de valeur dans le code (le wrapper D23 ne valide pas les
valeurs) : hors
0..3→ erreur locale actionnable avant tout réseau, nommant les quatre contextes.2et3sont transmis tels quels : le WMS répond, et depuis L1.1 son diagnostic remonte entier. - D3 reste la règle par défaut : en Writing les statuts sont des
énumérations,
== "Release"échoue. La bascule est un opt-in explicite — les descriptions d'outils doivent porter l'avertissement. Acte le rapport D3/query_typeen D25. - Interaction avec le resolver (attention, c'est le point délicat) : la
table de résolution est construite sur le Metadata Reading. Quand
query_type != 0: un nom qui se résout se résout normalement (Productsmarche en Writing, mesuré) ; un nom inconnu du Reading ne doit pas être bloqué en dur — passe-le tel quel avec unwarningdans la réponse (même mécanique que le repli « Metadata injoignable » existant), car le modèle Writing/Metrics peut contenir des entités hors Reading.
Vérification attendue (protocole, LIMAGRAIN) :
call_query_api("Products", query_type: 1, limit: 1)→ succès, 1 ligne.call_query_api("Products", query_type: 3)→ erreur structurée contenantApplicationMetricDataContext.call_query_api("Products", query_type: 7)→ erreur locale avant réseau (aucun[API] POSTdans stderr), nommant les valeurs valides.query_wms_entities("Container", limit: 1)sansquery_type→ strictement le comportement d'aujourd'hui (résoluContainers, Reading).- Un nom inconnu avec
query_type: 1→ transmis tel quel avecwarning, pas d'échec local. count_wms_entities("Product", query_type: 1)→ un nombre (~51 160).
L4.2 — Paramètre application sur les outils AD et workflow
Problème. application vient du profil (WMS_APPLICATION, partagé) :
src/services/ad-service.js:85 et src/services/workflow-service.js:50. Le
MCP n'interroge donc jamais que EasyWMS. Or CustomApp porte le spécifique
client (153 workflows CST_* sur ce tenant) — précisément ce qu'on cherche
en debug — et 9 applications sont déclarées (POST /AD/api/Application/GetAll,
payload null).
À faire.
- Paramètre
application(défaut : l'application du profil, donc comportement inchangé sans lui) sur :get_ad_elements,search_ad_elements,get_ad_element_details,search_workflows,get_workflow_details,list_workflow_categories. - Clés de cache :
ad-servicepasse de « un cache par type » à « un cache par (application, type) » ;workflow-servicede « un cache global » à « un cache par application ». Sans ça, un appel CustomApp pollue le cache EasyWMS. L'abonnementonSwitch()continue d'invalider tout (D8). Acte le contrat en D26. get_application_summarydoit refléter les nouvelles clés (état par application et par type) sans exploser en volume (D24).list_workflow_categories: adosse la liste àApplication/GetAll(9 applications, comptes réels par application) plutôt qu'auxapplicationNamedu seul cache EasyWMS. La note de L1.3 reste vraie — il n'existe pas de champ catégorie ; la réponse liste des applications et le dit. Le paramètrecategorydesearch_workflows(filtre surapplicationName) doit rester cohérent avec le nouveau paramètreapplication— documente leur articulation dans les descriptions, ne casse ni l'un ni l'autre.- Pente naturelle interdite : ne précharge pas les 9 applications (le type
Resourcepèse 29 374 éléments sur la seule EasyWMS). Le chargement reste paresseux, par application effectivement demandée.
Vérification attendue (protocole, LIMAGRAIN) :
search_workflows("CST_", application: "CustomApp")→ objets peuplés (CST_SendRejectContainersToPK…).get_ad_elements("Workflow", application: "CustomApp")→ élémentsCST_*.- Séquence séquentielle EasyWMS → CustomApp → EasyWMS sur
search_workflows: les comptes restent distincts (~4 012 vs 153), aucune pollution croisée ;get_application_summarymontre les deux caches. - Sans paramètre
application→ comportement strictement inchangé. switch_wms_profilepuis retour → caches invalidés (logCache cleared).
Méthode
- Phase 0 d'abord (sondes rejouées telles quelles — la forme
entitiesde la réponse AD est déjà établie, ne la redécouvre pas). - L4.0, puis L4.1, puis L4.2 — chaque bloc vérifié en exécution via le protocole avant de passer au suivant.
- Attention à la concurrence : le serveur traite les
tools/callen concurrence (point ouvert de la ROADMAP). Pour les vérifications qui enchaînent bascules ou séquences de cache, envoie les requêtes séquentiellement (attendre chaque réponse), pas en rafale. - Les schémas changent : reboucler sur les 23 noms de
tools/list(aucunUnknown tool;execute_commandvérifié statiquement, non appelé) et vérifier qu'un paramètre inconnu est toujours rejeté (D23). - Baseline avant/après : handshake (23/6) +
npm test(4/4, exit 0). - « Non résolu » est une réponse acceptable pour une investigation time-boxée ; une hypothèse présentée comme solution ne l'est pas.
Livraison
- Un commit par bloc (L4.0, L4.1, L4.2), messages expliquant le pourquoi, mesures rejouées dans le corps du message.
- Documentation dans les mêmes commits : D25 et D26 dans DECISIONS.md ;
CLAUDE.md — nuancer «
QueryType: 0, jamais 1 » en « défaut 0,query_typeest un opt-in documenté (D25) », documenter le paramètreapplicationet les nouvelles clés de cache dans la section Caches ; ROADMAP.md — retirer L4.0, L4.1, L4.2 (L4.3, L4.4, L4.5 restent). - Ne pousse pas.
.envintact. Toute anomalie hors périmètre découverte en route : dans ROADMAP.md, pas dans le code. - Compte-rendu final : pour chaque bloc, la vérification attendue rejouée et
son résultat mesuré (colle les sorties), plus la baseline finale.
Laisse
handoff-lot4.mden place pour la révision.