Files
mcp-wms-api/CLAUDE.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

12 KiB

CLAUDE.md

Guide pour Claude Code (claude.ai/code) sur ce dépôt.

Avant de modifier quoi que ce soit, lisez DECISIONS.md. Il consigne les choix d'architecture et les pièges vérifiés sur un WMS réel. La plupart des comportements qui semblent bizarres y sont expliqués et sont volontaires ; les références D1, D2… de ce fichier y renvoient.

Besoin Fichier
Installer, lancer, brancher Claude Desktop README.md
Pourquoi le code est ainsi, pièges terrain DECISIONS.md
Le serveur ne répond pas, lire ses logs MONITORING.md
Ce qui reste à faire ROADMAP.md
Accéder aux logs du WMS docs/logs.md
Références EasyWMS (API, entités) docs/

Objet

Serveur MCP donnant à Claude un accès en lecture à un WMS EasyWMS (Mecalux) pour le debug et l'analyse. Fonctionnel et en service.

Architecture : 100 % API REST, aucun accès base de données (D1).

API Endpoint Usage
Query POST {api}/QueryExecute requêtes LINQ, lignes
Référence complète https://<host>/ApplicationService/Help page d'aide générée du service — la source de vérité sur les champs et les endpoints
Query scalaire POST {api}/QueryScalarExecute Count(), Sum() — à préférer pour « combien »
Command POST {api}/CommandExecute exécution de commandes WMS
Metadata GET {api}/Metadata/Entities, GET {api}/Metadata/EntityProperties entités interrogeables et leurs champs
GenericSearch GET {api}/GenericSearch/Categories, POST {api}/GenericSearch/Search recherche plein texte indexée
Application Dictionary POST {ad}/{Type}/GetByApplication 20 types d'éléments, dont Workflow

{api} = https://<host>/ApplicationService/api et {ad} = https://<host>/AD/api, construits depuis le host du profil actif.

Authentification : OAuth 2.0 avec refresh automatique (D2, et MONITORING.md §4).


Structure

src/
├── index.js                  Point d'entrée MCP : handlers list/read/call, routage
├── config/
│   └── profile-manager.js    Registre multi-profils + bascule runtime
├── resources/                Contexte en lecture seule (6 resources)
│   ├── wms-entities.js       wms://entities
│   ├── entity-schemas.js     wms://entity-schemas
│   ├── query-examples.js     wms://query-examples — exemples LINQ + recettes de diagnostic
│   ├── workflows.js          workflows://overview
│   ├── apis.js               api://catalog
│   └── logs.js               logs://guide — patterns d'erreur et scénarios de debug
├── services/                 Logique métier
│   ├── api-service.js        OAuth + client HTTP + helpers de requête (singleton)
│   ├── workflow-service.js   Workflows, lazy loading + cache
│   ├── ad-service.js         Application Dictionary, 20 types, cache par type
│   ├── wms-query-service.js  Construction d'expressions LINQ
│   └── log-service.js        Lecture et recherche dans les fichiers de logs
└── tools/                    23 outils MCP
    ├── wms-query-tools.js    query_wms_entities, count_wms_entities,
    │                         get_entity_schema, search_wms_data
    ├── api-tools.js          call_query_api, execute_command
    ├── workflow-tools.js     search_workflows, get_workflow_details,
    │                         list_workflow_categories
    ├── ad-tools.js           get_application_summary, get_ad_elements,
    │                         search_ad_elements, get_ad_element_details, list_ad_types
    ├── metadata-tools.js     get_entity_metadata, generic_search
    ├── config-tools.js       get_system_parameters
    ├── profile-tools.js      list_wms_profiles, get_current_wms_profile,
    │                         switch_wms_profile
    └── log-tools.js          read_recent_logs, list_log_files, search_logs

scripts/
├── test-connection.js        Smoke test de connectivité (npm test)
└── test-ad-api.ps1           Validation curl des endpoints AD (credentials en paramètres)

docs/
└── reference-queries-api.php Client PHP d'origine — source des patterns d'API.
                              ⚠️ utilise QueryType 1 : ne pas recopier (D3)

Routage. src/index.js route les appels d'outils par préfixe de nom (name.startsWith('query_wms_'), name.includes('_logs'), …). En ajoutant un outil, vérifiez que son nom tombe dans la bonne branche — sinon il apparaîtra dans tools/list mais renverra Unknown tool.


Conventions non négociables

  1. console.error() uniquement. stdout est réservé au JSON MCP ; toute écriture y casse la session Claude Desktop (D6).
  2. Préfixer les logs par composant : [Server], [API], [Profile], [Workflow], [AD], [Logs], [<X>Tools]. Le tableau complet est dans MONITORING.md §2.
  3. Un outil ne plante jamais le serveur. Toute erreur revient en réponse structurée { success: false, error, tool } avec isError: true — le wrapper est dans le handler tools/call de src/index.js.
  4. Messages d'erreur actionnables. Ils sont lus par Claude, pas par un humain : dire quoi faire ensuite (« appelez switch_wms_profile », « profils disponibles : … »).
  5. Aucun accès base de données (D1).
  6. 1000 lignes maximum par requête, timeout 30 s (MAX_QUERY_ROWS, QUERY_TIMEOUT).
  7. Pas de concaténation LINQ à partir d'entrées utilisateur quand un filtre côté JS suffit (D11).

Multi-profils

