# CLAUDE.md Guide pour Claude Code (claude.ai/code) sur ce dépôt. **Avant de modifier quoi que ce soit, lisez [DECISIONS.md](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](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) | | Superviser le projet, réviser une livraison | [docs/supervision.md](docs/supervision.md) | | Accéder aux logs du WMS | [docs/logs.md](docs/logs.md) | | Références EasyWMS (API, entités) | [docs/](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:///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` | où `{api}` = `https:///ApplicationService/api` et `{ad}` = `https:///AD/api`, construits depuis le host du profil actif. Authentification : OAuth 2.0 avec refresh automatique (D2, et [MONITORING.md](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) │ ├── entity-resolver.js Résolution Name|TableName -> TableName (D21) │ ├── 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` construit au démarrage une **table nom d'outil → module** depuis les `listTools()` des 8 modules de `src/tools/` ; `tools/list` et le dispatch sont servis par cette même table, donc un outil listé est routé par construction (D22). Deux modules déclarant le même nom font échouer le serveur au démarrage. --- ## 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]`, `[Tools]`. Le tableau complet est dans [MONITORING.md](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` : ```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://`. **`_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. **`MAX_LOG_SEARCH_CHARS`** (défaut 25 000) : plafond en caractères de la réponse de `search_logs` — au-delà, des résultats entiers sont écartés et signalés (`truncated`, D24). **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 ```js 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 | Où | |---|---| | `Where` | dans l'`Expression` | | `OrderBy` | dans l'`Expression` — **obligatoire 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) : `entity_type` accepte le nom d'entité AD (`Container`) ou le `TableName` (`Containers`), insensible à la casse — la résolution passe par `entity-resolver.js` (D21). Courantes : `Products`, `Containers`, `Accounts`, `Suppliers`, `Kits`, `Alias` (invariant, pas de pluriel), `Tasks`, `Stocks`, `ProductLocations`, `InboundOrders`, `Receptions`, `OutboundOrders`. La liste faisant foi (288 entités, toutes applications confondues) 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](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 ```bash 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) : ```bash 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. Rien à faire dans `src/index.js` pour un module existant : la table de routage est construite depuis `listTools()` (D22). Un **nouveau module** doit être ajouté à `TOOL_MODULES`. 4. Logger avec le préfixe du module. 5. Renvoyer les erreurs, ne pas les lever hors du wrapper. 6. Tester le handshake complet : ```bash 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](ROADMAP.md) : lots de correction planifiés et propositions explicitement écartées.