diff --git a/CLAUDE.md b/CLAUDE.md index 0be7e13..aa72877 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,6 +12,7 @@ volontaires ; les références `D1`, `D2`… de ce fichier y renvoient. | Installer, lancer, brancher Claude Desktop | [README.md](README.md) | | Pourquoi le code est ainsi, pièges terrain | [DECISIONS.md](DECISIONS.md) | | Le serveur ne répond pas, lire ses logs | [MONITORING.md](MONITORING.md) | +| Ce qui reste à faire | [ROADMAP.md](ROADMAP.md) | | Accéder aux logs du WMS | [docs/logs.md](docs/logs.md) | | Références EasyWMS (API, entités) | [docs/](docs/) | @@ -273,11 +274,16 @@ printf '%s\n%s\n%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{" ## Points ouverts -- **`select_expression`** : projections en erreur de compilation côté serveur - (D13). Principal irritant restant. -- **Historique des shipment templates** : hors de portée, les logs concernés - n'existent pas sur l'hôte joignable (D16). -- **Logs chargés intégralement en mémoire** : coûteux sur les gros fichiers - ([docs/logs.md](docs/logs.md) §6). -- **Déploiement sur VM par SSH** : non finalisé. L'exécutable est validé, la - configuration SSH reste à faire. +Voir [ROADMAP.md](ROADMAP.md) : lots de correction planifiés, cause racine +commune (résolution `Name` -> `TableName` des entités), et propositions +explicitement écartées. + +⚠️ Deux pièges connus et non encore corrigés, à garder en tête en attendant le +lot 1 : + +- `entity_type` est interpolé sans validation dans `Context.{entity_type}`. Le + nom attendu est le `TableName` de l'API Metadata, pas le nom d'entité de l'AD + (`Container` -> `Containers`, mais `Alias` -> `Alias`). Un mauvais nom donne un + HTTP 500 dont le détail est aujourd'hui perdu. +- `get_entity_metadata` et `list_log_files` sont listés dans `tools/list` mais + non routés dans `src/index.js` : ils renvoient `Unknown tool`. diff --git a/README.md b/README.md index 295c19b..e560ab9 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ C:\WMS\mcp\.env | [CLAUDE.md](CLAUDE.md) | Architecture, inventaire des outils, conventions de code | | [DECISIONS.md](DECISIONS.md) | **Pourquoi** le code est ainsi + pièges vérifiés en production | | [MONITORING.md](MONITORING.md) | Superviser le serveur MCP : logs, token, caches, symptômes | +| [ROADMAP.md](ROADMAP.md) | Travaux planifiés par lot, et ce qui a été écarté | | [docs/logs.md](docs/logs.md) | Accès aux logs du WMS | | [docs/](docs/) | Références EasyWMS (API, entités) | diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..a68d3cd --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,171 @@ +# Roadmap + +Travaux planifiés, par lot. Chaque lot est livrable indépendamment. + +Constats issus de la session de diagnostic du **24/08/2026** (profil `LIMAGRAIN`, +host `10.255.255.2`, tenant `LIMAGRAI2512`), déclenchée par un rapport d'usage +d'une session Cowork. Toutes les anomalies ci-dessous ont été **reproduites** +contre le WMS réel — ce ne sont pas des hypothèses. + +Les décisions actées vivent dans [DECISIONS.md](DECISIONS.md) ; ce fichier ne +contient que ce qui reste à faire. + +--- + +## Cause racine commune + +Le MCP interpole `entity_type` dans `Context.{entity_type}` **sans aucune +validation** (vérifié : aucune liste blanche dans le code). Or le nom attendu +par le contexte de lecture n'est pas le nom d'entité de l'Application +Dictionary. + +L'API Metadata (`GET /Metadata/Entities`, **232 entités**) donne la +correspondance exacte : + +| `Name` (renvoyé par `search_ad_elements`) | `TableName` (attendu par `Context.`) | +|---|---| +| `Container` | `Containers` | +| `Product` | `Products` | +| `ContainerType` | `ContainerTypes` | +| `Alias` | `Alias` — **invariant, pas de pluriel** | +| `Item` | *n'existe pas dans le modèle Reading* | + +Ce n'est donc pas une règle de pluralisation : c'est un mapping, et seul +`TableName` fait foi. `TableName` est unique sur les 232 entités. + +Conséquences déjà constatées : +- une session utilisant les noms de l'AD (singuliers) déclenche un **HTTP 500** + sur chaque requête ; +- la liste d'entités documentée était fausse (`Aliases` n'existe pas, c'est + `Alias`) ; +- le MCP n'expose que 12 entités figées là où l'API en connaît 232. + +--- + +## Lot 1 — Déblocage + +Objectif : rendre le MCP auto-diagnosticable et réparer ce qui est cassé. Ce lot +seul aurait suffi à ce qu'une session se débrouille sans intervention. + +### L1.1 — Remonter le détail des erreurs HTTP + +Aujourd'hui toute erreur d'API se résume à `Request failed with status code 500`. +Or le WMS renvoie déjà le diagnostic complet dans le corps de la réponse : + +```json +{"ClassName":"System.AggregateException","Message":"Compile Error: ... +'ApplicationReadingContext' ne contient pas de définition pour 'Container' ..."} +``` + +Enrichir l'erreur au point de passage unique (`api-service.post` / `.get`) avec : +statut, URL, verbe, payload envoyé, corps de réponse tronqué à ~2000 caractères. + +**Fichier :** `src/services/api-service.js` (catch de `post` et `get`). + +### L1.2 — Fiabiliser le routage des outils + +Deux outils sont listés dans `tools/list` mais ne sont routés vers aucun module, +à cause du routage par préfixe : + +| Outil | Cause | Erreur observée | +|---|---|---| +| `get_entity_metadata` | capté par `startsWith('get_entity_')` avant sa propre branche | `Unknown WMS query tool` | +| `list_log_files` | ne contient pas `_logs` mais `_log_files` | `Unknown tool` | + +Remplacer le routage par préfixe par une **table explicite nom → module**, +construite depuis les `listTools()` de chaque module. Un outil listé mais non +routé devient alors impossible par construction, au lieu d'être rattrapé au cas +par cas. + +**Fichier :** `src/index.js` (handler `tools/call`). + +### L1.3 — Corriger les projections de champs des workflows + +L'API AD renvoie les champs en minuscules (`id`, `name`, `version`, +`applicationName`). Deux endroits supposent une autre forme : + +- `search_workflows` projette `w.Id`, `w.Code`, `w.Name`, `w.Category` → tous + `undefined`, supprimés par `JSON.stringify` → **50 objets vides** pour un + `count` pourtant correct ; +- `workflow-service` lit `w.category || w.Category`, deux clés inexistantes → + `list_workflow_categories` renvoie **0 catégorie** et classe les 4012 + workflows en `Uncategorized`. + +Le champ le plus proche d'une catégorie est `applicationName`, mais il vaut +`EasyWMS` pour tous les workflows : la notion de catégorie n'a **aucun support** +dans les données. Décider en connaissance de cause plutôt que d'inventer une +taxonomie. + +**Fichiers :** `src/tools/workflow-tools.js`, `src/services/workflow-service.js`. + +--- + +## Lot 2 — Correctif de fond + +### L2.1 — Résolution des entités via l'API Metadata + +Accepter `entity_type` au nom d'entité (`Container`) ou au nom de jeu +(`Containers`), insensible à la casse, et émettre `Context.{TableName}`. Cache +identique aux autres (TTL partagé, invalidation au changement de profil). + +Sur nom inconnu, échouer **avant tout appel réseau**, avec un message +actionnable : + +> « Item » n'existe pas dans le modèle Reading. Proches : ItemGroup, StockItem. +> 232 entités disponibles — utilisez `get_entity_metadata` pour la liste. + +Supprime la cause des 500 et débloque 232 entités au lieu de 12. + +### L2.2 — Rejeter les paramètres inconnus + +Le SDK MCP ignore silencieusement les paramètres non déclarés : un appel +`read_recent_logs(lines: 60)` retombe sur le défaut `count = 100` sans le +moindre signal, et l'appelant conclut à un paramètre ignoré. + +Ajouter `additionalProperties: false` aux 23 schémas d'outils. + +C'est le correctif retenu **à la place** d'une uniformisation des noms de +paramètres : renommer casse les usages existants pour un gain cosmétique, alors +que la cause réelle est l'absence de signal. + +--- + +## Lot 3 — Ergonomie et documentation + +### L3.1 — Bornage des sorties volumineuses + +- `get_system_parameters` : ajouter `limit` / `offset`, aujourd'hui absents + (sortie constatée : 70 000 caractères, rejetée par le client). +- `search_logs` : garde-fou de taille. `max_results` existe déjà, mais les + `context_lines` multiplient le volume (88 000 caractères pour 50 résultats). +- Renvoyer `truncated: true` explicitement plutôt que de laisser le client se + faire rejeter. + +### L3.2 — Documentation + +- DECISIONS.md : **D21** la règle `TableName`, **D22** le routage par table + explicite. +- CLAUDE.md : corriger la liste d'entités (`Aliases` → `Alias`) et renvoyer vers + `get_entity_metadata` comme source de vérité. +- `wms://query-examples` : un exemple singulier/pluriel commenté. + +--- + +## Écarté + +| Proposition | Raison | +|---|---| +| Uniformiser les noms de paramètres (`entity_type` / `query` partout) | Casse les usages existants ; les alias de transition doublent la surface à maintenir. La cause réelle est traitée par L2.2. | +| Exposer un `indexStatus` sur `generic_search` | `TotalDocuments: 0` est déjà le signal. Le MCP n'a aucun moyen d'interroger l'état de l'index de recherche. | +| Outil dédié `get_query_syntax_help` | L'information doit se trouver dans le message d'erreur, là où elle est lue (L2.1), pas dans un outil qu'il faut penser à appeler. | + +--- + +## Points ouverts (hors lots) + +- **`select_expression`** : les projections via le paramètre `Select` provoquent + des erreurs de compilation côté serveur (D13). Irritant principal restant. +- **Déploiement SSH sur la VM** : l'exécutable est validé, la configuration SSH + reste à faire. +- **Historique des shipment templates** : hors de portée, les logs concernés + n'existent pas sur l'hôte joignable (D16).