---
title: "Stratégie 4 : séparer le contenu (MCP) de la structure (Git)"
description: "Architecture pour un site Hugo géré simultanément par MCP (contenu) et Git (structure). Le `.gitignore content/` qui résout tout. Pourquoi MCP ne devrait jamais commiter."
url: "https://www.arleo.eu/posts/strategie-4-mcp-vs-git/"
language: "fr"
datePublished: "2026-05-09T12:40:58+02:00"
dateModified: "2026-05-15T23:51:25+02:00"
tags: ["mcp","git","hugo","architecture","homelab"]
categories: ["infrastructure"]
contentSignal: "ai-train=no, search=yes, ai-input=yes"
---

# Stratégie 4 : séparer le contenu (MCP) de la structure (Git)
Architecture pour un site Hugo géré simultanément par MCP (contenu) et Git (structure). Le `.gitignore content/` qui résout tout. Pourquoi MCP ne devrait jamais commiter.



## Le problème

Tu as un site Hugo. Tu veux pouvoir :

1. **Éditer le contenu via Claude.ai** (publier un article, corriger une coquille, mettre à jour un draft) sans toucher à un terminal SSH.
2. **Versionner la structure** (layouts, themes, hugo.toml, scripts de déploiement) dans Git, comme un dev sérieux.

Premier réflexe : « tout dans Git ». Les articles aussi. Le MCP commit, push, le webhook GitHub fait un rebuild. Propre, dans la philosophie GitOps.

Sauf que ça ne marche pas si bien. Voici pourquoi, et la solution simple que j'appelle la **Stratégie 4**.

## Pourquoi « tout dans Git » casse en pratique

Imaginons que ton MCP fait un `git commit` à chaque `create_page`. Stratégie naïve, mais souvent proposée. Voici les problèmes :

### Problème 1 — Conflit MCP ↔ Git

Tu push depuis ton laptop un nouveau layout (`layouts/index.html` modifié). Au même moment, le MCP est en train de commiter une nouvelle version d'un article. Race condition, le MCP peut faire un `git pull --rebase` qui échoue, ou pire, écraser ton commit local.

### Problème 2 — Identité Git du MCP

Le MCP commit en tant que qui ? Avec quelle clé GPG ? Si tu as une politique « commits signés obligatoires », le MCP doit gérer une clé GPG, qu'il faut sécuriser, faire tourner, etc.

### Problème 3 — Auto-commit indésirable

Tu fais un test, tu crées un article draft pour expérimenter, tu le supprimes. Mais le MCP a déjà commité. Maintenant tu as un commit "wip test" dans l'historique, à rebaser ou squasher manuellement.

### Problème 4 — Réversibilité asymétrique

Un `git revert` côté repo n'a aucun effet sur les fichiers que le MCP a déjà créés. Tu te retrouves avec un repo et un état filesystem désynchronisés.

## La Stratégie 4 : séparer les zones

L'idée : **MCP et Git n'écrivent jamais sur les mêmes fichiers**.

| Zone | Qui édite | Versionné dans Git ? |
|------|-----------|----------------------|
| `content/**/*.md` | **MCP exclusivement** | ❌ NON (`.gitignore`) |
| `layouts/`, `themes/`, `static/`, `hugo.toml`, `deploy.sh` | **Git push exclusivement** | ✅ OUI |

Le `.gitignore` côté repo :

```
content/
public/
resources/
```

Implications :

- Le MCP peut écrire dans `content/` quand il veut. Aucun conflit Git possible.
- Tu peux faire `git reset --hard` côté repo en confiance — `content/` reste intact.
- Pas besoin que le MCP gère une identité Git.
- Pas de "commit pollution".

## Trade-off : pas de versioning du contenu

Tu perds le versioning Git du contenu. C'est une perte réelle :

- Pas de `git blame` sur un article pour voir qui a écrit quoi.
- Pas de `git log content/csp-nonce/index.fr.md` pour voir l'historique.
- Pas de PR review pour les articles.

**Mitigation** : snapshots VM + backup `content/` chiffré quotidien sur QNAP. Ça couvre la **récupération en cas de désastre**, mais pas le versioning fin (qui-a-changé-quoi-quand).

Pour mon homelab perso (un seul auteur, articles techniques, pas de workflow de validation éditoriale), c'est un trade-off acceptable. Pour un blog d'équipe avec 10 contributeurs, je reconsidérerais.

## Architecture finale

