CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSで通知を止める
Claude DesktopやVS Code拡張機能がcanUseTool経由でホストするセッションで、Notification hookのpermission_prompt通知だけを止める環境変数を解説します。
Claude DesktopやVS Code拡張機能でClaude Codeを動かしていると、権限確認に約6秒応答しないとNotification hookのpermission_promptが発火します。すでに自作スクリプトで外部サービスへ通知を転送している場合、ホスト側の確認ダイアログと合わせて二重に鳴ることがあります。CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSを1に設定すると、この特定のセッション形態でだけpermission_prompt通知を止められます。
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSとは
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSとは、Claude Codeが権限確認をAgent SDKのcanUseToolコールバックへ送るセッションで、Notification hookのpermission_prompt通知だけを止める環境変数です。Claude DesktopとVS Code拡張機能は、このcanUseTool経由でClaude Codeをホストしています。
1に設定すると、これらのホストが動かすセッションだけでpermission_promptが発火しなくなります。ターミナルから直接claudeを起動するセッションには影響しません。Claude Code v2.1.233以降が必要です。
permission_prompt通知はv2.1.233から動くようになった
この環境変数が意味を持つ背景には、permission_prompt自体の挙動変更があります。Claude Code v2.1.233より前は、Claude DesktopやVS Code拡張機能がホストするセッションで、Notification hookのpermission_promptがそもそも発火しませんでした。公式changelogのv2.1.233には、この不具合の修正が記載されています。
同じv2.1.233でpermission_promptは発火するようになりましたが、タイマーの動きがターミナルとは違います。
permission_promptが発火するタイミング
ターミナルセッション
プロンプトが出てから約6秒間キー入力が無いと発火します。キーを押すたびにタイマーが延びます。
canUseTool経由のセッション
権限確認が出てから一律で約6秒後に発火し、入力しても延びません。先にユーザーかPermissionRequest hookが応答すれば、発火しません。
つまりv2.1.233は、以前は鳴らなかった通知を新しく鳴るようにした変更です。CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSは、その新しい挙動を選んで元に戻すための設定です。
設定方法
シェルの環境変数として設定するか、チームで揃えたいならsettings.jsonのenvブロックに書きます。
export CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS=1
claudeプロジェクト共有の設定にする場合は、.claude/settings.jsonに次のブロックを追加します。
{
"env": {
"CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS": "1"
}
}シェルのexportとsettings.jsonのenvブロックの両方で同じ変数を設定した場合は、envブロック側の値が優先されます。複数の設定ファイルが同じ変数を設定しているときも、優先順位の高い設定ファイルの値が採用されます。
ただし、この変数が対象にするClaude Desktopでは順序が逆になる場面があります。Desktopがセッションを起動するときに組み立てる起動環境がすでに同じ変数を持っていると、設定ファイルのenv値は無視されます。無視された変数名はclaude --debugのデバッグログで確かめられます。
プロジェクト・ローカル設定のenvは、ワークスペースを信頼した後に適用されるのが基本です。モデル選択・タイムアウト・機能トグルのような安全な変数だけは、起動時から読み込まれます。この変数が安全な変数に分類されるかはenvの説明に記載がないため、チームで配るならユーザー設定かシェルのほうが取りこぼしを避けられます。
Notification hookが受け取る入力
Notificationのhookは、session_id・cwd・hook_event_nameなど全hook共通のフィールドに加えて、message(通知本文)・title(任意の見出し)・notification_type(発火した通知の種類)を受け取ります。permission_promptを止めたいときは、notification_typeがpermission_promptのケースだけをmatcherで絞り込むか、この環境変数でセッション単位に止めます。
{
"session_id": "abc123",
"cwd": "/Users/.../my-project",
"hook_event_name": "Notification",
"message": "Claude needs your permission",
"title": "Permission needed",
"notification_type": "permission_prompt"
}Notification hookは通知を止めたり内容を書き換えたりはできません。systemMessageとcontinueを返しても破棄され、terminalSequenceだけは出力されます。外部サービスへの転送やログ保存のような副作用のためのhookです。
似た設定・hookとの違い
permission_promptとその周辺には、名前や役割が紛らわしい設定がいくつかあります。
| 設定 / hook | 効果 | この環境変数との違い |
|---|---|---|
disableAllHooks | 効果すべてのhookの実行を止める | この環境変数との違いpermission_promptだけでなく全hookが止まる。個別のhookを設定に残したまま無効化する方法は無い |
PermissionRequest hook | 効果権限確認が出た瞬間に発火する専用のhook event | この環境変数との違いpermission_promptは約6秒待ってから発火する通知用。即座に判定したいならこちらを使う |
preferredNotifChannel | 効果デスクトップ通知かターミナルベルか、通知の届け方を選ぶ設定 | この環境変数との違いhookが動くかどうかには関与しない。設定を変えてもhook自体は変わらず発火する |
CLAUDE_CODE_DISABLE_NOTIFICATION_PRESENCE_CHECK | 効果PushNotificationツールの在席判定を無効化する | この環境変数との違い対象がNotification hookではなくPushNotificationツール。別の通知経路を制御する |
disableAllHooksは他のNotification hookやPreToolUse hookも道連れにします。値は設定の優先順位が適用された後のものが使われるので、プロジェクトのfalseがユーザー設定のtrueを上書きすることもあります。1回の実行だけhookを切るなら--settings '{"disableAllHooks": true}'が使え、管理者設定のhookはユーザー側の設定では止まりません。claude --helpには、設定やプラグインのhookを丸ごと飛ばす--bareもあります。
PermissionRequest hookは、通知を出すだけのNotification hookと違い、権限確認そのものに答えられます。behaviorに"allow"か"deny"を返すことで、ユーザーに代わって許可・拒否を決められ、"allow"のときはupdatedInputでツールの入力を書き換えたり、updatedPermissionsで許可ルールを追加したりもできます。"deny"のときはmessageで理由をClaudeへ伝え、interruptをtrueにすればClaudeの処理自体を止められます。ここで先に応答が返れば、permission_promptのNotification hook自体が発火しません。
ただしPermissionRequest hookが代われるのはツール使用の許可確認だけです。サンドボックス化されたコマンドがネットワークへアクセスしようとしたときの確認はPermissionRequest hookの対象外で、シグナルを取りたければpermission_promptのNotification hookを使うしかありません。ターミナルセッションでこのネットワークアクセス確認にpermission_promptが発火するにはClaude Code v2.1.246以降が必要で、ツール使用の確認自体はそれより前のバージョンから対象でした。
canUseToolセッションで通知が二重になる条件
Claude DesktopとVS Code拡張機能は、権限確認をcanUseToolコールバックで受け取り、ホスト側の画面で承認や拒否を選ばせます。そこへ、約6秒以内に応答しなければNotification hookのpermission_promptが加わるため、hookから外部サービスへ転送していると、同じ権限確認の通知が2か所に届きます。
ターミナルでは席を外したときだけ鳴る通知でした。canUseToolセッションではキー入力と無関係に鳴るので、重複が目につきます。
外部サービスへの転送自体をやめたくない場合は、Notification hookのmatcherにpermission_promptを残したままにします。ターミナルセッションでは転送を続けつつ、canUseTool経由のセッション(Claude DesktopやVS Code拡張機能)でだけ、この環境変数でpermission_promptだけを個別に止められます。
Notification hookの設定は、どこに書くかで有効範囲が変わります。~/.claude/settings.jsonに書けば自分の全プロジェクトに適用され、.claude/settings.jsonに書けばプロジェクト単位でチームに共有できます。転送先が個人のSlack宛先のようにマシン固有の値なら~/.claude/settings.jsonか.claude/settings.local.json(gitignore対象)に置き、リポジトリへコミットしないようにします。
デスクトップ通知を出すNotification hookの例
公式ドキュメントに掲載されているNotification hookの例では、messageフィールドをデスクトップ通知として表示します。
#!/bin/bash
# Notification hook: ping the desktop when Claude Code needs attention.
input=$(cat)
title="Claude Code"
body=$(jq -r '.message // "Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'このスクリプトをNotificationのmatcherにpermission_promptを指定して登録している場合、canUseToolセッションではCLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS=1を設定するだけでこの経路が止まります。hookの登録自体を書き換える必要はありません。TaskCreated/TaskCompletedフックで進捗を外部通知するように、Notification hook以外の経路で外部通知を組んでいる場合は、そちらへは影響しません。
設定したのに鳴るときの切り分け
permission_promptが止まらないとき
- 1
起動方法を確かめる
ターミナルで
claudeを直接起動しているなら、この変数は効きません。ターミナルのpermission_promptを止めるには、Notification hookの設定をpermission_promptにマッチしないよう変えるか、disableAllHooksで全hookを止めます。 - 2
バージョンを確かめる
claude --versionでv2.1.233以上か見ます。それより前はcanUseToolセッションでpermission_promptが発火しないので、変数を足しても変化は出ません。 - 3
設定の置き場所を確かめる
別の設定ファイルが同じ変数を上書きしていないか、Desktopの起動環境が同じ変数を先に持っていないかを見ます。変更後はセッションを起動し直すと確実です。
この変数が止めるのはpermission_promptだけです。matcher無しで登録したNotificationのhookでは、idle_promptやelicitation_dialogなど他の通知タイプが引き続き発火します。
claudeのhelpに出ているhook関連の口
v2.1.289以降のclaude --helpには、canUseToolの仕組みやhookの動作確認に関わる3つのオプションが載っています。
claude --version
# 2.1.289 (Claude Code)
claude --help
# --bare Minimal mode: skip hooks (those defined
# in settings and by installed plugins; ...
# --include-hook-events Include all hook lifecycle events in the
# output stream (only works with
# --output-format=stream-json)
# --permission-prompts <target>
# Who answers permission prompts with
# --print: "host" (the SDK host or
# --permission-prompt-tool) or "none" ...--permission-promptsのhostは、--printで動かすときに権限確認の応答をSDKホスト側が担う設定で、既定値です。Claude DesktopやVS Code拡張機能がClaude Codeをホストする形と同じ向きの仕組みが、コマンドラインにも出ていることになります。
--include-hook-eventsは--output-format=stream-jsonと組み合わせて、hookのライフサイクルイベントを出力に含めます。自作のNotification hookが発火したかどうかを、通知の到着ではなく出力側から見たいときの手がかりです。--bareはhookを飛ばすため、1回の実行だけhookの影響を外して比べる用途に向きます。
まとめ
ターミナルでpermission_promptの通知が多すぎるなら、この変数ではなくmatcherの設計で絞ります。Desktop・VS Code拡張機能で外部転送が二重になっているなら、CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS=1でその経路だけを切れます。他のhookは動き続けます。