Files
mcp-wms-api/MONITORING.md
T
Arthur Ria 0ff44f7b78 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>
2026-08-24 15:15:31 +02:00

221 lines
8.9 KiB
Markdown

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