7.9 KiB
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 parnode src/index.js, forke par le client. - SDK :
@modelcontextprotocol/sdkdeclare 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
ctximmuable 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..envlu par un parseur maison (dotenv declare mais non utilise). - Pas de
.gitignore->.envest 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 /mcproute sur le meme handler),GET /healthz. - Un McpServer neuf par requete via factory
createServer(ctx), lectxetant 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), retirerdotenv.
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: truerend 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+allowedHostspilote parMCP_ALLOWED_HOSTS. Verifier que NPM forwarde bienHost: $host(l'app doit voirmcp-wms.arthur-ria.fr, pas127.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 awikiPath, 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
authMiddlewarepass-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/sdk1.29.0 ; transport cibleStreamableHTTPServerTransport(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