Files
mcp-wms-api/ROADMAP.md
T
Arthur Ria 86923542fa Roadmap et décisions : exploitation de la référence API du service
Source : la page d'aide générée https://<host>/ApplicationService/Help, qui
documente des champs et des endpoints que le MCP n'utilise pas. Toutes les
affirmations ci-dessous ont été testées contre LIMAGRAI2512.

D3 corrigé — QueryContextType a quatre valeurs, pas deux :
Reading 0 (ApplicationReadingContext), Writing 1 (ApplicationWritingRepository),
DataWarehouse 2 (non configuré sur ce tenant : IDataWarehouse non résolu),
Metrics 3 (ApplicationMetricDataContext, présent, modèle non exploré). Le
message d'erreur nomme le contexte, ce qui donne un moyen rapide de savoir quel
QueryType a servi.

L4.1 étendu aux quatre contextes.

L4.2 tranché sur son point dur : le champ Application ne partitionne pas le
contexte de lecture. Context.AgvTasks répond aussi bien avec Application AGV
qu'avec EasyWMS — le contexte est commun au tenant. La table de résolution du
lot 2 devra donc agréger le Metadata de toutes les applications, mais un
paramètre application sur QueryExecute serait inutile. Les entités CustomApp
restent inatteignables sous les quatre QueryType, au singulier comme au
pluriel, et Metadata renvoie 0 entité pour cette application : ce sont des
définitions EasyBuilder sans projection requêtable. L'API AD est le seul accès
au spécifique client.

Trois chantiers ajoutés :
- L4.3 ClientModule, non renseigné, d'où des requêtes du MCP journalisées sous
  « Client: GNA » et indistinguables du vrai client GNA.
- L4.4 API WorkflowLog (GetInstances, GetLogs, Validate), joignable et
  fonctionnelle, susceptible de remettre en cause D16.
- L4.5 champs inexploités de QueryExecute : Parameters (requêtes paramétrées,
  piste pour D13), CommandTimeout, QueryId + QueryCancel, QueryExecuteStream
  (piste pour L3.1), plus les endpoints event sourcing et les sondes
  healthcheck/ready.

CLAUDE.md pointe désormais vers la page d'aide comme source de vérité.
MONITORING.md documente healthcheck/ready comme sonde légère.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 15:58:28 +02:00

