Une API key placée directement dans un fichier .mcp.json peut sembler être un raccourci acceptable pendant le développement.
Le MCP server fonctionne, la connexion est établie et l’on prévoit de déplacer le secret dans une variable d’environnement « plus tard ».
Le problème commence lorsque cette configuration est commitée.
À partir de cet instant, le credential ne se trouve plus seulement sur la machine du développeur : il voyage avec le repository.
Cet exemple illustre un principe essentiel de sécurité avec Claude Code et MCP :
Un credential ne doit jamais voyager avec la configuration qui le référence.
Voyons pourquoi, comment corriger cette architecture et surtout comment empêcher Claude Code de reproduire accidentellement ce type d’erreur.
Le scénario : une API key dans .mcp.json
Un développeur doit connecter Claude Code à un data warehouse MCP server.
Le serveur utilise une API key associée à un service account.
Pour faire fonctionner rapidement la connexion, le développeur écrit directement la clé dans .mcp.json.
La configuration ressemble à ceci :
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer sk-abc123..."
}
}
Techniquement, cela fonctionne.
Le MCP client peut envoyer le credential dans le header Authorization et accéder au serveur.
Le développeur prévoit de déplacer ensuite la clé dans une environment variable.
Mais avant cette correction, .mcp.json est ajouté au repository afin que les autres membres de l’équipe puissent récupérer automatiquement la configuration.
La clé API est donc commitée avec le fichier.
Le credential commence à se propager
Dans le scénario du module, trois membres de l’équipe clonent le repository dans les 48 heures suivantes.
Un pipeline CI effectue également un clone.
La clé se retrouve alors dans plusieurs endroits :
Machine du développeur
│
├── Repository Git + historique
│
├── Machine développeur 2
│
├── Machine développeur 3
│
├── Machine développeur 4
│
└── CI runner
Le problème n’est donc plus limité au fichier original.
Le credential est désormais distribué avec le projet.
C’est précisément ce que doit éviter une architecture correcte de gestion des secrets.
« Je supprime la clé et je recommite » : pourquoi cela ne suffit pas
Le développeur découvre l’erreur.
Il modifie immédiatement .mcp.json.
La clé est supprimée et remplacée par une variable d’environnement.
Puis il effectue un nouveau commit.
La version actuelle du fichier ne contient effectivement plus le secret.
Mais l’ancien commit existe toujours.
Git conserve l’historique.
Le credential reste donc récupérable depuis cet historique.
C’est un principe essentiel :
Écraser ou supprimer un credential dans un commit ultérieur ne supprime pas le credential de l’historique du repository.
Il faut donc considérer une clé commitée comme compromise.
La conséquence : rotation obligatoire
Une fois le credential exposé, le remettre dans un emplacement sécurisé ne suffit plus.
Il faut effectuer une rotation.
Cela signifie :
- invalider l’ancien credential ;
- générer une nouvelle valeur ;
- fournir cette nouvelle valeur aux systèmes autorisés.
Dans le scénario étudié, cette rotation provoque un problème supplémentaire.
Deux services externes utilisent également la même clé.
Ils cessent donc de fonctionner lorsque l’ancien credential est révoqué.
Il faut trois heures à l’équipe pour identifier et réparer les différents consommateurs.
Cet incident révèle deux problèmes différents :
Problème 1
Credential stocké dans la configuration
Problème 2
Même credential utilisé par plusieurs consommateurs
sans gestion suffisamment claire de ses dépendances
La bonne configuration .mcp.json
La correction consiste à ne jamais inscrire la valeur du credential directement dans la configuration.
Au lieu de ceci :
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer sk-abc123..."
}
}
on utilise :
{
"type": "http",
"url": "https://warehouse.internal/mcp",
"headers": {
"Authorization": "Bearer ${WAREHOUSE_MCP_TOKEN}"
}
}
La différence est fondamentale.
.mcp.json ne contient plus le credential.
Il contient seulement le nom permettant de le retrouver au runtime.
Le secret réel est stocké ailleurs.
Le principe : séparer configuration et secret
On obtient alors cette architecture :
.mcp.json
│
│ référence
▼
${WAREHOUSE_MCP_TOKEN}
│
│ résolution au runtime
▼
credential réel
Le repository peut contenir .mcp.json.
Il peut être cloné par dix ou cent développeurs.
La valeur du secret ne voyage pas avec lui.
C’est la première pratique fondamentale du module :
Separation
Le credential ne doit jamais voyager avec la configuration qui le référence.
Où stocker la véritable valeur ?
Une fois le secret sorti du fichier, il faut décider où le conserver.
Le document distingue principalement deux solutions :
- environment variable ;
- managed secret store.
Le choix dépend du contexte.
Cas 1 : environment variable
Une environment variable convient lorsqu’un secret est utilisé localement ou injecté au moment de l’exécution.
Par exemple :
WAREHOUSE_MCP_TOKEN=...
Claude Code ou le MCP client récupère ensuite cette valeur lorsque .mcp.json référence :
${WAREHOUSE_MCP_TOKEN}
Cette approche est également adaptée à un pipeline CI.
Le système CI peut injecter le secret dans l’environnement du runner sans placer sa valeur dans le repository.
On obtient :
CI secret
↓
environment variable
↓
MCP configuration
↓
MCP server
Le credential n’a pas besoin d’être écrit dans le projet.
Cas 2 : secret store
Lorsque plusieurs personnes ou services utilisent le même secret, le document recommande plutôt un secret store.
Un secret store est un service géré qui :
- conserve les credentials ;
- les fournit aux consommateurs autorisés au runtime ;
- centralise leur gestion ;
- permet de savoir qui accède à quoi ;
- facilite leur rotation.
Cette centralisation évite un problème classique :
Secret
├── fichier service A
├── fichier service B
├── fichier service C
├── pipeline
└── machine développeur
Avec un secret store, on cherche plutôt à obtenir :
Secret store
/ | \
/ | \
Service A Service B Pipeline
Les consommateurs récupèrent la valeur lorsqu’ils en ont besoin.
Environment variable ou secret store ?
Le raisonnement proposé par le module peut être résumé ainsi :
| Situation | Solution |
|---|---|
| Secret utilisé localement | Environment variable |
| Secret injecté dans un pipeline | Environment variable |
| Secret temporaire lié à une exécution | Environment variable |
| Secret partagé entre plusieurs services | Secret store |
| Besoin de gestion centralisée | Secret store |
| Besoin d’audit des accès | Secret store |
Ce n’est donc pas simplement une question de technologie.
Il faut choisir le mécanisme correspondant au cycle de vie du credential.
La troisième pratique : rotation
Après separation et storage, le troisième principe est la rotation.
La rotation consiste à remplacer régulièrement un credential et à le remplacer immédiatement après toute suspicion d’exposition.
Le point essentiel est le suivant :
Un credential exposé ne peut pas redevenir secret.
Si quelqu’un a pu récupérer une API key, supprimer la copie visible ne permet pas de savoir si cette valeur a déjà été copiée ailleurs.
Il faut donc la remplacer.
Pourquoi une bonne architecture facilite la rotation
Supposons que l’application contienne directement :
sk-prod-warehouse-abc123
Changer cette clé nécessite de retrouver tous les endroits où elle a été copiée.
Avec une variable :
WAREHOUSE_MCP_TOKEN
le code ne dépend plus de la valeur.
On peut avoir :
WAREHOUSE_MCP_TOKEN
↓
ancienne valeur
puis :
WAREHOUSE_MCP_TOKEN
↓
nouvelle valeur
Le code et .mcp.json n’ont pas besoin d’être modifiés.
C’est l’un des principaux bénéfices opérationnels de la séparation entre configuration et secret.
Appliquer le least privilege
Le module ajoute une autre bonne pratique : chaque credential doit avoir le minimum de permissions nécessaire.
C’est le principe du least privilege.
Imaginons deux clés.
Clé A
→ accès administrateur au data warehouse
Clé B
→ lecture uniquement sur les données nécessaires
Si l’intégration MCP a uniquement besoin de lire certaines données, lui attribuer la clé A augmente inutilement le risque.
Le credential doit être limité à la tâche qu’il sert.
Ainsi, même en cas de compromission, le blast radius reste plus faible.
Connaître les consommateurs d’un credential
L’incident décrit dans le module révèle également un problème organisationnel.
Lorsque la clé est remplacée, deux autres services cessent de fonctionner.
Pourquoi ?
Parce qu’ils utilisaient également ce credential.
Pour rendre une rotation prévisible, il faut donc savoir :
Quels services utilisent cette clé ?
Maintenir cette information réduit le risque de découvrir les dépendances uniquement au moment où la rotation casse la production.
Empêcher Claude Code d’écrire un credential dans .mcp.json
La gestion correcte des secrets règle le problème architectural.
Mais une autre question apparaît :
Comment empêcher Claude Code de réintroduire accidentellement une clé inline ?
Le module recommande deux niveaux de protection.
Première couche : CLAUDE.md
On peut inscrire la convention de sécurité dans CLAUDE.md.
Par exemple :
## Credentials
Never write credential values inline in .mcp.json.
Credentials must be supplied through environment
variables or an approved secret-management mechanism.
Cette règle devient alors une instruction du projet.
Claude Code peut en tenir compte pendant les sessions.
Mais il existe une limite importante.
CLAUDE.md fournit une instruction au modèle.
Ce n’est pas une barrière technique déterministe.
Deuxième couche : PreToolUse hook
Pour une règle de sécurité critique, le document recommande d’ajouter un PreToolUse hook.
Le principe est le suivant :
Claude veut modifier .mcp.json
↓
PreToolUse
↓
inspection de l'opération
↓
credential détecté ?
/ \
oui non
↓ ↓
BLOCK ALLOW
Le hook inspecte les opérations d’écriture ou d’édition concernant .mcp.json.
S’il détecte un pattern ressemblant à un credential inline, il bloque l’opération.
Le document indique qu’un PreToolUse hook peut bloquer un tool call avant son exécution en sortant avec le code approprié.
CLAUDE.md vs Hook : distinction fondamentale
C’est l’un des concepts les plus importants à retenir.
CLAUDE.md
Communique l’intention :
« Ne mets jamais de credentials inline. »
PreToolUse hook
Applique la politique :
« Une opération contenant un credential inline ne sera pas exécutée. »
On peut résumer :
CLAUDE.md
↓
Instruction
↓
comportement attendu du modèle
PreToolUse hook
↓
Enforcement
↓
comportement imposé par le système
Pour une convention de code, une instruction peut être suffisante.
Pour une barrière de sécurité critique, un mécanisme déterministe est plus robuste.
Exemple : credential stocké sur un CI runner
Le module propose également une trace d’authentification de ce type :
[MCP Client] Connecting to https://data-api.internal/mcp ...
[MCP Client] GET /auth/token, 401 Unauthorized
[MCP Client] Reading credential from:
/home/jenkins/.config/mcp-credentials.json
[MCP Client] Credential value:
WAREHOUSE_TOKEN=sk-****[redacted]
[MCP Client] Retrying with credential, 401 Unauthorized
[MCP Client] Connection failed after 3 attempts
Une réponse superficielle serait :
Remplacer simplement la clé dans
mcp-credentials.json.
Mais cela conserverait le défaut architectural.
Le credential resterait stocké dans un fichier.
La correction proposée consiste à :
- effectuer une rotation de la clé rejetée ;
- retirer le credential du fichier ;
- injecter la nouvelle valeur comme environment variable dans le CI runner ;
- faire référencer cette variable par la configuration MCP.
On corrige donc simultanément l’incident et sa cause structurelle.
Les trois règles à retenir
La gestion des secrets présentée dans le module peut finalement être résumée par trois concepts.
1. Separation
credential ≠ configuration
Le secret ne doit jamais voyager avec le fichier qui le référence.
2. Storage
Secret local / CI
→ environment variable
Secret partagé / auditable
→ secret store
Le secret doit disposer d’un emplacement adapté à son usage.
3. Rotation
credential exposé
↓
rotation
↓
nouveau credential
Une valeur compromise doit être remplacée.
Ce qu’il faut retenir pour la certification
Plusieurs raisonnements sont particulièrement importants dans un scénario d’examen.
Une clé a été commitée puis supprimée dans le commit suivant
La clé doit toujours être considérée comme compromise.
Elle reste dans l’historique du repository.
Un .mcp.json doit être partagé avec toute l’équipe
Le fichier peut contenir une référence à une environment variable, mais pas la valeur du credential.
Plusieurs services utilisent le même secret et celui-ci doit être auditable
Un managed secret store est plus adapté qu’une multiplication des copies du secret.
Une API key vient d’être exposée
Elle doit être rotated.
La remettre simplement dans un emplacement sécurisé ne suffit pas.
Claude Code ne doit jamais inscrire de credentials dans .mcp.json
Une instruction dans CLAUDE.md explique la règle.
Un PreToolUse hook permet de l’imposer de façon déterministe.
Une intégration dispose de permissions beaucoup plus larges que nécessaire
Il faut appliquer le least privilege afin de réduire le blast radius en cas de compromission.
Le piège classique
Face à une règle de sécurité, il faut toujours distinguer :
Ce que Claude devrait faire
≠
Ce que le système lui permet de faire
Cette distinction dépasse largement la gestion des API keys.
Elle constitue un principe général pour concevoir des systèmes agentiques sûrs :
Les instructions orientent le comportement du modèle ; les contrôles déterministes imposent les limites de sécurité.
Conclusion
Une API key écrite directement dans .mcp.json transforme un fichier de configuration partageable en vecteur de propagation du secret.
La bonne architecture consiste à séparer les responsabilités :
.mcp.json
↓
référence le secret
↓
environment variable
ou secret store
↓
credential réel
Puis à protéger cette architecture côté Claude Code :
CLAUDE.md
↓
communique la règle
PreToolUse hook
↓
impose la règle
Enfin, trois pratiques structurent le cycle de vie du secret :
Separation → Storage → Rotation
Ce modèle permet non seulement de réduire le risque de fuite, mais aussi de rendre les credentials plus faciles à partager correctement, à auditer et à remplacer lorsqu’un incident survient.
Dans la suite de la série
MCP : choisir le bon transport et le bon scope
Nous verrons pourquoi stdio et HTTP répondent à des scénarios différents, comment distinguer les scopes Local, Project et Enterprise, et pourquoi une configuration techniquement valide peut malgré tout être inadaptée au mode de déploiement recherché.