<!--more-->
<!-- mermaid-source:Z3JhcGggVEIKICAgIHN1YmdyYXBoIGNsaWVudHNbIkNsaWVudHMiXQogICAgICAgIEMxWyJDbGF1ZGUuYWk8YnIvPsOpZGl0aW9uIGNvbnRlbnUiXQogICAgICAgIEMyWyJWUyBDb2RlIC8gU1NIPGJyLz7DqWRpdGlvbiBzdHJ1Y3R1cmUiXQogICAgZW5kCiAgICBzdWJncmFwaCBudWNbIk5VQyDigJQgcHJveHkgT0F1dGgiXQogICAgICAgIFBbIm1jcC1vYXV0aC1wcm94eSA6ODA4NCJdCiAgICBlbmQKICAgIHN1YmdyYXBoIHZtWyJWTSBIdWdvIl0KICAgICAgICBNQ1BbImh1Z28tbWNwIEZhc3RBUEk8YnIvPjcgdG9vbHMgTUNQIl0KICAgICAgICBzdWJncmFwaCB6b25lX21jcFsiWm9uZSBNQ1Ag4oCUIGNvbnRlbnQvIl0KICAgICAgICAgICAgTURbIiouZnIubWQgwrcgKi5lbi5tZCJdCiAgICAgICAgZW5kCiAgICAgICAgc3ViZ3JhcGggem9uZV9naXRbIlpvbmUgR2l0IOKAlCBsYXlvdXRzLyB0aGVtZXMvIHN0YXRpYy8iXQogICAgICAgICAgICBHSVRbIuKGkCBnaXQgcHVsbCBHaXRIdWIiXQogICAgICAgIGVuZAogICAgZW5kCiAgICBDMSAtLT58IkhUVFBTIE9BdXRoIDIuMSJ8IFAgLS0+fCJIVFRQUyBpbnRlcm5lInwgTUNQCiAgICBNQ1AgLS0+IHpvbmVfbWNwCiAgICBDMiAtLT58IlNTSCArIGdpdCBwdXNoInwgem9uZV9naXQKICAgIHpvbmVfbWNwIC0uICIuZ2l0aWdub3JlIGNvbnRlbnQvIOKAlCBwYXMgZGUgY29uZmxpdHMiIC4tPiB6b25lX2dpdAogICAgc3R5bGUgem9uZV9tY3AgZmlsbDojMWEzYTVjCiAgICBzdHlsZSB6b25lX2dpdCBmaWxsOiMxNDVhMzI= -->
{{< mermaid-svg hash="e16c670e" >}}

```
┌────────────────────┐         ┌──────────────────────┐
│  Claude.ai client  │  HTTPS  │  hugo-mcp-proxy NUC  │
│  (édition contenu) │ ──────► │  (mcp-oauth-proxy)   │
└────────────────────┘         └──────────────────────┘
                                          │
                                          │  HTTP interne
                                          ▼
                              ┌────────────────────────┐
                              │  Hugo MCP Server VM    │
                              │  (FastAPI, 7 tools)    │
                              │                        │
                              │  ZONE MCP :            │
                              │  /home/jm/hugo-site/   │
                              │  └── content/          │
                              └────────────────────────┘

┌────────────────────┐         ┌──────────────────────┐
│  Laptop dev        │  SSH    │  VM Hugo             │
│  (édition layout)  │ ──────► │                      │
└────────────────────┘         │  ZONE GIT :          │
                               │  /home/jm/hugo-site/ │
                               │  ├── layouts/        │
                               │  ├── themes/         │
                               │  ├── static/         │
                               │  ├── hugo.toml       │
                               │  └── deploy.sh       │
                               └──────────────────────┘
```

Les deux zones partagent le même filesystem, mais aucun fichier en commun. Pas d'overlap, pas de conflit.

## Webhook Git (optionnel mais utile)

Pour automatiser le rebuild quand tu push une nouvelle version de `hugo.toml` :

1. GitHub envoie un webhook POST sur `https://mcp-hugo.arleo.eu/webhook/git`
2. L'endpoint valide le HMAC-SHA256 (secret partagé)
3. Si OK, déclenche `git fetch && git reset --hard origin/main && hugo --minify && rsync`
4. `git reset --hard` peut être appelé en confiance — il ne touche jamais à `content/` (gitignored)

Le rate limiting est important : 1 rebuild/min max, pour éviter qu'un push spam (ou un attaquant qui réussit à signer un payload) ne sature le serveur.

## Ce que ça permet

Concrètement, dans ma session de travail typique :

- J'écris un article via Claude.ai → MCP crée le fichier `.md` dans `content/`. Hugo build, déploie, purge Cloudflare. 5 secondes total.
- Je modifie un partial du thème via VS Code → SSH push direct ou `git push` → webhook → rebuild. 30 secondes.

Les deux flux sont **indépendants**. Si le MCP crash, je peux quand même push du code Git. Si Git tombe, le MCP continue de servir.

## Variantes considérées et rejetées

### Stratégie 1 — MCP commit chaque édition

Tous les problèmes listés ci-dessus. Rejetée pour V1, peut-être V2 si je passe à un workflow multi-auteur.

### Stratégie 2 — Branches séparées

`main` pour la structure, `content` pour les articles. MCP push uniquement sur `content`, Git push uniquement sur `main`. Élégant mais complexe : il faut gérer les merges, les rebases, et l'état effectif sur prod devient difficile à raisonner.

### Stratégie 3 — Submodule Git

`content/` comme submodule pointant vers un autre repo. Le MCP commite dans le submodule. Trop de cérémonie pour un homelab.

### Stratégie 4 — Zones séparées (celle-ci)

Simple. Robuste. Pas de cérémonie. Trade-off connu (pas de versioning fin du contenu) mais accepté pour mon usage.

## Conclusion

Quand on combine deux outils qui veulent éditer les mêmes fichiers (MCP et Git), la solution n'est pas de les faire **coopérer** (synchroniser, locker, merger). C'est de **les empêcher d'éditer les mêmes fichiers**.

`.gitignore content/` est 1 ligne de config qui résout 4 classes de problèmes. C'est de l'architecture en faisant moins, pas plus.

L'implémentation effective (webhook, IPAddressAllow GitHub ranges, HMAC) est documentée dans le brief `webhook-git-rebuild` archivé dans le repo `hugo-mcp/docs/backlogs/`. À déployer dans une prochaine session — pour l'instant, l'édition Git se fait en SSH direct, ce qui marche aussi.



## Tags

- mcp
- git
- hugo
- architecture
- homelab

## Categories

- infrastructure