302 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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é.
---
## Lot 4 — Modèle de données et applications
Deux angles morts constatés le 24/08/2026, plus larges que les lots 1 à 3. Les
chiffres ci-dessous sont mesurés sur le tenant `LIMAGRAI2512`.
### L4.1 — Le modèle Writing est inatteignable
`QueryType` est figé à `0` (Reading) en dur dans `api-service.js`
(`executeQuery` et `executeScalarQuery`). Or `QueryContextType` a **quatre**
valeurs. Testées une à une :
| Valeur | Contexte | Résultat sur `LIMAGRAI2512` |
|---|---|---|
| `0` | Reading | opérationnel (seul utilisé aujourd'hui) |
| `1` | Writing | **opérationnel**`Context.Products` répond |
| `2` | DataWarehouse | **non configuré** : `Could not resolve serviceType 'IDataWarehouse…'` |
| `3` | Metrics | contexte présent (`ApplicationMetricDataContext`), modèle non exploré |
Exposer `query_type` sur les outils de requête, défaut `0`. Attention : D3 reste
vrai — en Writing les champs de statut sont des **énumérations**, donc
`== "Release"` échoue. La bascule doit être un choix explicite et documenté.
Le contexte `Metrics` mérite une exploration à part : c'est probablement là que
vivent les données agrégées produites par les jobs `MetricGatherer`.
### L4.2 — Une seule application sur neuf est visible
`Application` vient de `WMS_APPLICATION` dans `.env`, **partagé par tous les
profils**, sans surcharge par appel ni paramètre d'outil. Le MCP n'interroge donc
jamais que `EasyWMS`.
`POST /AD/api/Application/GetAll` en déclare **9** :
| Application | Workflows | Queries | Entities |
|---|---:|---:|---:|
| EasyWMS | 4012 | 2239 | 338 |
| **CustomApp** | **153** | **54** | **11** |
| AGV | 71 | 14 | 5 |
| Notifications | 26 | 35 | 24 |
| GalileoFaults | 9 | 20 | 24 |
| Common | 1 | 7 | 25 |
| SmartUI, User, WarehouseWebDesigner | 0 | 08 | 0 |
**CustomApp porte le spécifique client** — ses workflows sont préfixés `CST_`
(`CST_SendRejectContainersToPK`, `CST_Task`, `CST_Container`…). C'est
précisément ce qu'on cherche en debug, et c'est aujourd'hui invisible. Au total
**260 workflows et ~130 queries** hors périmètre.
Deux chantiers de difficulté très différentes :
**API AD — simple.** L'application est un champ du payload
(`[application, tenant, pageSize, offset]`). Vérifié : `["CustomApp", tenant,
5, 0]` sur `/Workflow/GetByApplication` renvoie bien les workflows `CST_`. Il
suffit d'un paramètre `application` sur les outils AD et workflow, avec une clé
de cache incluant l'application (sinon un cache pollué mélange les
applications).
**QueryExecute — tranché : le champ `Application` ne partitionne rien.**
`Context.AgvTasks` (entité de l'application AGV) répond aussi bien avec
`Application: "AGV"` qu'avec `Application: "EasyWMS"`. Le contexte de lecture est
**commun au tenant** : toutes les applications y déversent leurs entités.
Conséquence pour L2.1 : la table de résolution doit **agréger le Metadata de
toutes les applications** (`GET /Metadata/Entities?applicationName=…` par
application, 232 + 20 + 6 + …), et non se limiter à `EasyWMS`. Inutile en
revanche d'ajouter un paramètre `application` à `QueryExecute` : il ne changerait
rien.
**Les entités `CustomApp` ne sont interrogeables dans aucun contexte.** Les 11
entités `CST_` ont été testées sous les quatre `QueryType`, au singulier et au
pluriel : échec partout, et `Metadata/Entities` comme `Metadata/EntitiesAll`
renvoient **0 entité** pour `CustomApp`. Aucune n'est marquée
`isDataWarehouse`. Ce sont des définitions EasyBuilder (`FromMetadata: false`)
sans projection dans un contexte requêtable.
**L'API AD reste donc le seul accès au spécifique client** — ce qui rend le
paramètre `application` sur les outils AD et workflow d'autant plus utile.
### L4.3 — Identifier le MCP dans les logs du WMS
`QueryExecute` accepte un champ **`ClientModule`** que le MCP n'envoie pas.
Résultat : ses requêtes apparaissent dans les logs du WMS sous
`Execute error. Client: GNA` — le client OAuth partagé — donc indistinguables de
celles du vrai client GNA.
Renseigner `ClientModule` (`"MCP-WMS"` ou le nom du profil actif) rend chaque
requête du MCP traçable côté serveur. Vérifié : le champ est accepté.
### L4.4 — Historique d'exécution des workflows par API
`ApplicationService` expose une API `WorkflowLog` que le MCP n'utilise pas :
| Endpoint | Usage |
|---|---|
| `GET /WorkflowLog/GetInstances?processDefinitionId=&skip=&take=&startDateFrom=&startDateTo=` | instances d'un workflow sur une plage de dates, filtrables par attribut |
| `GET /WorkflowLog/GetInstance?processId=` | une instance |
| `GET /WorkflowLog/GetLogs?processId=&skip=&take=&logDateFrom=&logDateTo=` | journal d'exécution d'une instance |
| `GET /WorkflowLog/Validate?applicationName=&processDefinitionId=` | validation d'une définition |
Endpoints joignables et fonctionnels — `Validate` renvoie
`{"Success":true,"ErrorMessage":null,"Warnings":[]}`. `GetInstances` répond `[]`
sur le workflow testé : à confirmer sur un workflow ayant réellement tourné, la
journalisation n'étant pas forcément active partout.
**Peut remettre en cause D16** (historique des shipment templates jugé hors de
portée faute de logs fichier) : si l'historique d'exécution est disponible par
API, la conclusion change. À vérifier avant d'écrire quoi que ce soit.
### L4.5 — Champs de `QueryExecute` inexploités
La référence de l'API documente des champs que le MCP n'envoie jamais :
| Champ | Intérêt |
|---|---|
| `Parameters` | requêtes **paramétrées** (dictionnaire `nom -> {TypeName, Value}`) — supprimerait toute concaténation de chaîne dans les filtres, et pourrait débloquer D13 (`Select`) |
| `CommandTimeout` | timeout par requête, au lieu du timeout HTTP global de 30 s |
| `QueryId` + `POST /QueryCancel` | annulation d'une requête longue |
| `POST /QueryExecuteStream` | résultats en flux — piste sérieuse pour L3.1 (sorties volumineuses) |
Autres endpoints jamais utilisés, à évaluer : `QueryEvents`, `QueryCommands`,
`QueryCorrelationEvents`, `QuerySnapshots` (event sourcing — utile en debug),
`GET /Metadata/Commands|Events|Aggregates` et leurs variantes `…All`,
`GET /configuration/applications` (liste les applications **avec leur version**,
plus riche que `/AD/api/Application/GetAll`), `GET /healthcheck?tenantCode=` et
`GET /ready?tenantCode=` (sondes de disponibilité, répondent 200).
---
---
## É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).