Files
mcp-wms-api/ROADMAP.md
T
2026-08-24 17:04:46 +02:00

13 KiB
Raw Blame History

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 ; 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 Aliasinvariant, 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 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 — Erreurs d'authentification muettes

authenticate() (api-service.js) ré-enveloppe l'erreur axios en new Error('Authentication failed: ' + error.message) : le corps de la réponse du STS est perdu, alors qu'il contient le diagnostic complet.

Preuve (24/08/2026, profil AD) : le smoke test affiche seulement Authentication failed: Request failed with status code 400 ; en rejouant la même requête à la main, le corps était {"error":"invalid_request","error_description":"Tenant not found"} — le diagnostic exact, invisible depuis les outils comme depuis le smoke test.

Faire remonter error.response.data dans le message, comme L1.1 l'a fait pour les outils. Même contrainte : ne jamais logguer les credentials.

L3.3 — Documentation

  • DECISIONS.md : D21 la règle TableName (D22, le routage par table explicite, a été livrée avec le lot 1).
  • CLAUDE.md : corriger la liste d'entités (AliasesAlias) 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 2 et 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érationnelContext.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

Les requêtes du MCP apparaissent dans les logs du WMS sous Execute error. Client: GNA — le client OAuth partagé — donc indistinguables de celles du vrai client GNA.

La piste ClientModule est invalidée (mesuré le 24/08/2026, lot 1) : le champ est bien accepté par QueryExecute (pas d'erreur), mais il est sans effet observable. Une requête en échec envoyée avec ClientModule: "MCP-WMS" est tracée Execute error. Client: GNA, et ni MCP-WMS ni ClientModule n'apparaissent nulle part dans ApplicationService.log ni HttpResponseTime.log. Le Client: des logs vient du client OAuth, pas du payload — le champ n'a donc pas été renseigné.

Piste restante (non vérifiée) : un client OAuth dédié au MCP côté EasySTS changerait le Client: des logs, mais suppose une configuration côté WMS.

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)

  • Profil AD : tenant introuvable (mesuré le 24/08/2026). npm test -- --all échoue 0/4 sur ce profil ; le STS de 10.255.255.2 répond 400 {"error":"invalid_request","error_description":"Tenant not found"} pour le tenant AD. L'hôte et le STS fonctionnent (LIMAGRAIN, même hôte, passe 4/4) : c'est la valeur AD_TENANT du .env qui ne correspond plus à un tenant existant. Correction côté propriétaire du dépôt (mettre à jour ou retirer le profil) — pas un bug du code. Le message opaque du smoke test est traité à part (L3.2).
  • 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).