Documentation : structure README / CLAUDE / DECISIONS / MONITORING
Nouveaux documents :
- README.md : porte d'entrée humaine, absente jusqu'ici. Objet du projet,
installation, npm test, branchement Claude Desktop, ajout d'un profil WMS,
compilation de l'exécutable.
- DECISIONS.md : 20 décisions et pièges vérifiés sur un WMS réel (D1..D20),
chacun avec son pourquoi. Extrait ce qui était noyé dans CLAUDE.md :
100 % API, tenant_code OAuth, réponses {entities}, casse des propriétés,
dotenv sur stderr, dates relatives LINQ non traduisibles, absence de
CommandParameterData, etc.
- MONITORING.md : supervision du serveur MCP. Préfixes de logs, séquence
d'un démarrage sain, cycle de vie du token OAuth et ses trois filets,
état des caches, table symptôme -> cause. Une section dit explicitement
ce qui n'est pas instrumenté (ni healthcheck, ni métriques, ni alerte).
- docs/logs.md : accès aux logs du WMS. Chemins, placeholder {host},
blocage volontaire sur les profils SaaS, les trois outils, format des
lignes, limites connues.
Mises à jour :
- CLAUDE.md réécrit et aligné sur le code. Correction de l'écart le plus
gênant : le code utilise QueryType 0 (Reading), la doc annonçait 1, soit
l'inverse de ce qui fonctionne pour les comparaisons de statut par
chaîne. Corrigés également : 6 resources et non 7 (workflows://categories
n'existe pas), section .env mono-profil obsolète, références à des
fichiers de test absents, README annoncé mais inexistant. Le suivi de
projet et les checklists de phases sont retirés.
- docs/README.md : index réel du dossier. L'ancien promettait une resource
docs:// qui n'a jamais existé.
- docs/getting_started.md : avertissement en tête, c'est une capture
partielle du portail Mecalux dont les liens internes ne résolvent pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+344
@@ -0,0 +1,344 @@
|
||||
# Décisions d'architecture et pièges vérifiés
|
||||
|
||||
Ce fichier consigne **pourquoi** le code est écrit comme il l'est. Chaque entrée
|
||||
décrit une décision prise ou un piège constaté **sur un WMS réel** — pas une
|
||||
supposition. Avant de « corriger » un comportement qui paraît étrange, cherchez-le
|
||||
ici : il est probablement volontaire.
|
||||
|
||||
Convention : une décision reste dans le fichier même si elle est révisée ; on
|
||||
ajoute alors une entrée `Révisée le …` plutôt que de réécrire l'histoire.
|
||||
|
||||
---
|
||||
|
||||
## D1 — 100 % API, aucun accès Oracle direct
|
||||
|
||||
**Décision.** Toutes les données transitent par les API REST du WMS. Aucune
|
||||
connexion base de données.
|
||||
|
||||
**Pourquoi.** Le serveur MCP doit fonctionner depuis un poste ou une VM sans
|
||||
credentials Oracle, sans client Oracle installé, et sans risque d'écriture
|
||||
directe en base. L'API impose en prime les règles métier et les droits du
|
||||
compte utilisé.
|
||||
|
||||
**Conséquence.** Les fichiers `src/services/oracle-service.js`,
|
||||
`src/resources/database.js` et `src/tools/database-tools.js` ont été supprimés
|
||||
(ils étaient de toute façon morts : non branchés, `oracledb` n'était même pas
|
||||
une dépendance). **Ne pas les réintroduire.** Si une donnée n'est pas
|
||||
atteignable par API, elle est hors périmètre — voir D16.
|
||||
|
||||
---
|
||||
|
||||
## D2 — OAuth : `tenant_code` est obligatoire
|
||||
|
||||
**Piège.** L'endpoint `/EasySTS/OAuth/Token` répond `400 Bad Request` si le
|
||||
paramètre `tenant_code` est absent, sans message explicite.
|
||||
|
||||
**Solution.** Le corps du grant `password` contient toujours les quatre
|
||||
paramètres :
|
||||
|
||||
```
|
||||
grant_type=password&tenant_code=<TENANT>&username=<USER>&password=<PASS>
|
||||
```
|
||||
|
||||
Voir `src/services/api-service.js`, méthode `authenticate()`.
|
||||
|
||||
---
|
||||
|
||||
## D3 — `QueryType: 0` (Reading), pas 1
|
||||
|
||||
**Piège.** `QueryType` sélectionne le modèle de données interrogé :
|
||||
|
||||
| Valeur | Modèle | Champs de statut |
|
||||
|---|---|---|
|
||||
| `0` | **Reading** | chaînes de caractères (`"Release"`) |
|
||||
| `1` | Writing | énumérations |
|
||||
|
||||
Les comparaisons de statut par chaîne — de loin le cas le plus courant en
|
||||
debug — **échouent** en `QueryType: 1`. Le code force donc `0` dans
|
||||
`executeQuery()` et `executeScalarQuery()`.
|
||||
|
||||
**Attention.** D'anciens exemples (dont le PHP de référence) utilisent `1`. Ne
|
||||
les recopiez pas.
|
||||
|
||||
---
|
||||
|
||||
## D4 — Les API AD renvoient `{ entities: [...] }`, pas un tableau
|
||||
|
||||
**Piège.** `POST /AD/api/{Type}/GetByApplication` renvoie un objet enveloppe. Un
|
||||
code qui traite la réponse comme un tableau obtient silencieusement
|
||||
`0 élément` — le symptôme historique était « Successfully cached 0 workflows ».
|
||||
|
||||
**Solution.** Toujours extraire : `response?.entities || []`.
|
||||
|
||||
---
|
||||
|
||||
## D5 — Les propriétés arrivent en minuscules *ou* en majuscules
|
||||
|
||||
**Piège.** Selon le type d'élément et la version du WMS, l'API renvoie `name`
|
||||
ou `Name`, `id` ou `Id`.
|
||||
|
||||
**Solution.** Systématiquement `const name = w.name || w.Name || ''` avant tout
|
||||
filtrage ou tri. Une recherche qui « ne trouve pas » un élément qui existe est
|
||||
presque toujours ce bug.
|
||||
|
||||
---
|
||||
|
||||
## D6 — dotenv doit écrire sur stderr
|
||||
|
||||
**Piège.** dotenv affiche une bannière de version sur **stdout**. Or le
|
||||
protocole MCP réserve stdout au JSON : Claude Desktop échoue alors avec
|
||||
`Unexpected token 'd', "[dotenv@17."... is not valid JSON`.
|
||||
|
||||
**Solution.** `src/index.js` détourne `process.stdout.write` vers stderr le
|
||||
temps du chargement de dotenv, puis le restaure.
|
||||
|
||||
**Règle générale.** Dans tout le projet, on log avec `console.error()`.
|
||||
**Jamais** `console.log()`.
|
||||
|
||||
---
|
||||
|
||||
## D7 — Le `.env` est lu à côté de l'exécutable quand le serveur est packagé
|
||||
|
||||
**Décision.** `src/index.js` résout le chemin du `.env` selon le contexte :
|
||||
|
||||
| Contexte | Chemin du `.env` |
|
||||
|---|---|
|
||||
| Sources (`npm start`) | racine du projet |
|
||||
| Exécutable pkg (`process.pkg`) | dossier de `process.execPath` |
|
||||
|
||||
**Pourquoi.** Avec un chemin statique, pkg **embarque le `.env` dans le
|
||||
snapshot** de l'exe : les credentials sont figés dans le binaire et
|
||||
reconfigurer un déploiement impose un rebuild. Le chemin dynamique via
|
||||
`process.execPath` empêche pkg de le détecter, donc rien n'est embarqué, et
|
||||
`dist/.env` devient le fichier de configuration du déploiement.
|
||||
|
||||
**Vérification.** Sans `.env` à côté de l'exe, le serveur démarre en
|
||||
avertissant `WMS_PROFILES is empty` — preuve qu'aucune valeur n'est embarquée.
|
||||
|
||||
---
|
||||
|
||||
## D8 — Multi-profils au runtime plutôt qu'un serveur MCP par WMS
|
||||
|
||||
**Décision.** Un seul serveur MCP dessert plusieurs backends WMS ; Claude bascule
|
||||
avec `switch_wms_profile`.
|
||||
|
||||
**Pourquoi.** L'alternative — une entrée par client dans
|
||||
`claude_desktop_config.json` — multiplie les processus, les jeux de credentials
|
||||
et les caches, pour un usage où l'on ne consulte qu'un WMS à la fois.
|
||||
|
||||
**Conséquence.** L'état actif est **global au processus**. Un changement de
|
||||
profil doit invalider tout ce qui dépend du tenant. Les services s'abonnent via
|
||||
`profileManager.onSwitch()` :
|
||||
|
||||
| Service | Réaction au switch |
|
||||
|---|---|
|
||||
| `api-service` | `resetToken()` — le token OAuth appartient au tenant précédent |
|
||||
| `workflow-service` | `clearCache()` |
|
||||
| `ad-service` | `invalidateCache()` — tous les types |
|
||||
|
||||
**Ne jamais** appeler ces invalidations à la main depuis un autre module :
|
||||
l'abonnement suffit, et le doublon masquerait un oubli d'abonnement.
|
||||
|
||||
**Sans profil actif** (`DEFAULT_WMS_PROFILE` absent ou invalide), `getCurrent()`
|
||||
lève une erreur qui **énumère les profils disponibles**. C'est intentionnel :
|
||||
Claude lit ce message et enchaîne sur `switch_wms_profile` au lieu d'échouer.
|
||||
|
||||
---
|
||||
|
||||
## D9 — Profils SaaS : accès aux logs refusé, pas silencieux
|
||||
|
||||
**Décision.** Quand `<PROFIL>_SAAS=true`, `read_recent_logs`, `search_logs` et
|
||||
`list_log_files` **lèvent une erreur explicite** renvoyant vers les outils API.
|
||||
|
||||
**Pourquoi.** Le WMS est hébergé dans le cloud Mecalux : le partage
|
||||
`\\<host>\inetpub\logs\...` n'est pas joignable. Retourner « 0 fichier » ferait
|
||||
croire à une absence d'erreurs dans les logs, ce qui est un faux négatif
|
||||
dangereux en diagnostic. Voir [docs/logs.md](docs/logs.md).
|
||||
|
||||
---
|
||||
|
||||
## D10 — Chargement paresseux + cache 1 h
|
||||
|
||||
**Décision.** Workflows et éléments AD ne sont **pas** chargés au démarrage,
|
||||
mais à la première requête qui les concerne, puis mis en cache
|
||||
(`WORKFLOW_CACHE_TTL`, 3 600 000 ms par défaut).
|
||||
|
||||
**Pourquoi.** L'ensemble représente ~38 800 éléments dont 29 374 `Resource` :
|
||||
tout charger au boot ferait échouer le handshake MCP par timeout, pour des
|
||||
données souvent inutiles à la session.
|
||||
|
||||
**Pagination.** La taille de page est réglée **par type** dans
|
||||
`AD_ELEMENT_TYPES` (`src/services/ad-service.js`) : `View: 200`,
|
||||
`Workflow: 5000`, `Resource: 15000`, tout le reste `100000` (soit une seule
|
||||
page). Ces valeurs viennent de l'observation des timeouts serveur — les
|
||||
augmenter à l'aveugle fait échouer les types lourds.
|
||||
|
||||
---
|
||||
|
||||
## D11 — Les paramètres système : `Parameter` + `ParamValue`, fusionnés en JS
|
||||
|
||||
**Piège.** Le modèle Reading ne contient **pas** d'entité
|
||||
`CommandParameterData`. La configuration se lit dans deux entités :
|
||||
|
||||
| Entité | Contenu |
|
||||
|---|---|
|
||||
| `Parameter` | définition + `DefaultValue` |
|
||||
| `ParamValue` | surcharges par entrepôt, liées par `ParameterId` |
|
||||
|
||||
**Décision.** `get_system_parameters` charge les deux intégralement (~200 et
|
||||
~50 lignes) et fait la fusion **côté JavaScript**, en exposant la *valeur
|
||||
effective* par entrepôt (surcharge si présente, défaut sinon).
|
||||
|
||||
**Pourquoi côté JS.** Les filtres (`warehouse`, `param_class`, `search`,
|
||||
`only_overridden`) sont appliqués en JS pour éviter toute concaténation de
|
||||
chaîne LINQ à partir d'entrées utilisateur — pas d'injection possible, et pas
|
||||
de dépendance aux limites du traducteur LINQ (D12).
|
||||
|
||||
Fichier : `src/tools/config-tools.js`.
|
||||
|
||||
---
|
||||
|
||||
## D12 — Les dates relatives ne sont pas traduisibles en LINQ
|
||||
|
||||
**Piège vérifié en production.** `DateTime.Now`, `DateTime.Today` et
|
||||
`AddDays()` ne sont **pas** traduits par le moteur de requêtes : la requête
|
||||
échoue à la compilation.
|
||||
|
||||
**Solution.** Toujours une date littérale :
|
||||
|
||||
```csharp
|
||||
Context.OutboundOrders.Where(z => z.CreationDate > new DateTime(2026, 8, 1))
|
||||
```
|
||||
|
||||
C'est à l'appelant (donc à Claude) de calculer la date avant d'écrire la
|
||||
requête.
|
||||
|
||||
---
|
||||
|
||||
## D13 — `select_expression` reste instable
|
||||
|
||||
**État.** Les projections passées via le paramètre API `Select` déclenchent des
|
||||
erreurs de compilation côté serveur.
|
||||
|
||||
**Contournement actuel.** Interroger les lignes complètes et filtrer les
|
||||
colonnes côté client.
|
||||
|
||||
**Non résolu.** C'est le principal point ouvert du projet. Toute tentative de
|
||||
correction doit être validée sur un vrai WMS avant d'être documentée ici.
|
||||
|
||||
---
|
||||
|
||||
## D14 — `executeCommand` : pas de suffixe d'assembly
|
||||
|
||||
**Piège.** Ajouter `, Mecalux.ITSW.EasyWMS.Modules.Contracts` au nom de commande
|
||||
provoque une `FileLoadException`.
|
||||
|
||||
**Solution.** Utiliser le `command_name` **tel quel** :
|
||||
l'`InternalCommandName` fourni par l'AD contient déjà le nom pleinement
|
||||
qualifié correct.
|
||||
|
||||
---
|
||||
|
||||
## D15 — Validation TLS désactivée
|
||||
|
||||
**Décision.** `httpsAgent: new https.Agent({ rejectUnauthorized: false })`.
|
||||
|
||||
**Pourquoi.** Les WMS on-premise sont exposés en HTTPS avec un certificat
|
||||
auto-signé sur une IP privée.
|
||||
|
||||
**Limite assumée.** Acceptable sur réseau interne ou via VPN. Sur un profil
|
||||
SaaS joint par Internet, cela supprime la protection contre l'interception —
|
||||
à revoir si l'outil sort du cadre du diagnostic interne.
|
||||
|
||||
---
|
||||
|
||||
## D16 — Historique des shipment templates : hors périmètre
|
||||
|
||||
**Constat.** L'entité Reading `ShipmentTemplate` n'expose que la **dernière**
|
||||
exécution (`LastExecuteDate`, `Status`, `IsEnabled`).
|
||||
|
||||
L'historique complet n'existe que dans les logs texte
|
||||
`ApplyShipmentTemplates`, **absents de l'hôte joignable** (`10.255.255.2`) :
|
||||
ils résident sur les serveurs de production / ETL des clients.
|
||||
|
||||
**Décision.** Aucun outil d'analyse de ces logs n'a été construit — il n'aurait
|
||||
rien à lire. Les recettes purement API sont dans la resource
|
||||
`wms://query-examples`.
|
||||
|
||||
---
|
||||
|
||||
## D17 — `WorkflowAction` et `WritingModel` retirés de la liste AD
|
||||
|
||||
**Constat.** Les endpoints `/AD/api/WorkflowAction/GetByApplication` et
|
||||
`/AD/api/WritingModel/GetByApplication` répondent `404 Not Found`.
|
||||
|
||||
**Décision.** Ces deux types sont sortis de `AD_ELEMENT_TYPES` : il en reste
|
||||
**20**, dont 3 valides mais vides (`Dashboard`, `TimelineTemplate`, `Toggle`).
|
||||
Détail de la campagne de validation :
|
||||
[docs/ad-api-validation.md](docs/ad-api-validation.md).
|
||||
|
||||
---
|
||||
|
||||
## D18 — Build : `@yao-pkg/pkg` ciblant node22
|
||||
|
||||
**Décision.** Le build utilise `@yao-pkg/pkg` (fork maintenu de `pkg`, archivé
|
||||
depuis) avec la cible **`node22-win-x64`**.
|
||||
|
||||
**Pourquoi cette cible.** `node20-win-x64` n'a pas de binaire prébuilt
|
||||
disponible : pkg bascule alors sur une compilation de Node depuis les sources,
|
||||
qui échoue faute de `vcbuild.bat` (toolchain MSVC absente).
|
||||
|
||||
**Avertissements normaux au build.** `Cannot find module
|
||||
'@modelcontextprotocol/sdk/server/index.js'` et `Entry 'main' not found` :
|
||||
pkg ne sait pas résoudre statiquement la table `exports` du SDK. L'exécutable
|
||||
produit **fonctionne** — vérifié en démarrant l'exe. Ne pas chercher à
|
||||
« corriger » ces avertissements.
|
||||
|
||||
---
|
||||
|
||||
## D19 — La resource `docs://` a été supprimée
|
||||
|
||||
**Constat.** `src/resources/documentation.js` (193 lignes) exposait un index des
|
||||
`.md` de `docs/`, mais n'a **jamais été branché** dans `src/index.js` : le
|
||||
handler `resources/list` ne l'incluait pas et `resources/read` ne routait aucune
|
||||
URI `docs://`. `docs/README.md` promettait pourtant la fonctionnalité aux
|
||||
utilisateurs.
|
||||
|
||||
**Décision.** Fichier supprimé, `docs/README.md` corrigé. `docs/` reste un
|
||||
dossier de référence pour les humains et pour un agent qui lit le dépôt — pas
|
||||
une resource MCP.
|
||||
|
||||
**Si on veut la fonctionnalité un jour**, il faut la brancher réellement (2
|
||||
lignes dans `src/index.js`) *et* décider de son sort dans l'exécutable pkg, qui
|
||||
n'embarque pas `docs/`.
|
||||
|
||||
---
|
||||
|
||||
## D20 — Purge du dépôt (2026-08-24)
|
||||
|
||||
Supprimés lors du nettoyage :
|
||||
|
||||
| Élément | Raison |
|
||||
|---|---|
|
||||
| 5 `.md` dupliqués à la racine | copies md5-identiques de `docs/api/` et `docs/entities/` |
|
||||
| `JANITOR_main.js`, `JANITOR_entities.json` | application Electron sans lien avec le MCP |
|
||||
| `temp/*.json` | dumps de workflows versionnés par accident (`temp/` désormais ignoré) |
|
||||
| `claude_desktop_config_ssh.json` | **mots de passe en clair** + variables `ORACLE_*` de l'architecture supprimée (D1) |
|
||||
| `IMPLEMENTATION_SUMMARY.md` | doublon d'`AD_API_TEST_RESULTS.md`, déplacé en `docs/ad-api-validation.md` |
|
||||
| `src/resources/documentation.js` | code mort (D19) |
|
||||
| `src/config/constants.js` (83 l.) | module entier inutilisé : `require` présent dans `wms-query-service.js`, mais **aucune** de ses constantes n'était lue |
|
||||
| `log-service.js` : `findRecentErrors`, `readFullLog`, `getLogStats` (~100 l.) | exportées, jamais appelées — aucun outil MCP ne les exposait |
|
||||
| `RESOURCE_URIS.WORKFLOWS_CATEGORIES` | URI déclarée, jamais servie |
|
||||
| `LOG_FILE_PATTERN` | lue depuis `.env`, jamais utilisée (le scan filtre sur `.log` en dur) |
|
||||
|
||||
Les trois fonctions de `log-service.js` étaient fonctionnelles ; si l'une d'elles
|
||||
redevient utile (`findRecentErrors` en particulier), la reprendre depuis le
|
||||
commit `b59cbb3` et **l'exposer réellement** comme outil MCP plutôt que de la
|
||||
laisser inatteignable.
|
||||
|
||||
⚠️ **Credentials à faire tourner.** `claude_desktop_config_ssh.json` et
|
||||
l'ancienne version de `test-ad-api.ps1` contenaient des mots de passe en clair.
|
||||
Le fichier est retiré du répertoire de travail, **mais il reste dans
|
||||
l'historique git** (commit `b59cbb3`). Considérez ces mots de passe comme
|
||||
compromis et changez-les ; à défaut, réécrivez l'historique avant toute
|
||||
publication du dépôt.
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
# Supervision du serveur MCP
|
||||
|
||||
Comment savoir si le serveur tourne, ce qu'il fait, et pourquoi il ne répond
|
||||
pas. Ce document décrit **l'existant** — il n'y a ni endpoint de santé, ni
|
||||
métriques exportées : toute l'observabilité passe par **stderr** et par le
|
||||
smoke test `npm test`.
|
||||
|
||||
Pour lire les logs **du WMS** (et non ceux du serveur MCP), voir
|
||||
[docs/logs.md](docs/logs.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Où regarder
|
||||
|
||||
| Quoi | Où |
|
||||
|---|---|
|
||||
| Logs du serveur MCP | `%APPDATA%\Claude\logs\` (fichier `mcp-server-wms.log`) |
|
||||
| Logs applicatifs du WMS | partages `\\<host>\...` — voir [docs/logs.md](docs/logs.md) |
|
||||
| État de la connexion WMS | `npm test` (voir §5) |
|
||||
| Profil actif / caches | outils `get_current_wms_profile`, `get_application_summary` |
|
||||
|
||||
**Tout passe par stderr.** stdout est réservé au JSON du protocole MCP : la
|
||||
moindre écriture sur stdout casse la session Claude Desktop (voir D6 dans
|
||||
[DECISIONS.md](DECISIONS.md)). En pratique, cela veut dire que **les logs sont
|
||||
la seule sortie observable**, et qu'ils sont complets.
|
||||
|
||||
Suivre les logs en direct :
|
||||
|
||||
```bash
|
||||
Get-Content -Wait -Tail 50 "$env:APPDATA\Claude\logs\mcp-server-wms.log"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Lire les préfixes
|
||||
|
||||
Chaque ligne est préfixée par son composant. Le préfixe suffit à localiser le
|
||||
problème.
|
||||
|
||||
| Préfixe | Composant | Ce qu'il signale |
|
||||
|---|---|---|
|
||||
| `[Server]` | `src/index.js` | démarrage, routage des outils, erreurs non rattrapées |
|
||||
| `[Profile]` | `config/profile-manager.js` | chargement des profils, bascule de profil |
|
||||
| `[API]` | `services/api-service.js` | OAuth, chaque requête HTTP, retries 401 |
|
||||
| `[Workflow]` | `services/workflow-service.js` | cache workflows, pagination |
|
||||
| `[AD]` | `services/ad-service.js` | cache par type d'élément, pagination |
|
||||
| `[Logs]` | `services/log-service.js` | chemins de logs illisibles ou absents |
|
||||
| `[WMSQuery]`, `[Metadata]` | services | construction des requêtes |
|
||||
| `[*Tools]` | `src/tools/` | exécution d'un outil précis |
|
||||
|
||||
---
|
||||
|
||||
## 3. Démarrage : à quoi ressemble un boot sain
|
||||
|
||||
```
|
||||
[dotenv@17.2.4] injecting env (30) from .env
|
||||
[Profile] Loaded 3 profile(s). Active: LIMAGRAIN
|
||||
[Server] Starting WMS MCP Server...
|
||||
[Server] Architecture: 100% API-based (no direct database access)
|
||||
[Server] Profiles available: AD, EUROTRAFIC, LIMAGRAIN
|
||||
[Server] Active profile: LIMAGRAIN
|
||||
[Server] WMS MCP Server running on stdio
|
||||
[Server] Ready to accept requests from Claude Desktop
|
||||
```
|
||||
|
||||
Trois points à contrôler dans cet ordre :
|
||||
|
||||
1. **`injecting env (N)`** — si `N` vaut 0, le `.env` n'a pas été trouvé. En
|
||||
mode packagé il est attendu **à côté de l'exe** (D7).
|
||||
2. **`Loaded N profile(s)`** — si 0, `WMS_PROFILES` est vide ou les variables
|
||||
`<NOM>_HOST/USERNAME/PASSWORD/TENANT` manquent.
|
||||
3. **`Active profile: …`** — si le message est `No active profile`, ce n'est
|
||||
**pas** une panne : Claude doit appeler `switch_wms_profile` avant la
|
||||
première requête, et l'erreur renvoyée le lui indique explicitement (D8).
|
||||
|
||||
Aucune connexion au WMS n'est tentée au démarrage : un boot propre ne prouve
|
||||
donc **pas** que le WMS est joignable. Pour cela, voir §5.
|
||||
|
||||
---
|
||||
|
||||
## 4. Cycle de vie du token OAuth
|
||||
|
||||
Le token est obtenu **paresseusement**, à la première requête, puis rafraîchi
|
||||
automatiquement. Réglages dans `.env` :
|
||||
|
||||
| Variable | Défaut | Rôle |
|
||||
|---|---|---|
|
||||
| `TOKEN_REFRESH_THRESHOLD` | `1000` s | âge au-delà duquel un refresh est déclenché avant la requête |
|
||||
| `TOKEN_MAX_AGE` | `1190` s | âge au-delà duquel on ne tente plus le `refresh_token` mais une ré-authentification complète |
|
||||
| `QUERY_TIMEOUT` | `30000` ms | timeout HTTP de toute requête WMS |
|
||||
|
||||
Séquence observable :
|
||||
|
||||
```
|
||||
[API] Authenticating profile="LIMAGRAIN" tenant="LIMAGRAI2512" ...
|
||||
[API] Authentication successful. Token expires in ~1190s
|
||||
[API] POST /QueryExecute
|
||||
... (~17 min plus tard)
|
||||
[API] Refreshing token with refresh_token grant...
|
||||
[API] Token refreshed successfully
|
||||
```
|
||||
|
||||
Trois filets de sécurité, dans cet ordre :
|
||||
|
||||
1. **Avant la requête** — si `âge > TOKEN_REFRESH_THRESHOLD`, refresh préventif.
|
||||
2. **Refresh en échec** — bascule automatique sur le grant `password`
|
||||
(`[API] Token refresh failed, re-authenticating`).
|
||||
3. **Réponse 401** — un refresh est déclenché et la requête est **rejouée une
|
||||
fois** (`[API] Unauthorized, refreshing token and retrying...`).
|
||||
|
||||
**Ce qui est normal.** Une ligne `Token refresh failed` isolée suivie d'une
|
||||
authentification réussie : le filet a joué son rôle.
|
||||
|
||||
**Ce qui ne l'est pas.** Ces trois lignes en boucle rapprochée signalent des
|
||||
credentials invalides ou un tenant erroné — le serveur n'abandonne jamais de
|
||||
lui-même, il retentera à chaque requête.
|
||||
|
||||
---
|
||||
|
||||
## 5. Test de bout en bout
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
Teste le profil actif ; `npm test -- AD` cible un profil, `npm test -- --all`
|
||||
les teste tous. Quatre vérifications en lecture seule, aucune écriture WMS :
|
||||
|
||||
| Test | Ce qu'il prouve |
|
||||
|---|---|
|
||||
| OAuth | host joignable, credentials et tenant corrects |
|
||||
| `QueryExecute` | API ApplicationService opérationnelle |
|
||||
| `QueryScalarExecute` | requêtes scalaires (`Count`) opérationnelles |
|
||||
| AD API (`Validator`) | API Application Dictionary opérationnelle |
|
||||
|
||||
Sortie attendue :
|
||||
|
||||
```
|
||||
=== Profil LIMAGRAIN ===
|
||||
host=10.255.255.2 tenant=LIMAGRAI2512 saas=false
|
||||
OK OAuth - token obtenu (age max ~1190s)
|
||||
OK QueryExecute - 1 ligne(s)
|
||||
OK QueryScalarExecute - 51160 produit(s)
|
||||
OK AD API (Validator) - 10 element(s)
|
||||
-> 4/4 tests reussis
|
||||
```
|
||||
|
||||
Code de sortie `0` si tout passe, `1` sinon — utilisable tel quel dans une
|
||||
tâche planifiée.
|
||||
|
||||
C'est le premier réflexe quand Claude signale une erreur WMS : il isole en
|
||||
quelques secondes une panne de connectivité d'un problème de requête.
|
||||
|
||||
---
|
||||
|
||||
## 6. État des caches
|
||||
|
||||
Deux caches, TTL commun `WORKFLOW_CACHE_TTL` (1 h par défaut), tous deux vidés
|
||||
à chaque `switch_wms_profile` (D8).
|
||||
|
||||
| Cache | Contenu | Purge |
|
||||
|---|---|---|
|
||||
| workflows | ~3 700 workflows | TTL, ou bascule de profil |
|
||||
| AD | un cache **par type** (20 types, ~38 800 éléments) | TTL par type, ou bascule de profil |
|
||||
|
||||
**Les inspecter sans redémarrer** : l'outil `get_application_summary` liste les
|
||||
types chargés et leur nombre d'éléments — un type absent signifie simplement
|
||||
qu'il n'a jamais été demandé dans cette session (D10).
|
||||
|
||||
Signature d'un chargement dans les logs :
|
||||
|
||||
```
|
||||
[AD] Cache expired or empty, fetching Resource...
|
||||
[AD] Fetching Resource: offset=0, pageSize=15000
|
||||
[AD] Fetched 15000 Resource (total: 15000)
|
||||
[AD] Fetching Resource: offset=15000, pageSize=15000
|
||||
...
|
||||
[AD] Successfully cached 29374 Resource
|
||||
[AD] Cache hit: Resource (29374 elements) <- appels suivants
|
||||
```
|
||||
|
||||
Une première requête sur `Resource` prend plusieurs dizaines de secondes : ce
|
||||
n'est pas un blocage, c'est la pagination. Les suivantes sont instantanées.
|
||||
|
||||
---
|
||||
|
||||
## 7. Symptômes → causes
|
||||
|
||||
| Symptôme | Cause probable | Vérification |
|
||||
|---|---|---|
|
||||
| Le serveur n'apparaît pas dans Claude Desktop | chemin invalide dans `claude_desktop_config.json`, ou Claude pas redémarré | ouvrir `%APPDATA%\Claude\logs\` |
|
||||
| `Unexpected token … is not valid JSON` | quelque chose a écrit sur **stdout** | chercher un `console.log()` ajouté (D6) |
|
||||
| `injecting env (0)` | `.env` introuvable | en packagé : le placer à côté de l'exe (D7) |
|
||||
| `No WMS profile selected` | `DEFAULT_WMS_PROFILE` absent ou invalide | c'est un état normal — appeler `switch_wms_profile` |
|
||||
| `Authentication failed: … 400` | `tenant_code` ou credentials erronés | `npm test -- <PROFIL>` (D2) |
|
||||
| Boucle `refresh failed` / `Authenticating` | credentials invalides | `npm test` |
|
||||
| `ETIMEDOUT` / `ECONNREFUSED` | host injoignable (VPN, pare-feu) | `Test-NetConnection <host> -Port 443` |
|
||||
| `timeout of 30000ms exceeded` | requête trop lourde | ajouter un `Where`, réduire `take`, ou augmenter `QUERY_TIMEOUT` |
|
||||
| `Successfully cached 0 workflows` | réponse non enveloppée par `entities` | D4 |
|
||||
| Recherche vide sur un élément existant | casse des propriétés (`name` vs `Name`) | D5 |
|
||||
| `Log access is disabled for SaaS profile` | profil `SAAS=true` | comportement voulu (D9), utiliser les outils API |
|
||||
| Erreur de compilation LINQ sur une date | `DateTime.Now` employé | date littérale (D12) |
|
||||
|
||||
---
|
||||
|
||||
## 8. Ce qui n'est pas instrumenté
|
||||
|
||||
À connaître avant de promettre une supervision qui n'existe pas :
|
||||
|
||||
- **Pas de healthcheck** exposé, ni HTTP ni MCP. `npm test` est le seul contrôle
|
||||
automatisable, et il faut le lancer soi-même.
|
||||
- **Pas de métriques** : ni compteur d'appels, ni latence, ni taux d'erreur.
|
||||
- **Pas de fichier de log propre au serveur** : tout est capté par Claude
|
||||
Desktop, avec sa rotation à lui.
|
||||
- **Pas d'alerte** : une panne d'authentification n'est visible qu'au prochain
|
||||
appel d'un outil.
|
||||
- **`uncaughtException` et `unhandledRejection` sont journalisés mais
|
||||
n'arrêtent pas le processus** (`src/index.js`). Le serveur peut donc survivre
|
||||
dans un état dégradé — d'où l'intérêt de relire les logs jusqu'au début en cas
|
||||
de comportement erratique, et non seulement la dernière erreur.
|
||||
@@ -0,0 +1,152 @@
|
||||
# WMS MCP Server
|
||||
|
||||
Serveur [MCP](https://modelcontextprotocol.io) qui donne à Claude un accès en
|
||||
lecture à un WMS **EasyWMS** (Mecalux), pour le diagnostic et l'analyse.
|
||||
|
||||
Concrètement, dans Claude Desktop :
|
||||
|
||||
> « Combien de commandes sont bloquées en statut Release sur LIMAGRAIN ? »
|
||||
> « Trouve les workflows qui touchent au réapprovisionnement. »
|
||||
> « Cherche `Order 4711` dans les logs. »
|
||||
> « Quels paramètres sont surchargés sur l'entrepôt 2 ? »
|
||||
|
||||
**Architecture : 100 % API REST.** Aucun accès direct à Oracle — voir D1 dans
|
||||
[DECISIONS.md](DECISIONS.md).
|
||||
|
||||
---
|
||||
|
||||
## Ce que le serveur expose
|
||||
|
||||
**23 outils** répartis en 8 familles :
|
||||
|
||||
| Famille | Outils |
|
||||
|---|---|
|
||||
| Requêtes WMS | `query_wms_entities`, `count_wms_entities`, `get_entity_schema`, `search_wms_data` |
|
||||
| API brutes | `call_query_api`, `execute_command` |
|
||||
| Workflows | `search_workflows`, `get_workflow_details`, `list_workflow_categories` |
|
||||
| Application Dictionary | `get_application_summary`, `get_ad_elements`, `search_ad_elements`, `get_ad_element_details`, `list_ad_types` |
|
||||
| Métadonnées | `get_entity_metadata`, `generic_search` |
|
||||
| Configuration | `get_system_parameters` |
|
||||
| Profils | `list_wms_profiles`, `get_current_wms_profile`, `switch_wms_profile` |
|
||||
| Logs | `read_recent_logs`, `list_log_files`, `search_logs` |
|
||||
|
||||
**6 resources** de contexte : `wms://entities`, `wms://entity-schemas`,
|
||||
`wms://query-examples`, `workflows://overview`, `api://catalog`, `logs://guide`.
|
||||
|
||||
**Multi-WMS.** Plusieurs backends (clients, tenants) coexistent dans un seul
|
||||
serveur ; Claude bascule à la demande avec `switch_wms_profile`.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Prérequis : Node.js 18+ et un accès réseau au WMS (VPN si nécessaire).
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Copiez `.env.example` en `.env` et renseignez au moins un profil :
|
||||
|
||||
```env
|
||||
WMS_API_AUTH=Basic R05BOklFNGU3aXFoZHQ=
|
||||
WMS_APPLICATION=EasyWMS
|
||||
WMS_API_PATH=/ApplicationService/api
|
||||
WMS_TOKEN_PATH=/EasySTS/OAuth/Token
|
||||
WORKFLOW_API_PATH=/AD/api
|
||||
|
||||
WMS_PROFILES=AD
|
||||
DEFAULT_WMS_PROFILE=AD
|
||||
|
||||
AD_HOST=10.255.255.2
|
||||
AD_USERNAME=...
|
||||
AD_PASSWORD=...
|
||||
AD_TENANT=AD
|
||||
AD_SAAS=false
|
||||
```
|
||||
|
||||
Vérifiez la connectivité — le test est en lecture seule :
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
Sortie attendue : `4/4 tests reussis`. En cas d'échec, voir
|
||||
[MONITORING.md](MONITORING.md) §7.
|
||||
|
||||
---
|
||||
|
||||
## Brancher Claude Desktop
|
||||
|
||||
Éditez `%APPDATA%\Claude\claude_desktop_config.json` :
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"wms": {
|
||||
"command": "node",
|
||||
"args": ["D:\\chemin\\vers\\mcp-wms-api\\src\\index.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Puis **fermez et rouvrez complètement** Claude Desktop. Les logs du serveur
|
||||
apparaissent dans `%APPDATA%\Claude\logs\`.
|
||||
|
||||
---
|
||||
|
||||
## Ajouter un WMS
|
||||
|
||||
1. Ajoutez son nom à `WMS_PROFILES` (séparateur : virgule).
|
||||
2. Définissez `<NOM>_HOST`, `<NOM>_USERNAME`, `<NOM>_PASSWORD`, `<NOM>_TENANT`.
|
||||
3. Mettez `<NOM>_SAAS=true` si le WMS est hébergé dans le cloud Mecalux — les
|
||||
outils de log seront alors désactivés pour ce profil, à dessein.
|
||||
|
||||
Les URL se construisent à partir du host : rien d'autre à dupliquer. Testez
|
||||
avec `npm test -- <NOM>`.
|
||||
|
||||
---
|
||||
|
||||
## Compiler un exécutable Windows
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Produit `dist/wms-mcp-server.exe` (~76 Mo, autonome, cible `node22-win-x64`).
|
||||
|
||||
**Placez le `.env` à côté de l'exe** : en mode packagé, c'est là qu'il est lu,
|
||||
et aucun credential n'est embarqué dans le binaire (D7). Les avertissements
|
||||
`Cannot find module '@modelcontextprotocol/sdk/…'` pendant le build sont
|
||||
normaux et sans effet (D18).
|
||||
|
||||
Déploiement type sur la VM :
|
||||
|
||||
```
|
||||
C:\WMS\mcp\wms-mcp-server.exe
|
||||
C:\WMS\mcp\.env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
| Fichier | Contenu |
|
||||
|---|---|
|
||||
| [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 |
|
||||
| [docs/logs.md](docs/logs.md) | Accès aux logs du WMS |
|
||||
| [docs/](docs/) | Références EasyWMS (API, entités) |
|
||||
|
||||
---
|
||||
|
||||
## Sécurité
|
||||
|
||||
- `.env` et `dist/` sont ignorés par git — **ne les committez jamais**.
|
||||
- La validation TLS est désactivée pour accepter les certificats auto-signés
|
||||
des WMS on-premise (D15).
|
||||
- ⚠️ L'historique git contient un ancien fichier de configuration avec des mots
|
||||
de passe en clair (commit `b59cbb3`). Ces credentials sont à considérer comme
|
||||
compromis — voir D20.
|
||||
+36
-32
@@ -1,41 +1,45 @@
|
||||
# Documentation WMS
|
||||
# Documentation de référence
|
||||
|
||||
Bienvenue dans la documentation du WMS !
|
||||
Documents de référence sur EasyWMS et ses API, conservés dans le dépôt pour
|
||||
être consultables hors ligne et par un agent qui lit le code.
|
||||
|
||||
## Comment utiliser cette documentation
|
||||
> ⚠️ **Ce dossier n'est pas exposé comme resource MCP.** Une resource `docs://`
|
||||
> a existé sans jamais être branchée ; elle a été supprimée. Ces fichiers se
|
||||
> lisent directement depuis le dépôt. Voir D19 dans
|
||||
> [DECISIONS.md](../DECISIONS.md).
|
||||
|
||||
Cette documentation est automatiquement accessible via le serveur MCP. Claude peut lire tous les fichiers `.md` présents dans ce dossier et ses sous-dossiers.
|
||||
## Contenu
|
||||
|
||||
## Organisation
|
||||
| Fichier | Nature |
|
||||
|---|---|
|
||||
| [logs.md](logs.md) | **Rédigé pour ce projet** — accès aux logs WMS, outils, limites |
|
||||
| [ad-api-validation.md](ad-api-validation.md) | **Rédigé pour ce projet** — campagne de validation curl des 19 types AD testés (17 valides) |
|
||||
| [api/Application Service API Reference.md](api/Application%20Service%20API%20Reference.md) | Référence de l'API ApplicationService |
|
||||
| [api/POST apiQueryExecute.md](api/POST%20apiQueryExecute.md) | Détail de l'endpoint `QueryExecute` |
|
||||
| [entities/easywms_reading_entites.md](entities/easywms_reading_entites.md) | Catalogue des entités du modèle **Reading** |
|
||||
| [entities/easywms_reading_entites_outboundorder.md](entities/easywms_reading_entites_outboundorder.md) | Détail de l'entité `OutboundOrder` |
|
||||
| [entities/easywms_reading_entites_outboundorder_OutboundOrderStatus.md](entities/easywms_reading_entites_outboundorder_OutboundOrderStatus.md) | Valeurs de `OutboundOrderStatus` |
|
||||
| `getting_started.md`, `docs_downloads/` | Extractions du portail documentaire Mecalux |
|
||||
| `reference-queries-api.php` | Client PHP d'origine, source des patterns d'API. ⚠️ utilise `QueryType: 1` — ne pas recopier, voir D3 |
|
||||
|
||||
Organisez vos fichiers de documentation comme vous le souhaitez :
|
||||
**Sur les extractions du portail** : ce sont des captures partielles. Leurs
|
||||
liens internes pointent vers des pages non téléchargées (`ReleaseNotes.md`,
|
||||
`video_tutorials/`, …) et ne fonctionnent pas. Le seul document réellement
|
||||
exploitable de cet ensemble est
|
||||
`docs_downloads/communications/EasyWMS_WebApi_en.html.md` (référence complète de
|
||||
la Web API, ~320 Ko).
|
||||
|
||||
```
|
||||
docs/
|
||||
├── README.md (ce fichier)
|
||||
├── getting-started.md (guide de démarrage)
|
||||
├── api/
|
||||
│ ├── overview.md
|
||||
│ └── endpoints.md
|
||||
├── workflows/
|
||||
│ ├── reception.md
|
||||
│ └── expedition.md
|
||||
└── troubleshooting/
|
||||
└── common-errors.md
|
||||
```
|
||||
## Où trouver le reste
|
||||
|
||||
## Comment ajouter de la documentation
|
||||
| Question | Document |
|
||||
|---|---|
|
||||
| À quoi sert ce projet, comment l'installer | [../README.md](../README.md) |
|
||||
| Architecture, outils, resources, conventions de code | [../CLAUDE.md](../CLAUDE.md) |
|
||||
| Pourquoi le code est écrit ainsi, pièges vérifiés | [../DECISIONS.md](../DECISIONS.md) |
|
||||
| Le serveur ne répond pas / comment le superviser | [../MONITORING.md](../MONITORING.md) |
|
||||
|
||||
1. Créez vos fichiers `.md` dans ce dossier ou dans des sous-dossiers
|
||||
2. Redémarrez Claude Desktop
|
||||
3. Claude pourra automatiquement lire tous vos fichiers de documentation
|
||||
## Ajouter un document
|
||||
|
||||
## Accéder à la documentation depuis Claude
|
||||
|
||||
Dans Claude Desktop, vous pouvez demander :
|
||||
|
||||
- "Montre-moi le sommaire de la documentation"
|
||||
- "Lis la documentation sur les workflows"
|
||||
- "Affiche-moi la documentation de l'API"
|
||||
|
||||
Claude aura accès à tous les fichiers `.md` présents ici !
|
||||
Déposez le `.md` dans le sous-dossier qui convient et **ajoutez sa ligne au
|
||||
tableau ci-dessus**. Un document non listé ici est un document que personne ne
|
||||
retrouvera.
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
> ⚠️ **Capture partielle du portail documentaire Mecalux.** Ce sommaire est
|
||||
> conservé pour mémoire : la quasi-totalité de ses liens pointe vers des pages
|
||||
> qui n'ont pas été téléchargées et ne résolvent pas. Seul
|
||||
> [docs_downloads/communications/EasyWMS_WebApi_en.html.md](docs_downloads/communications/EasyWMS_WebApi_en.html.md)
|
||||
> est exploitable hors ligne. Voir [README.md](README.md).
|
||||
|
||||
# MAP - Documentation Portal
|
||||
|
||||
## Table des matières
|
||||
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
# Logs du WMS
|
||||
|
||||
Comment le serveur MCP accède aux fichiers de logs du WMS, ce qu'il sait faire
|
||||
et ce qu'il ne peut pas faire.
|
||||
|
||||
Pour superviser **le serveur MCP lui-même**, voir
|
||||
[MONITORING.md](../MONITORING.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Où sont les logs
|
||||
|
||||
Les logs sont lus **directement sur le système de fichiers** du serveur WMS,
|
||||
via des partages réseau Windows — jamais par API.
|
||||
|
||||
| Emplacement | Contenu |
|
||||
|---|---|
|
||||
| `\\<host>\inetpub\logs\LogFiles\Mecalux` | logs applicatifs IIS, organisés en sous-dossiers par composant |
|
||||
| `\\<host>\ProgramData\Mecalux\ETLLogs` | logs du middleware ETL |
|
||||
|
||||
Configuration dans `.env`, plusieurs chemins séparés par des `;` :
|
||||
|
||||
```env
|
||||
LOGS_PATH=\\{host}\inetpub\logs\LogFiles\Mecalux;\\{host}\ProgramData\Mecalux\ETLLogs
|
||||
```
|
||||
|
||||
**Le placeholder `{host}` est remplacé à chaque appel** par le host du profil
|
||||
actif. Il n'y a donc pas de chemin de logs à redéfinir par profil : un seul
|
||||
gabarit suffit, et il suit automatiquement `switch_wms_profile`.
|
||||
|
||||
Le scan est **récursif** et ne retient que les fichiers dont le nom se termine
|
||||
par `.log`. Un dossier absent ou inaccessible n'interrompt pas le scan : il est
|
||||
journalisé (`[Logs] Cannot scan directory …`) et ignoré.
|
||||
|
||||
---
|
||||
|
||||
## 2. Prérequis d'accès
|
||||
|
||||
Les partages `\\<host>\...` doivent être joignables **depuis la machine qui
|
||||
exécute le serveur MCP**, avec les droits de lecture du compte Windows courant.
|
||||
Le serveur ne présente aucun credential propre pour SMB — il hérite de la
|
||||
session Windows.
|
||||
|
||||
Vérification rapide, hors serveur MCP :
|
||||
|
||||
```bash
|
||||
Test-Path "\\10.255.255.2\inetpub\logs\LogFiles\Mecalux"
|
||||
```
|
||||
|
||||
Si cette commande renvoie `False`, aucun outil de log ne fonctionnera : le
|
||||
problème est réseau ou droits, pas applicatif.
|
||||
|
||||
---
|
||||
|
||||
## 3. Profils SaaS : les logs ne sont pas accessibles
|
||||
|
||||
Quand le profil actif est déclaré `<PROFIL>_SAAS=true`, les trois outils de log
|
||||
**échouent volontairement** avec :
|
||||
|
||||
> Log access is disabled for SaaS profile "X". The WMS is cloud-hosted — local
|
||||
> log files are not reachable. Use the WMS API (query/command/workflow tools)
|
||||
> instead.
|
||||
|
||||
C'est un choix explicite, pas une régression : le WMS est hébergé dans le cloud
|
||||
Mecalux, son système de fichiers n'est pas exposé. Retourner « 0 fichier »
|
||||
laisserait croire à une absence d'erreurs — un faux négatif dangereux en
|
||||
diagnostic. Voir D9 dans [DECISIONS.md](../DECISIONS.md).
|
||||
|
||||
**Sur un profil SaaS, le diagnostic passe donc uniquement par les API** :
|
||||
`query_wms_entities`, `count_wms_entities`, `search_wms_data`, les outils AD et
|
||||
`get_system_parameters`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Les trois outils
|
||||
|
||||
### `list_log_files`
|
||||
|
||||
Liste tous les `.log` trouvés sous `LOGS_PATH`, **triés du plus récent au plus
|
||||
ancien**, avec taille, date de modification et dossier d'origine. Point de
|
||||
départ naturel : il montre quels composants ont écrit récemment.
|
||||
|
||||
Si aucun fichier n'est trouvé, l'erreur **énumère les chemins scannés** — c'est
|
||||
généralement suffisant pour diagnostiquer un `{host}` mal résolu.
|
||||
|
||||
### `read_recent_logs(count, log_file)`
|
||||
|
||||
Renvoie les `count` dernières lignes (100 par défaut). Sans `log_file`, prend
|
||||
**le fichier le plus récemment modifié, tous dossiers confondus** — ce qui n'est
|
||||
pas forcément celui que l'on croit sur un serveur multi-composants ; préférez
|
||||
nommer le fichier.
|
||||
|
||||
La résolution de `log_file` se fait en trois passes, de la plus stricte à la
|
||||
plus permissive :
|
||||
|
||||
1. chemin direct sous le premier `LOGS_PATH`
|
||||
(ex. `ApplicationDictionary\ApplicationDictionary.log`) ;
|
||||
2. nom exact recherché dans chaque sous-dossier immédiat ;
|
||||
3. correspondance partielle, insensible à la casse.
|
||||
|
||||
En cas d'échec, l'erreur liste les fichiers disponibles.
|
||||
|
||||
### `search_logs(keyword, max_results, context_lines)`
|
||||
|
||||
Recherche insensible à la casse dans **tous** les fichiers, du plus récent au
|
||||
plus ancien, en s'arrêtant à `max_results` (50 par défaut). Chaque résultat
|
||||
porte son fichier, son numéro de ligne et `context_lines` lignes avant/après (2
|
||||
par défaut), la ligne trouvée étant marquée `isMatch`.
|
||||
|
||||
C'est l'outil à privilégier pour tracer un identifiant métier (numéro de
|
||||
commande, code produit, id de tâche) à travers les composants.
|
||||
|
||||
---
|
||||
|
||||
## 5. Format des lignes
|
||||
|
||||
`ApplicationService.log` suit ce format :
|
||||
|
||||
```
|
||||
YYYY-MM-DD HH:MM:SS.ffff [thread] [Level] [Component] [message]
|
||||
```
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
2026-08-24 09:14:22.1873 [42] [ERROR] [OutboundOrderService] [Order 4711 not found]
|
||||
```
|
||||
|
||||
Conséquence pratique : une recherche par **date préfixe** (`2026-08-24 09:`)
|
||||
fonctionne bien, et le niveau se filtre par `[ERROR]` — crochets inclus, pour
|
||||
éviter les faux positifs sur le mot « error » dans un message.
|
||||
|
||||
Les logs ETL n'ont pas le même format ; ne présumez pas d'une structure commune
|
||||
entre les deux emplacements.
|
||||
|
||||
---
|
||||
|
||||
## 6. Limites à connaître
|
||||
|
||||
- **Lecture intégrale en mémoire.** `read_recent_logs` et `search_logs`
|
||||
chargent chaque fichier entier avant de le découper. Sur un log de plusieurs
|
||||
centaines de Mo, c'est lent et coûteux en RAM. Vérifiez les tailles avec
|
||||
`list_log_files` avant de lancer une recherche large.
|
||||
- **Recherche par sous-chaîne uniquement**, pas d'expression régulière.
|
||||
- **Pas de filtre temporel** : `search_logs` ne sait pas restreindre à une
|
||||
plage horaire. Le contournement est d'inclure le préfixe de date dans le
|
||||
mot-clé.
|
||||
- **Aucun filtre de nom de fichier configurable.** Le scan retient tous les
|
||||
`.log`. (Une variable `LOG_FILE_PATTERN` a existé dans `.env` sans jamais
|
||||
être appliquée ; elle a été supprimée — voir D20.)
|
||||
- **Pas de rotation ni de purge** : le serveur MCP lit, il n'écrit ni ne
|
||||
supprime rien.
|
||||
|
||||
---
|
||||
|
||||
## 7. Hors périmètre : l'historique des shipment templates
|
||||
|
||||
Les logs `ApplyShipmentTemplates` **ne sont pas présents** sur l'hôte joignable
|
||||
(`10.255.255.2`) : ils résident sur les serveurs de production / ETL des
|
||||
clients. Aucun outil ne les analyse, car il n'y aurait rien à lire.
|
||||
|
||||
Ce que l'on peut obtenir par API se limite à la **dernière** exécution, via
|
||||
l'entité Reading `ShipmentTemplate` (`LastExecuteDate`, `Status`,
|
||||
`IsEnabled`). Voir D16 dans [DECISIONS.md](../DECISIONS.md) et les recettes de
|
||||
la resource `wms://query-examples`.
|
||||
Reference in New Issue
Block a user