leadtech-bmad-mcp: serveur MCP central HTTP via sidecar Tailscale

Transforme le MCP leadtech-bmad de stdio local en service HTTP central
conteneurisé, accessible depuis tout périphérique du tailnet.

- server.main(): transport piloté par LEADTECH_MCP_TRANSPORT (stdio par
  défaut → aucune régression locale; streamable-http pour le central).
  Host/port via LEADTECH_MCP_HOST/_PORT.
- _build_transport_security(): whitelist d'hôtes via LEADTECH_MCP_ALLOWED_HOSTS
  pour lever la protection anti-DNS-rebinding (HTTP 421) derrière le sidecar.
- Dockerfile (python:3.11-slim, build index au démarrage, lance le serveur HTTP).
- docker-compose.yml: service mcp (réseau interne, aucun port publié) +
  sidecar tailscale (tailscale serve TLS MagicDNS). user 1000:1000 pour
  l'écriture dans le bind-mont. ALLOW_WRITE=1 sur l'instance centrale.
- tailscale/serve.json, .env.example, mcp.config.http.example.json.
- .gitignore: ignore le .env (secrets), garde .env.example.
- docs/design_nuc_tailscale.md: statut passé à IMPLÉMENTÉ + URL réelle.

Validé: handshake MCP initialize HTTPS via tailnet → 200, 7 tools listables,
écriture 95_a_capitaliser.md confirmée, 79 tests verts.
This commit is contained in:
MaksTinyWorkshop
2026-06-25 10:30:53 +02:00
parent 2fa34f0f6f
commit 1c876309f1
8 changed files with 240 additions and 13 deletions
@@ -1,8 +1,13 @@
# leadtech-bmad-mcp — Design : MCP central sur NUC via Tailscale
> **Statut : PISTE / chantier non démarré** (2026-06-25).
> Ce document capture un design abouti mais non implémenté. Ce n'est PAS une décision figée
> (pas dans `40_decisions_et_archi.md` tant que le chantier n'est pas lancé). À reprendre tel quel.
> **Statut : IMPLÉMENTÉ — déploiement à finaliser** (mise à jour 2026-06-25).
> Le transport HTTP, l'image Docker, le compose et le sidecar Tailscale sont en place et
> testés (handshake MCP `streamable-http` OK, 79 tests verts). Reste à fournir l'authkey
> Tailscale et lancer `docker compose up` sur le host. Voir "État d'implémentation" plus bas.
>
> Host réel : `docker-dev.wyvern-snapper.ts.net` (le "NUC" du design ; tailnet `wyvern-snapper`).
> Hostname tailnet dédié du MCP : **`leadtech-mcp`** (sidecar) →
> URL client : **`https://leadtech-mcp.wyvern-snapper.ts.net/mcp`**.
## Besoin
@@ -53,15 +58,34 @@ Briques déjà en place :
Brique manquante : le **déclencheur de seuil** (compter les entrées, lancer le triage au-delà de N).
## Reste à faire (quand le chantier démarre)
## État d'implémentation
- [ ] POC : passer le serveur en transport HTTP (`mcp.run(transport="streamable-http", host=..., port=...)`) et tester en local
- [ ] Dockerfile + compose (bind `/srv/helpers/_Assistant_Lead_Tech`, env `LEADTECH_ROOT`, `LEADTECH_MCP_ALLOW_WRITE`)
- [ ] Bind sur l'interface Tailscale du NUC ; vérifier l'accès depuis le Mac via le tailnet
- [ ] Reconfigurer les clients : `"leadtech-bmad": { "type": "http", "url": "http://nuc.<tailnet>.ts.net:<port>/mcp" }`
- [ ] Déclencheur de seuil pour le triage de capitalisation
- [ ] Décider du flux Git de `95_a_capitaliser.md` (commit/push manuel au NUC après validation — option retenue par défaut)
- [ ] S'assurer que les projets cibles de `route_to_project_memory` sont clonés sur le NUC (sinon tool sans effet pour eux)
Fait (code + infra dans `mcp/leadtech_bmad_mcp/`) :
- [x] Transport HTTP : `server.main()` lit `LEADTECH_MCP_TRANSPORT` (`stdio` par défaut → aucune
régression locale ; `streamable-http` pour le central). Host/port via `LEADTECH_MCP_HOST`/`_PORT`.
- [x] `Dockerfile` (`python:3.11-slim`, install `-e .`, build index au démarrage, lance le serveur HTTP).
- [x] `docker-compose.yml` : service `mcp` (réseau interne `leadtech-mcp-internal`, **aucun port publié**)
+ sidecar `tailscale` (`tailscale serve` TLS MagicDNS). `user: "1000:1000"` pour écrire l'index
et `95_a_capitaliser.md` dans le bind-mont sans casser les permissions du repo.
- [x] `tailscale/serve.json` (proxy `/``http://mcp:8080`, pas de Funnel).
- [x] `.env.example` (authkey) + `.env` gitignoré.
- [x] `LEADTECH_MCP_ALLOW_WRITE=1` câblé sur le service central.
- [x] Validé : handshake MCP `initialize` sur `/mcp` → HTTP 200 + `serverInfo` ; 79 tests verts dans l'image.
Reste à faire pour mettre en service :
- [ ] Générer une authkey Tailscale (Reusable, NON-Ephemeral), la mettre dans `.env` (`LEADTECH_TS_AUTHKEY`).
- [ ] `docker compose up -d --build` sur le host, approuver le nœud `leadtech-mcp` si machine-approval,
puis **disable key expiry** sur ce nœud.
- [ ] Vérifier depuis le Mac : `curl https://leadtech-mcp.wyvern-snapper.ts.net/mcp` (handshake).
- [ ] Reconfigurer les clients (cf. `mcp.config.http.example.json`) puis relancer la session Claude Code.
Pistes non bloquantes (hors périmètre de ce déploiement) :
- [ ] Déclencheur de seuil pour le triage de capitalisation (brique manquante identifiée plus haut).
- [ ] Flux Git de `95_a_capitaliser.md` (commit/push manuel au host après validation — option retenue par défaut).
- [ ] Vérifier que les projets cibles de `route_to_project_memory` sont présents (ici `/srv/projects/*` ✅, bind-monté).
## Points d'attention