Files
mcp-wms-wiki/MONITORING.md
T

7.9 KiB

Suivi projet - Migration MCP WMS Wiki (stdio -> HTTP)

Derniere mise a jour : 2026-05-31

1. Objectif

Rendre le serveur MCP mcp-wms-wiki "HTTP-compliant" (transport Streamable HTTP) pour l'heberger en remote sur le serveur TrueNAS Scale "Fangtooth" en app Docker custom, expose derriere Nginx Proxy Manager + Cloudflare sur le domaine cible mcp-wms.arthur-ria.fr, consommable comme connecteur MCP distant depuis Claude Desktop.

2. Etat actuel (source : analyse Claude Code)

  • Serveur MCP Node.js ESM ("type": "module"), repo : git.arthur-ria.fr/Mecalux/mcp-wms-wiki.
  • Local sur disque : /home/arthur/dev/mcp-wms-wiki.
  • Transport actuel : stdio (StdioServerTransport, src/index.js:168-171). Lance par node src/index.js, forke par le client.
  • SDK : @modelcontextprotocol/sdk declare en "latest" mais lockfile fige 1.29.0 (= la version qui compte).
  • Serveur instancie en haut niveau : new McpServer({ name: 'wms-wiki', version: '1.0.0' }) (src/index.js:63-66).
  • 5 tools (src/index.js:70-126) : search_wiki, get_wiki_page, list_wiki_sections, get_glossary_term, get_related_pages.
  • 2 resources : wiki://overview, wiki://entities-map (src/index.js:130-164).
  • Handlers stateless : recoivent un ctx immuable construit au demarrage ({ pages, invertedIndex, glossary, wikiPath }).
  • Chargement wiki 100% local au demarrage (buildWikiIndex, buildGlossary, loadSpecialPage), tout en memoire. ~40 MB de markdown.
  • Une seule var d'env reellement consommee : WIKI_PATH. .env lu par un parseur maison (dotenv declare mais non utilise).
  • Pas de .gitignore -> .env est commite (aujourd'hui sans secret : WIKI_PATH=./wiki, DEBUG=false).
  • Contenu potentiellement confidentiel (sous-dossier limagrain/, refs Mecalux).

3. Architecture cible

  • Transport : Streamable HTTP, mode stateless, enableJsonResponse: true (reponse JSON one-shot, pas de SSE).
  • App Express : POST /mcp (+ GET /mcp route sur le meme handler), GET /healthz.
  • Un McpServer neuf par requete via factory createServer(ctx), le ctx etant construit une seule fois et partage (immuable).
  • Containerise (node:20-alpine), wiki embarque dans l'image, deploye en app Docker custom TrueNAS.
  • Reverse proxy NPM + Cloudflare. Build de l'image directement sur le NAS (pas de registry externe).

4. Decisions actees

  • Stateless : oui, avec factory createServer(ctx) par requete (correction du partage de McpServer).
  • enableJsonResponse : oui (true) -> simplifie tout le reverse proxy, supprime le risque SSE/timeout Cloudflare.
  • Wiki : embarque dans l'image en V1 (immuable, rollback par tag). Volume monte seulement si la cadence de maj devient genante.
  • Registry : build sur le TrueNAS depuis le repo (coherent avec le workflow Whisper).
  • Transport legacy /sse : non (YAGNI).
  • wiki_old_13-05-2026/ : a supprimer (40 MB inutiles).
  • SDK : epingler en 1.29.0, retirer "latest".
  • Deps : ajouter express ^4 + zod ^3 (alignee SDK), retirer dotenv.

Roadmap auth (point cle)

  • V1 : authless. Ajout direct dans Claude Desktop via Settings > Connectors (connecteur distant authless, aucun MCP local / pas de mcp-remote).
  • V2 : OAuth cote serveur. Le connecteur natif Claude Desktop gere OAuth + Dynamic Client Registration. Objectif explicite : eviter de faire tourner un MCP local juste pour l'auth. Le SDK fournit l'infra (server/auth/).
  • Le code V1 doit etre structure pour accueillir l'OAuth en V2 : middleware authMiddleware (pass-through en V1) monte uniquement devant /mcp, avec commentaire indiquant ou brancher l'OAuth.

5. Amendements techniques au plan initial

  • Bug evite : ne PAS partager un seul McpServer entre transports per-requete en stateless (cross-talk de reponses possible en concurrence). -> factory createServer(ctx) par requete, ctx partage immuable.
  • enableJsonResponse: true rend inutile toute la conf NPM "Advanced" SSE (proxy_buffering off, read_timeout 3600, websockets) et supprime le risque Cloudflare "coupe SSE a 100s".
  • DNS rebinding : enableDnsRebindingProtection: true + allowedHosts pilote par MCP_ALLOWED_HOSTS. Verifier que NPM forwarde bien Host: $host (l'app doit voir mcp-wms.arthur-ria.fr, pas 127.0.0.1:3000). Tester au curl post-deploy.
  • Config par env : PORT (3000), HOST (0.0.0.0), WIKI_PATH (/app/wiki), MCP_ALLOWED_HOSTS.

6. Securite

  • BLOQUANT (et critique car V1 authless) : audit traversee de repertoire sur get_wiki_page (src/services/page.js). Resoudre par rapport a wikiPath, rejeter .. et chemins absolus, confiner au sous-arbre. Corriger avant toute exposition.
  • Authless + contenu confidentiel : tant qu'OAuth (V2) n'est pas la, ne pas exposer publiquement (LAN / Tailscale / VPN) OU mettre une Access List NPM / Cloudflare Access devant. Si passage en Cloudflare orange-cloud, desactiver le cache sur /mcp.
  • Secrets : .gitignore + git rm --cached .env + .env.example. En prod TrueNAS, variables injectees par la definition de l'app, pas de .env. Rien a purger dans l'historique aujourd'hui (pas de secret commite), mais discipline a partir de maintenant.

7. Deploiement (cible)

  • TrueNAS app Docker custom : service unique mcp-wms-wiki, image buildee sur le NAS, restart: unless-stopped, env (PORT, HOST, WIKI_PATH, MCP_ALLOWED_HOSTS), pas de volume (wiki embarque).
  • NPM : proxy host mcp-wms.arthur-ria.fr -> http://<ip_app>:3000, HTTPS force + redirect. Avec JSON (pas de SSE), pas de directives avancees particulieres a prevoir.
  • Cloudflare : DNS-only (gris) pour valider, puis orange possible (le risque SSE ayant disparu avec JSON). Si orange + contenu confidentiel : pas de cache sur /mcp.

8. Checklist d'implementation (V1)

  • Factory createServer(ctx) + ctx construit une seule fois.
  • App Express : POST /mcp, GET /mcp (meme handler), GET /healthz.
  • Transport per-requete : stateless + enableJsonResponse: true + enableDnsRebindingProtection: true + allowedHosts.
  • Middleware authMiddleware pass-through devant /mcp (placeholder V2 OAuth).
  • package.json : SDK epingle 1.29.0, +express, +zod, -dotenv, engines node >=20.
  • Audit + correction traversee de repertoire sur page.js.
  • Dockerfile (node:20-alpine, npm ci --omit=dev, wiki embarque, USER node, HEALTHCHECK).
  • .dockerignore, .gitignore, .env.example, git rm --cached .env.
  • Suppression wiki_old_13-05-2026/.
  • Snippet docker-compose TrueNAS + script smoke-test curl.
  • Deploy TrueNAS + conf NPM + DNS Cloudflare.
  • Test : ajout connecteur authless dans Claude Desktop (Settings > Connectors).

9. Questions ouvertes / a trancher plus tard

  • V2 : choix precis du fournisseur OAuth cote serveur (l'utilisateur a l'habitude de FortiToken).
  • Le MCP "API" separe doit-il aussi migrer en HTTP et partager le domaine (/api + /wiki) ou un sous-domaine distinct ? A cadrer car influence la conf NPM.
  • Multi-utilisateurs prevus ? (sinon pas de rate-limiting / logs d'acces necessaires).

10. References

  • Repo : https://git.arthur-ria.fr/Mecalux/mcp-wms-wiki
  • Local : /home/arthur/dev/mcp-wms-wiki
  • Domaine cible : mcp-wms.arthur-ria.fr (endpoint /mcp)
  • SDK : @modelcontextprotocol/sdk 1.29.0 ; transport cible StreamableHTTPServerTransport (dist/esm/server/streamableHttp.js)
  • Fichiers cles : src/index.js (transport l.168-171, McpServer l.63, tools l.70-126, resources l.130-164), src/services/page.js (a auditer), src/config, src/resources, src/services, src/tools
  • Infra : TrueNAS Scale 25 Fangtooth, Nginx Proxy Manager, Cloudflare

11. Etat d'avancement

  • Analyse du repo et du transport actuel (Claude Code)
  • Plan de migration + critique + amendements
  • Decisions actees (stateless, JSON, wiki embarque, authless V1 / OAuth V2)
  • Implementation V1 (en cours / a lancer cote Claude Code)
  • Deploiement TrueNAS
  • V2 OAuth