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

226 lines
9.2 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.
Sonde plus légère, si l'on veut seulement savoir si le WMS répond (sans
authentification applicative) : `GET https://<host>/ApplicationService/api/healthcheck?tenantCode=<TENANT>`
et `.../api/ready?tenantCode=<TENANT>` renvoient 200. Elles ne disent rien de la
validité des credentials — pour ça, `npm test`.
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.