Confier des appels d’API Nutanix (Prism Central, Move), Zabbix ou Jira à un agent comme Claude Code fait gagner un vrai temps, jusqu’au jour où il verrouille un compte ou annonce un succès qui n’a pas eu lieu. Voici les règles retenues après ces incidents, et comment les rendre mécaniques plutôt que simplement écrites dans les instructions.
1. Au premier 401, on s’arrête
L’incident fondateur : un outil d’initialisation lancé par l’agent a enchaîné environ 60 requêtes en 401 sur un compte d’annuaire. Résultat, compte verrouillé. Un agent qui « essaie autre chose » face à une erreur d’authentification est exactement ce qu’il ne faut pas.
Règle : au premier refus d’authentification, arrêt immédiat, aucun nouvel essai, on prévient l’humain.
2. Une seule requête de test avant un lot
Toute opération en lot commence par une requête qui valide l’authentification. Le lot ne part que si elle passe.
import sys
import requests
def verifier_auth(session: requests.Session, url_test: str) -> None:
r = session.get(url_test, timeout=10)
if r.status_code == 401:
sys.exit("401 sur la requête de test : arrêt, aucun nouvel essai.")
r.raise_for_status()
verifier_auth(session, URL_TEST) # une requête, pas soixante
for objet in lot:
traiter(session, objet) # même règle : un 401 arrête tout
3. Une écriture se contrôle par une lecture
L’agent ne déclare jamais un objet créé ou modifié sans l’avoir relu par une lecture indépendante dans l’API. Plusieurs fois, la sortie d’une commande d’écriture disait OK alors que l’état réel différait.
ecrire(objet_id, cible)
relu = lire(objet_id) # nouvel appel GET, pas la réponse de l'écriture
if any(relu.get(k) != v for k, v in cible.items()):
sys.exit("Écriture non confirmée par la relecture")
4. Simulation par défaut
Les scripts d’écriture tournent en dry-run sans drapeau explicite, et sauvegardent l’état avant tout PUT.
import argparse, json, pathlib, sys
p = argparse.ArgumentParser()
p.add_argument("--appliquer", action="store_true", help="sans ce drapeau : simulation")
args = p.parse_args()
avant = lire(objet_id)
pathlib.Path(f"avant-{objet_id}.json").write_text(json.dumps(avant, indent=2), encoding="utf-8")
if not args.appliquer:
print("SIMULATION, rien n'est écrit :", cible)
sys.exit(0)
ecrire(objet_id, cible)
5. L’irréversible reste humain
L’agent prépare les paramètres d’un plan de migration, l’humain le crée ou le démarre. Suppression, envoi d’un message, démarrage d’une réplication : validation explicite à chaque fois.
Même logique pour la documentation : une conclusion appuyée sur trois recherches web concordantes s’est révélée fausse. Interrogez l’outil lui-même (--help du binaire) avant de croire la doc web.
Des règles appliquées par des hooks
Ces règles ne restent pas seulement écrites dans les instructions : elles sont appliquées mécaniquement par des hooks Claude Code. D’après la référence, PreToolUse s’exécute avant un appel d’outil et peut le bloquer : un code de sortie 2 bloque l’action et le stderr du hook est transmis à Claude ; un code 0 sans sortie laisse le flux de permissions normal décider. Adapté de l’exemple de la doc :
#!/bin/bash
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")
if [[ "$command" == *"/start"* ]]; then
echo "Bloqué : démarrage réservé à un humain" >&2
exit 2
fi
exit 0
Les permissions complètent le dispositif : les règles sont évaluées dans l’ordre deny, ask, allow, et la première qui correspond l’emporte. Un hook qui renvoie allow ne contourne pas une règle deny. Réserve, d’après la même page : une extension (mod) qui gère la vérification des appels peut approuver un appel bloqué par un hook ou passer outre une règle deny, sauf si hook et règle sont posés en managed settings ; sur un poste partagé, c’est là qu’il faut les mettre. Pour un lancement sans surveillance, la doc du mode headless décrit le mode dontAsk, qui refuse tout ce qui aurait demandé une validation :
claude -p "Prépare le rapport du jour" --allowedTools "Read" --permission-mode dontAsk
Ce qu’il faut retenir
- Premier 401 : arrêt, aucun nouvel essai, l’humain décide.
- Une requête de test avant tout lot, et une lecture indépendante après toute écriture.
- Simulation par défaut, sauvegarde de l’état avant chaque PUT.
- L’irréversible reste humain, et l’on vérifie ce qu’un appel déclenche réellement.
- Les règles vivent dans des hooks
PreToolUse(code 2 = blocage) et des règles deny, pas seulement dans un prompt.
In English
Stop at the first 401, send one test request before a batch, read back after every write, run in simulation by default, and leave irreversible actions to a human. Five guardrails for an AI agent working on infrastructure APIs, enforced automatically with Claude Code hooks: a PreToolUse hook that exits with code 2 blocks the tool call.
Sources & vérification
Testé sur : Claude Code, usage quotidien en 2026
- Claude Code headless code.claude.com
- Claude Code permissions code.claude.com
- Claude Code hooks code.claude.com
- Expérience directe de l’auteur, en production (versions ci-dessus)
