# leadtech-bmad-mcp — Design : MCP central sur NUC via Tailscale

> **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

Avoir **un seul MCP central**, hébergé une fois, accessible en **lecture ET écriture**
depuis n'importe quelle machine, **sans clone local ni Python** sur chaque client.

Aujourd'hui le MCP tourne en **stdio local** : chaque machine (Mac, NUC, ...) exécute son
propre binaire. Git garantit déjà une doctrine identique partout (source de vérité = `origin`),
mais pas une instance d'exécution unique. Le besoin (écriture centralisée + appel sans clone)
impose une **instance unique exposée en réseau**.

## Architecture cible

```
   Mac (Claude Code)  ─┐
   Autre machine      ─┼─ HTTP via Tailscale ─►  NUC : MCP leadtech-bmad (1 instance, Docker)
   Mobile / client léger ─┘                            ├─ lit  : /srv/helpers/_Assistant_Lead_Tech (clone Git)
                                                       └─ écrit: 95_a_capitaliser.md (ALLOW_WRITE=1)
```

## Décisions de fond (prises, à confirmer au moment d'implémenter)

| Sujet | Décision |
| --- | --- |
| Hébergement | NUC, service HTTP permanent (Docker ou systemd) |
| Transport | FastMCP en `streamable-http` (aujourd'hui `mcp.run()` = stdio par défaut → modif code) |
| Réseau / accès | **Tailscale** : bind sur l'interface du tailnet, pas d'exposition WAN, pas de reverse-proxy obligatoire. Auth réseau gérée par Tailscale. |
| URL client | `http://nuc.<tailnet>.ts.net:<port>/mcp` dans la config MCP Claude Code (`"type": "http"`) |
| Écriture | `LEADTECH_MCP_ALLOW_WRITE=1` (le MCP central est le seul à écrire → plus de conflit multi-machine sur `95_a_capitaliser.md`) |
| Source de vérité données | Git (`origin`). Le NUC héberge le clone de référence ; les éditions manuelles faites ailleurs remontent via Git. |

## Workflow de capitalisation à seuil (idée structurante)

Deux temps :

1. Le MCP **accumule** les propositions (`propose_capitalization`) dans `95_a_capitaliser.md`
   (append, write activé côté NUC).
2. Quand la section dépasse un **seuil** (N entrées / lignes), **déclencher le triage** :
   le tool `triage_capitalization()` (déjà implémenté) pré-analyse — doublons, portée
   PROJET vs KNOWLEDGE, décision + confiance par entrée — et le skill `capitalisation-triage`
   produit un **rapport**.
3. **Choix final humain** : tu valides quoi intégrer dans `knowledge/` ou router en mémoire projet.
   Rien ne part dans la base validée sans toi (cohérent avec la doctrine de capitalisation contrôlée).

Briques déjà en place :
- tool MCP `triage_capitalization(project_filter?, max_entries?)` → rapport structuré
- skill `skills/capitalisation-triage/SKILL.md`

Brique manquante : le **déclencheur de seuil** (compter les entrées, lancer le triage au-delà de N).

## État d'implémentation

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

- `route_to_project_memory` écrit dans `<projet>/CLAUDE.md` via `resolve-project-path.sh`. Depuis le NUC,
  ça vise `/srv/projects/<nom>`, pas les chemins Mac (`/Volumes/TeraSSD/...`). Tool utile uniquement
  pour les projets présents sur le NUC.
- Les clients Claude Code chargent les serveurs MCP **au démarrage de session** : tout changement
  d'URL nécessite une nouvelle session.
- Garder `ALLOW_WRITE` maîtrisé : le MCP central écrit, donc la sécurité d'écriture (limitée à
  `95_a_capitaliser.md` et CLAUDE.md projets, jamais `knowledge/*`) reste critique.
