O hook PermissionRequest do Claude Code roda no momento em que o Claude Code vai mostrar um prompt de permissão, e pode responder esse prompt por você. Para permitir, o hook sai com 0 e imprime {"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}}; para negar, usa "behavior": "deny" com uma message para o Claude. Ele faz match pelo nome da ferramenta, não pode sobrescrever suas regras de deny, e o exit code 2 não faz nada nesse evento. Se o hook não imprimir nenhuma decisão, o prompt normal aparece.
01PermissionRequest comparado com PreToolUse e Notification
Três eventos de hook estão envolvidos com permissões, e cada um faz algo diferente:
PreToolUseAntes de toda chamada de ferramenta, precise ela de permissão ou nãoSim: allow, deny, ask ou deferPermissionRequestSó quando o Claude Code está prestes a perguntar a vocêSim: allow ou denyNotification (permission_prompt)Depois que um prompt fica esperando cerca de seis segundos enquanto você está ausenteNãoUse PermissionRequest quando o que importa são os próprios prompts: respondê-los, registrá-los ou enviá-los para algum lugar. Ele dispara na hora, sem o atraso de seis segundos. Use PreToolUse para regras que devem valer para toda chamada, como "nunca rode rm -rf", porque ele roda mesmo quando nenhum prompt apareceria. O guia do Notification hook explica o timing desse evento.
02O que o hook recebe
Além dos campos comuns, um hook PermissionRequest recebe o tool_name e o tool_input da chamada que o Claude quer fazer, e um array opcional 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 contém as mudanças de regra que o Claude Code sugeriria para esse pedido. Não é uma cópia exata das opções do diálogo, porque cada diálogo monta suas próprias opções.
03O objeto decision
Retorne um decision dentro de hookSpecificOutput:
behavior"allow" ou "deny"updatedInputSó para allow. Substitui todo o input da ferramenta, então inclua também os campos inalteradosupdatedPermissionsSó para allow. Mudanças de permissão a aplicar, como adicionar uma regra ou mudar o modomessageSó para deny. Diz ao Claude o motivointerruptSó para deny. true também para o ClaudeDuas regras para lembrar:
- As regras de deny e ask continuam valendo. Um hook que retorna
"allow"não sobrepõe uma regra de deny correspondente nas suas configurações, e o input modificado é verificado de novo contra essas regras. - O exit code
2é ignorado aqui. Na maioria dos eventos, sair com2bloqueia, mas um hookPermissionRequestque sai com2sem um objetodecisiondeixa o prompt como estava. Para negar, retorne a decisão.
04Exemplo: aprovar automaticamente um prompt
O próprio exemplo da Anthropic aprova o ExitPlanMode, o prompt que aparece quando o Claude termina um plano, para que você não seja perguntado toda vez:
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}
Mantenha os matchers restritos. Um matcher vazio ou .* com um allow geral aprovaria todos os prompts, incluindo escrita de arquivos e comandos de shell.
05Exemplo: decidir olhando o comando
Este script (precisa do jq) nega qualquer coisa que envolva git push, com um motivo que o Claude consegue ler, e deixa todos os outros prompts para você, sem imprimir nada:
#!/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" }]
}
]
}
}
Monte o JSON com jq em vez de concatenar strings, assim aspas na mensagem não quebram nada. E garanta que nada mais seja impresso no stdout. Se o seu perfil de shell der echo em alguma linha, o Claude Code não vai ler a decisão.
06Exemplo: permitir e lembrar durante a sessão
Para aprovar uma solicitação e fazer o Claude parar de perguntar até o fim da sessão, adicione uma entrada updatedPermissions com destination: "session". O hook pode devolver uma das permission_suggestions que recebeu, mudando só o destino:
#!/bin/bash
input=$(cat)
jq -c '{hookSpecificOutput: {hookEventName: "PermissionRequest",
decision: {behavior: "allow",
updatedPermissions: [(.permission_suggestions // [])[]
| select(.type == "addRules")
| .destination = "session"]}}}' <<<"$input"
Do jeito que está, ele permite toda solicitação que recebe, então use com um matcher restrito. O destino também pode ser localSettings, projectSettings ou userSettings, que gravam a regra em um arquivo de configuração em vez de mantê-la em memória.
07Muitas vezes uma regra de permissão é a melhor ferramenta
Se a resposta nunca muda, você não precisa de um hook. Coloque a regra no settings.json e o Claude Code a aplica sem executar nada:
{
"permissions": {
"allow": ["Bash(npm run *)"],
"deny": ["Bash(git push *)"]
}
}
As regras são avaliadas primeiro deny, depois ask, depois allow. Um hook vale a pena quando a decisão depende de algo que uma regra não enxerga, como o horário, a branch em que você está ou uma pessoa respondendo de outro lugar. Modos de permissão explicados cobre as outras opções nativas.
08Pegadinhas comuns
- Execuções não interativas. Com
claude -p, não há ninguém para responder. Os hooksPermissionRequestainda rodam e, se nenhum retornar uma decisão, a chamada é negada. - Timeouts. Um command hook pode rodar por até 600 segundos por padrão, e o Claude espera. Defina
timeout(em segundos) se o seu hook depende de algo lento, como uma pessoa. - Alguns prompts não o disparam. A requisição de rede de um comando em sandbox não executa o
PermissionRequest. Para isso, use o tipo de notificaçãopermission_prompt. - Segurança. Um hook que retorna
"allow"está aprovando ações em seu nome, com as permissões do seu usuário. Revise-o como qualquer outro código capaz de executar comandos.
09Onde o Claude Code Notifier se encaixa
O motivo mais comum para escrever um hook PermissionRequest é responder prompts quando você não está no teclado. Uma versão caseira precisa que o hook envie a solicitação para algum lugar, espere a resposta e a retorne antes do timeout. Dá para fazer, e tem gente que monta isso com um chat bot.
O Claude Code Notifier faz isso por você. A ferramenta de linha de comando gratuita, ccn, roda em Mac, Windows e Linux e instala seus hooks em ~/.claude/settings.json. Com o app de iPhone ou Android pareado, a solicitação de permissão chega no celular e você pode permitir uma vez, permitir na sessão ou negar; o ccn retorna essa decisão pelo hook PermissionRequest. Se você digitou nos últimos 30 segundos, nada fica esperando e o terminal pergunta normalmente. Se você está ausente, o terminal espera pelo celular até um limite que você escolhe (hold_ceiling_seconds, padrão 600) e para de esperar assim que você toca no teclado. É independente e não é feito pela Anthropic. Veja aprovar permissões pelo celular para o fluxo de uso e a aprovação remota é segura? para a parte de segurança.
10Perguntas frequentes
Como aprovar permissões automaticamente com um hook?
Adicione um hook PermissionRequest com um matcher restrito e faça-o imprimir uma decisão em JSON com "behavior": "allow" dentro de hookSpecificOutput, saindo com 0. Se o mesmo comando deve ser sempre permitido, uma regra permissions.allow no settings.json é mais simples e dispensa o hook.
Por que o exit code 2 não nega a solicitação?
O PermissionRequest é um dos eventos em que sair com 2 não é respeitado. O Claude Code ignora e mostra o prompt normal. Em vez disso, retorne "behavior": "deny" no objeto de decisão.
Um hook PermissionRequest pode sobrepor minhas regras de deny?
Não. As regras de deny e ask continuam sendo avaliadas, então um "allow" vindo de um hook não passa por uma regra de deny correspondente. Qualquer updatedInput que você retornar também é verificado contra essas regras.
Qual a diferença entre PermissionRequest e PreToolUse?
O PreToolUse roda antes de toda chamada de ferramenta e pode permitir, negar, perguntar ou adiar. O PermissionRequest roda só quando o Claude Code está prestes a mostrar um prompt, e pode permiti-lo ou negá-lo. Use PreToolUse para políticas e PermissionRequest para responder prompts.
O que acontece se meu hook não imprimir nada?
Nada muda. O Claude Code mostra o prompt de permissão de sempre no terminal, como se o hook não existisse.