Un même serveur dessert plusieurs backends WMS. Un profil = un host + des credentials + un tenant (D8).

Déclaration dans .env :

WMS_PROFILES=AD,LIMAGRAIN,EUROTRAFIC
DEFAULT_WMS_PROFILE=LIMAGRAIN

AD_HOST=10.255.255.2
AD_USERNAME=AD_PASSWORD=AD_TENANT=AD
AD_SAAS=false

Réglages partagés par tous les profils : WMS_API_AUTH, WMS_APPLICATION, WMS_API_PATH, WMS_TOKEN_PATH, WORKFLOW_API_PATH, LOGS_PATH, et les variables de cache / token / requêtes. Seul le host varie : les URL sont assemblées en https://<HOST><PATH>.

<NOM>_SAAS=true → WMS cloud : les outils de log échouent avec un message explicite (D9). Par défaut false.

LOGS_PATH accepte le placeholder {host}, substitué par le host du profil actif à chaque appel.

Au runtime. profile-manager est un singleton d'état global. Les services s'abonnent via onSwitch() pour invalider ce qui dépend du tenant :

Service Réaction
api-service resetToken()
workflow-service clearCache()
ad-service invalidateCache()

N'invalidez jamais ces caches à la main depuis un autre module — l'abonnement suffit (D8). Tout nouveau service portant un état lié au tenant doit s'abonner.

Sans profil actif, getCurrent() lève une erreur qui énumère les profils disponibles : c'est ainsi que Claude sait appeler switch_wms_profile.


Caches

Deux caches, TTL commun WORKFLOW_CACHE_TTL (3 600 000 ms), chargement paresseux, vidés à chaque bascule de profil (D10).

Cache Granularité Pagination
workflow-service global (~3 700 workflows) WORKFLOW_PAGE_SIZE, 5000
ad-service un par type (20 types) AD_ELEMENT_TYPES : View 200, Workflow 5000, Resource 15000, autres 100000

Les tailles de page par type viennent de l'observation des timeouts serveur — ne les augmentez pas à l'aveugle.

get_application_summary expose l'état des caches sans redémarrage.


Écrire une requête WMS

await apiService.executeQuery(
  'Context.OutboundOrders.Where(z => z.OutboundOrderStatus == "Release").OrderBy(z => z.Id)',
  { take: 100 }
);

Répartition entre l'expression et les options — c'est la source d'erreur la plus fréquente :

Élément
Where dans l'Expression
OrderBy dans l'Expressionobligatoire dès qu'on utilise take
Take / Skip / Select paramètres d'API, pas dans l'expression

Autres règles :

  • QueryType: 0 (Reading), jamais 1 : les statuts sont alors des chaînes (D3).
  • Pas de date relative. DateTime.Now, DateTime.Today, AddDays() ne sont pas traduisibles : écrire new DateTime(2026, 8, 1) (D12).
  • select_expression est instable : les projections via le paramètre Select provoquent des erreurs de compilation. Interroger les lignes complètes (D13).
  • Pour compter, utiliser count_wms_entities (QueryScalarExecute), pas un query suivi d'un .length.
  • executeCommand prend le nom de commande tel quel : ajouter le suffixe d'assembly provoque une FileLoadException (D14).

_parseQueryResponse() gère les deux formes de réponse (tableau plat, ou { Table: { Columns, Rows } }) — ne réimplémentez pas ce décodage ailleurs.


Entités et éléments AD

Entités interrogeables (Query API) : Products, Containers, Accounts, Suppliers, Kits, Aliases, Tasks, Stocks, ProductLocations, InboundOrders, Receptions, OutboundOrders. La liste faisant foi s'obtient par get_entity_metadata (API Metadata) — le catalogue de la resource wms://entities est un raccourci de confort, pas la référence.

Application Dictionary : 20 types, ~38 800 éléments. Resource (29 374) est de loin le plus lourd ; 3 types sont valides mais vides (Dashboard, TimelineTemplate, Toggle). WorkflowAction et WritingModel ont été retirés — 404 (D17). Détail : docs/ad-api-validation.md.

Paramètres système : pas d'entité CommandParameterData. La configuration se lit dans Parameter (+ DefaultValue) et ParamValue (surcharges par entrepôt, jointure sur ParameterId), fusionnées côté JS par get_system_parameters (D11).


Commandes

npm start                  # lancer le serveur (stdio)
npm test                   # smoke test du profil actif — lecture seule
npm test -- LIMAGRAIN      # smoke test d'un profil précis
npm test -- --all          # tous les profils
npm run build              # dist/wms-mcp-server.exe (node22-win-x64)

Le smoke test vérifie OAuth, QueryExecute, QueryScalarExecute et l'API AD, et sort en code 1 au moindre échec.

Validation des endpoints AD (PowerShell, credentials en paramètres) :

powershell -ExecutionPolicy Bypass -File scripts/test-ad-api.ps1 -WmsHost 10.255.255.2 -Username user -Password '***' -Tenant AD

Ajouter un outil

  1. Déclarer le schéma dans listTools() du module src/tools/ concerné.
  2. Traiter le cas dans son executeTool().
  3. Vérifier le routage par préfixe dans src/index.js — ou ajouter une branche.
  4. Logger avec le préfixe du module.
  5. Renvoyer les erreurs, ne pas les lever hors du wrapper.
  6. Tester le handshake complet :
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/list"}' | node src/index.js

Points ouverts

Voir 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.