# 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 `\\\...` — 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 `_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:///ApplicationService/api/healthcheck?tenantCode=` et `.../api/ready?tenantCode=` 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 -- ` (D2) | | Boucle `refresh failed` / `Authenticating` | credentials invalides | `npm test` | | `ETIMEDOUT` / `ECONNREFUSED` | host injoignable (VPN, pare-feu) | `Test-NetConnection -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.