Le hook PermissionRequest de Claude Code s'exécute au moment où Claude Code s'apprête à t'afficher une demande de permission, et il peut y répondre à ta place. Pour autoriser, le hook sort avec 0 et affiche {"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}} ; pour refuser, il utilise "behavior": "deny" avec un message pour Claude. Il filtre sur le nom de l'outil, ne peut pas outrepasser tes règles de refus, et le code de retour 2 n'a aucun effet sur cet événement. Si le hook n'affiche aucune décision, la demande normale apparaît.
01PermissionRequest comparé à PreToolUse et Notification
Trois événements de hook concernent les permissions, et ils ont des rôles différents :
PreToolUseAvant chaque appel d'outil, qu'il nécessite une permission ou nonOui : allow, deny, ask ou deferPermissionRequestUniquement quand Claude Code s'apprête à te demanderOui : allow ou denyNotification (permission_prompt)Quand une demande attend depuis environ six secondes pendant ton absenceNonUtilise PermissionRequest quand ce sont les demandes elles-mêmes qui t'intéressent : y répondre, les journaliser ou les envoyer ailleurs. Il se déclenche immédiatement, sans délai de six secondes. Utilise PreToolUse pour les règles qui doivent s'appliquer à chaque appel, comme « ne jamais lancer rm -rf », car il s'exécute même quand aucune demande n'apparaîtrait. Le guide du Notification hook explique le timing de cet événement.
02Ce que reçoit le hook
En plus des champs communs, un hook PermissionRequest reçoit le tool_name et le tool_input de l'appel que Claude veut faire, ainsi qu'un tableau optionnel permission_suggestions :
{
"session_id": "abc123",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}
permission_suggestions contient les modifications de règles que Claude Code suggérerait pour cette demande. Ce n'est pas une copie exacte des options de la boîte de dialogue, car chaque boîte de dialogue construit ses propres options.
03L'objet decision
Renvoie un decision dans hookSpecificOutput :
behavior"allow" ou "deny"updatedInputAllow uniquement. Remplace toute l'entrée de l'outil, donc inclus aussi les champs inchangésupdatedPermissionsAllow uniquement. Modifications de permissions à appliquer, comme ajouter une règle ou changer de modemessageDeny uniquement. Explique la raison à ClaudeinterruptDeny uniquement. true arrête aussi ClaudeDeux règles à retenir :
- Les règles deny et ask s'appliquent toujours. Un hook qui renvoie
"allow"ne passe pas outre une règle deny correspondante dans tes settings, et l'input modifié est revérifié avec ces règles. - Le code de sortie
2est ignoré ici. Sur la plupart des événements, sortir avec2bloque, mais un hookPermissionRequestqui sort avec2sans objetdecisionlaisse le prompt tel quel. Pour refuser, renvoie la décision.
04Exemple : approuver automatiquement un prompt
L'exemple d'Anthropic approuve ExitPlanMode, le prompt qui s'affiche quand Claude a terminé un plan, pour qu'on ne te le demande pas à chaque fois :
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}
Garde des matchers précis. Un matcher vide ou .* avec un allow global approuverait tous les prompts, y compris les écritures de fichiers et les commandes shell.
05Exemple : décider en fonction de la commande
Ce script (nécessite jq) refuse tout ce qui touche à git push avec une raison que Claude peut lire, et te laisse gérer tous les autres prompts en n'affichant rien :
#!/bin/bash
# ~/.claude/hooks/permission-check.sh
input=$(cat)
cmd=$(jq -r '.tool_input.command // empty' <<<"$input")
case "$cmd" in
*"git push"*)
jq -nc '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "deny",
message: "Pushing is not allowed from this session. Commit and stop."}}}'
;;
esac
exit 0
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/permission-check.sh" }]
}
]
}
}
Construis le JSON avec jq plutôt qu'en concaténant des chaînes, pour que des guillemets dans le message ne le cassent pas. Et assure-toi que rien d'autre n'écrit sur stdout. Si ton profil shell affiche une ligne, Claude Code ne lira pas la décision.
06Exemple : autoriser et mémoriser pour la session
Pour approuver une demande et que Claude ne redemande plus jusqu'à la fin de la session, ajoute une entrée updatedPermissions avec destination: "session". Un hook peut renvoyer l'une des permission_suggestions qu'il a reçues, en changeant seulement la destination :
#!/bin/bash
input=$(cat)
jq -c '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "allow",
updatedPermissions: [(.permission_suggestions // [])[]
| select(.type == "addRules")
| .destination = "session"]}}}' <<<"$input"
Tel quel, ce hook autorise toutes les demandes qu'il reçoit, donc associe-le à un matcher précis. La destination peut aussi être localSettings, projectSettings ou userSettings, qui écrivent la règle dans un fichier de settings au lieu de la garder en mémoire.
07Souvent, une règle de permission est plus adaptée
Si la réponse ne change jamais, tu n'as pas besoin de hook. Mets la règle dans settings.json et Claude Code l'applique sans rien exécuter :
{
"permissions": {
"allow": ["Bash(npm run *)"],
"deny": ["Bash(git push *)"]
}
}
Les règles sont évaluées dans l'ordre deny, puis ask, puis allow. Un hook se justifie quand la décision dépend de quelque chose qu'une règle ne voit pas : l'heure, la branche sur laquelle tu es, ou quelqu'un qui répond depuis ailleurs. Les modes de permission expliqués présente les autres options intégrées.
08Les pièges courants
- Exécutions non interactives. Avec
claude -p, il n'y a personne à qui demander. Les hooksPermissionRequests'exécutent quand même, et si aucun ne renvoie de décision, l'appel est refusé. - Timeouts. Un hook de type command peut tourner jusqu'à 600 secondes par défaut, et Claude attend. Définis
timeout(en secondes) si ton hook attend quelque chose de lent, comme une personne. - Certains prompts ne le déclenchent pas. La requête réseau d'une commande en sandbox ne lance pas
PermissionRequest. Utilise le type de notificationpermission_promptpour ça. - Sécurité. Un hook qui renvoie
"allow"approuve des actions en ton nom, avec les permissions de ton utilisateur. Relis-le comme n'importe quel code capable d'exécuter des commandes.
09Où se situe Claude Code Notifier
La raison la plus courante d'écrire un hook PermissionRequest, c'est de répondre aux prompts quand tu n'es pas devant ton clavier. En version DIY, le hook doit envoyer la demande quelque part, attendre une réponse et la renvoyer avant le timeout. C'est faisable, et certains le font avec un chat bot.
Claude Code Notifier s'en charge pour toi. Son outil en ligne de commande gratuit, ccn, tourne sur Mac, Windows et Linux et installe ses hooks dans ~/.claude/settings.json. Une fois l'app iPhone ou Android associée, une demande de permission arrive sur ton téléphone et tu peux autoriser une fois, autoriser pour la session ou refuser ; ccn renvoie cette décision depuis le hook PermissionRequest. Si tu as tapé quelque chose dans les 30 dernières secondes, rien n'attend et le terminal demande comme d'habitude. Si tu es absent, le terminal attend le téléphone jusqu'à une limite que tu choisis (hold_ceiling_seconds, 600 par défaut), et arrête d'attendre dès que tu touches le clavier. L'outil est indépendant et n'est pas fait par Anthropic. Consulte approuver les permissions depuis ton téléphone pour le workflow et l'approbation à distance est-elle sûre ? pour l'aspect sécurité.
10FAQ
Comment approuver automatiquement des permissions avec un hook ?
Ajoute un hook PermissionRequest avec un matcher précis, fais-lui afficher une décision JSON avec "behavior": "allow" dans hookSpecificOutput, puis sortir avec 0. Si la même commande doit toujours être autorisée, une règle permissions.allow dans settings.json est plus simple et ne nécessite pas de hook.
Pourquoi le code de sortie 2 ne refuse-t-il pas la demande ?
PermissionRequest fait partie des événements où la sortie 2 n'est pas prise en compte. Claude Code l'ignore et affiche le prompt normal. Renvoie plutôt "behavior": "deny" dans l'objet de décision.
Un hook PermissionRequest peut-il passer outre mes règles deny ?
Non. Les règles deny et ask sont toujours évaluées, donc un "allow" venant d'un hook ne passe pas une règle deny correspondante. Tout updatedInput que tu renvoies est aussi vérifié avec ces règles.
Quelle est la différence entre PermissionRequest et PreToolUse ?
PreToolUse s'exécute avant chaque appel d'outil et peut autoriser, refuser, demander ou différer. PermissionRequest ne s'exécute que quand Claude Code s'apprête à afficher un prompt, et peut l'autoriser ou le refuser. Utilise PreToolUse pour les politiques, PermissionRequest pour répondre aux prompts.
Que se passe-t-il si mon hook n'affiche rien ?
Rien ne change. Claude Code affiche le prompt de permission habituel dans le terminal, comme si le hook n'existait pas.