Files
_Assistant_Lead_Tech/mcp/leadtech_bmad_mcp/docker-compose.yml
T
MaksTinyWorkshop 1c876309f1 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.
2026-06-25 10:30:53 +02:00

82 lines
3.1 KiB
YAML

# leadtech-bmad-mcp — MCP central exposé au tailnet via sidecar Tailscale.
#
# Architecture (cf. docs/design_nuc_tailscale.md) :
#
# tailnet ──https──► [tailscale sidecar] ──http (réseau interne)──► [mcp:8080]
#
# - Le serveur MCP n'écoute QUE sur le réseau Docker interne (aucun port publié
# sur l'hôte → aucune exposition LAN/WAN).
# - Le sidecar rejoint le tailnet sous le hostname `leadtech-mcp` et termine TLS
# via `tailscale serve` (cert MagicDNS automatique).
# - URL client finale : https://leadtech-mcp.wyvern-snapper.ts.net/mcp
#
# Prérequis : copier .env.example en .env et y mettre LEADTECH_TS_AUTHKEY
# (auth key Tailscale Reusable, NON-Ephemeral — voir README / design doc).
name: leadtech-mcp
services:
mcp:
build:
context: .
dockerfile: Dockerfile
image: leadtech-bmad-mcp:local
container_name: leadtech-bmad-mcp
restart: unless-stopped
# Tourne en max:max (uid 1000) pour écrire l'index + 95_a_capitaliser.md
# dans le bind-mont sans casser les permissions du repo Git hôte.
user: "1000:1000"
environment:
LEADTECH_ROOT: /leadtech
LEADTECH_MCP_TRANSPORT: streamable-http
LEADTECH_MCP_HOST: 0.0.0.0
LEADTECH_MCP_PORT: "8080"
# Le sidecar termine TLS et forwarde le Host tailnet ; sans whitelist,
# la protection anti-DNS-rebinding renvoie 421. Inclut le hostname interne
# (mcp:8080) au cas où le proxy préserve l'autorité d'origine.
LEADTECH_MCP_ALLOWED_HOSTS: "leadtech-mcp.wyvern-snapper.ts.net,leadtech-mcp.wyvern-snapper.ts.net:443,mcp:8080,mcp"
# Instance centrale = seule à écrire (capitalisation centralisée).
LEADTECH_MCP_ALLOW_WRITE: "1"
volumes:
# Source de vérité : le clone Git Lead_tech (lecture + écriture buffer/index).
- /srv/helpers/_Assistant_Lead_Tech:/leadtech
# Les projets cibles de route_to_project_memory (mêmes chemins que sur l'hôte).
- /srv/projects:/srv/projects
networks:
- internal
# PAS de `ports:` — jamais exposé hors du réseau Docker interne.
tailscale:
image: tailscale/tailscale:v1.98.3
container_name: leadtech-mcp-ts
restart: unless-stopped
# ⚠ PAS de `hostname:` ici (collision DNS Docker service vs hostname,
# cf. knowledge/infra/risques/docker.md). TS_HOSTNAME suffit côté tailnet.
depends_on:
- mcp
environment:
TS_AUTHKEY: ${LEADTECH_TS_AUTHKEY:?LEADTECH_TS_AUTHKEY manquant — voir .env.example}
TS_HOSTNAME: leadtech-mcp
TS_STATE_DIR: /var/lib/tailscale
TS_SERVE_CONFIG: /config/serve.json
TS_USERSPACE: "false"
TS_EXTRA_ARGS: --accept-dns=false
volumes:
- /srv/docker-data/leadtech-mcp/tailscale/state:/var/lib/tailscale
- ./tailscale/serve.json:/config/serve.json:ro
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
networks:
- internal
labels:
- "homepage.group=Infra"
- "homepage.name=Lead_tech MCP"
- "homepage.href=https://leadtech-mcp.wyvern-snapper.ts.net/mcp"
networks:
internal:
name: leadtech-mcp-internal
driver: bridge