.png%3F2026-05-30T11%253A41%253A17.038Z&w=3840&q=100)
Comment j'ai construit un site de contenu où Claude écrit, traduit et publie directement dans Payload CMS via le protocole MCP, sans API intermédiaire
L'idée vient d'une frustration concrète. Maintenir un site de contenu en deux langues, avec des fiches produits qui changent (prix, disponibilités, nouveaux modèles), c'est un travail répétitif qui consomme du temps de cerveau pour très peu de valeur ajoutée. Le contenu existe déjà : dans la tête du rédacteur, dans les specs constructeur, dans les tests publiés ailleurs. Ce qui prend du temps, c'est la mise en forme. Structurer en H2, glisser un tableau comparatif, écrire la FAQ, gérer les liens affiliés, traduire.
Or c'est exactement ce qu'un LLM fait bien. La question devenait : comment lui donner les bons outils pour qu'il ne se contente pas de proposer un brouillon, mais qu'il publie vraiment, dans la bonne structure, avec les bonnes données.
La réponse a un nom : MCP.
Le Model Context Protocol est un standard open-source d'Anthropic qui permet à un LLM de dialoguer avec des outils externes via un protocole structuré. Payload propose un plugin officiel (@payloadcms/plugin-mcp) qui transforme chaque collection en un ensemble d'outils MCP exposés à Claude.
Concrètement, depuis Claude Desktop ou Claude Code, j'ai accès à findArticles, writeArticle, getProductTitles, et une dizaine d'autres outils qui parlent directement à la base. Pas d'API REST intermédiaire. Pas de token à gérer. Le LLM appelle une fonction, le plugin la traduit en payload.create() ou payload.find(), MongoDB répond.
L'outil le plus important, c'est writeArticle. Il accepte un Markdown enrichi avec des marqueurs de blocs personnalisés, et il s'occupe de tout convertir en Lexical AST, le format interne de Payload.
C'est là que le projet se complique. Payload stocke ses contenus dans un AST Lexical, un arbre JSON où chaque nœud est typé. Demander à un LLM d'écrire directement du Lexical, c'est demander à un humain d'écrire du XML à la main : faisable, mais douloureux, et chaque virgule mal placée casse le rendu.
L'alternative retenue : laisser Claude écrire du Markdown classique, avec des marqueurs entre crochets pour les blocs structurés.
Le parser fait deux choses non triviales.
D'abord, la résolution des identifiants. Le LLM raisonne avec des slugs lisibles (segway-navimow-i105e), mais Payload stocke des ObjectIDs MongoDB. Le parser collecte tous les slugs cités dans le Markdown, exécute une seule requête payload.find() avec un filtre or sur l'ensemble, et construit une Map slug vers ObjectID. Un slug introuvable lève une erreur explicite plutôt que de produire un lien mort.
Ensuite, le groupage des marqueurs consécutifs. Les blocs [FAQ_ITEM] et [HOW_TO_STEP] se fusionnent automatiquement : plusieurs lignes du même type d'affilée deviennent un seul bloc Payload. Le parser maintient un buffer interne et le "flush" dès qu'il rencontre autre chose. Côté Claude, l'écriture reste naturelle, une ligne par question, une ligne par étape.
Le LLM écrit en Markdown, le serveur publie en Lexical. La friction disparaît dans la traduction.
En production, un comportement inattendu : Claude truffe les textes de tirets cadratins. Partout. À la place des virgules, des parenthèses, des deux-points. C'est un tic stylistique des modèles entraînés sur du contenu web anglo-saxon, et ça donne au site une signature "écrit par IA" très repérable.
La charte éditoriale du Coin Vert les interdit. Mais les ajouter à CLAUDE.md (le fichier d'instructions persistantes lu par Claude au démarrage de chaque session) n'a pas suffi : le modèle replonge dans ses réflexes dès qu'il génère un peu de texte.
D'où un script de nettoyage, remove-em-dashes.ts. Il se connecte directement à Payload, sans HTTP, parcourt tous les articles en FR et EN, traverse récursivement le JSON des champs (y compris l'AST Lexical, profondément imbriqué) et supprime chaque occurrence. Quelques précautions techniques : ignorer les clés structurelles (slug, blockType, format...) pour ne pas corrompre la base, renvoyer null quand rien n'a changé pour éviter les faux updates, supporter un --dry-run pour auditer.
L'ironie n'est pas perdue. Un projet qui utilise massivement un LLM pour écrire, et qui doit maintenir un script à temps plein pour effacer ses tics stylistiques.
Chaque produit a un champ asin, l'identifiant universel Amazon. Au moment du rendu côté serveur, le header Accept-Language du visiteur est lu et l'URL affiliée est construite avec le bon domaine. Un visiteur britannique clique sur amazon.co.uk, un français sur amazon.fr, un allemand sur amazon.de. Cinq domaines supportés (FR, UK, US, DE, CA), une seule source de vérité dans la base.
Les prix sont rafraîchis via Rainforest API, un service de scraping Amazon normalisé qui évite d'avoir à scraper soi-même (et de violer les ToS). Le script tourne manuellement, jamais en cron : les données ne sont pas critiques au point de cramer des crédits API toutes les heures.
Le choix le plus structurant a déjà été évoqué : Payload embarqué dans Next.js via @payloadcms/next. Un seul package.json, un seul build, un seul déploiement Vercel. Les API routes appellent payload.find() en direct, sans HTTP intermédiaire.
Contrepartie à connaître : pendant le build, Next.js doit importer payload.config.ts pour générer les types, ce qui rend les secrets (URI MongoDB, clés API) nécessaires dès la phase de build et pas seulement au runtime. Sur Vercel, ça oblige à bien distinguer les variables d'environnement build-time des runtime, sous peine de builds qui plantent silencieusement.
Trois points honnêtes, après plusieurs mois de production.
D'abord, MCP change réellement le rapport à l'IA dans une codebase. Tant que Claude est un chat à côté, il propose. Dès qu'il a des outils typés, il agit, et c'est un saut qualitatif net. Mais ça déplace aussi la difficulté : ce n'est plus le prompt qui est dur à écrire, c'est l'outil qui est dur à concevoir. Un writeArticle mal pensé devient impossible à utiliser correctement, peu importe l'intelligence du modèle derrière.
Ensuite, le monorepo à un seul processus simplifie tellement l'infra qu'on en oublie les pièges. Le couplage CMS/front est une dette qui peut se réveiller. Si Le Coin Vert devait demain alimenter une app mobile, il faudrait extraire une vraie API publique, et le confort actuel des appels directs deviendrait un frein.
Enfin, les LLM ont des tics, et ces tics finissent dans la base de données. Travailler avec une IA en production, ce n'est pas seulement piloter ses sorties, c'est aussi assumer la maintenance corrective de ses habitudes. Le script remove-em-dashes n'est probablement pas le dernier de son genre.