Claude CodeのNotification hookを使って承認に通知音を鳴らす
Claude CodeのNotification hookが動く仕組みを理解する
はじめに
Claude Codeには、特定のイベントが発生したタイミングで任意のコマンドを実行できる hook機能 が公式に用意されている。その中でも Notification hookは、Claude Codeが承認待ち・入力待ち・処理完了になったタイミングで外部コマンドを呼び出せる仕組みだ。
この記事では、Notification hookがどのように動作するのか、その構造と原理を整理した上で、実際の設定方法とハマりポイントを解説する。
前提知識・用語整理
hookとは
Claude Codeにおけるhookとは、特定のライフサイクルイベントに対してシェルコマンドを紐づける仕組みだ。設定は ~/.claude/settings.json(グローバル)または .claude/settings.json(プロジェクトローカル)に記述する。
hookの種類には以下がある:
-
PreToolUse: ツール実行前 -
PostToolUse: ツール実行後 -
Notification: 通知イベント -
Stop: Claude Codeの停止時
Notification hookのmatcher
Notification hookでは matcher フィールドで発火タイミングを指定できる。
| matcher | 発火タイミング |
|---|---|
permission_prompt |
操作の承認を求めるとき |
idle_prompt |
入力待ち状態になったとき |
completed |
処理が完了したとき |
仕組みの解説
なぜ osascript で macOS通知が出せるのか
osascript はmacOSに標準搭載されているAppleScriptの実行コマンドだ。display notification は AppleScript の組み込み命令で、macOSの通知センターにネイティブ通知を送信する。
osascript -e 'display notification "メッセージ" with title "タイトル" sound name "default"'
sound name には macOS のシステムサウンド名を指定できる("default", "Glass", "Ping", "Pop", "Basso" など)。
なぜ afplay を追加するのか
osascript の sound name だけでも音は鳴るが、通知センターの設定によっては音が出ない場合がある。afplay は macOS のコマンドラインオーディオプレイヤーで、サウンドファイルを直接再生するため、通知の設定に依存せず確実に音を出せる。
afplay /System/Library/Sounds/Glass.aiff
&& で繋ぐことで、osascript が成功した場合のみ afplay を実行する。
hookコマンドがどう実行されるか
Claude Codeは settings.json の hooks セクションを読み込み、対応するイベントが発生すると、指定されたコマンドをシェルで実行する。コマンドはClaude Codeのプロセスの子プロセスとして起動されるため、Claude Code本体の処理はブロックされない。
手順
1. settings.json の作成・編集
# グローバル設定ファイルを開く
nano ~/.claude/settings.json
以下の内容を記述する(既存の設定がある場合はマージする):
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"承認が必要です\" with title \"Claude Code\" sound name \"default\"' && afplay /System/Library/Sounds/Glass.aiff"
}
]
}
]
}
}
2. 通知権限の付与
macOS はアプリケーションが通知を送信する際に権限を要求する。osascript 経由の通知は「Script Editor」のプロセスとして扱われるため、Script Editor の通知を許可する必要がある。
# 一度手動で実行して権限ダイアログを出す
osascript -e 'display notification "test"'
実行後、システム設定 > 通知 > Script Editor で「通知を許可」になっているか確認する。
3. Claude Code の再起動
# 起動中の場合は一度終了してから再起動
claude
4. 動作確認
Claude Codeで承認が必要な操作(ファイル書き込みなど)を実行して、通知バナーと通知音が出ることを確認する。
ハマりポイントとその技術的な理由
通知が出ない(権限未設定)
症状: 設定を書いたが通知バナーが出ない。afplay の音だけ鳴る場合もある。
原因: macOS の通知権限システムは、アプリごとに通知の許可/拒否を管理している。Script Editor が一度も通知を送ったことがない環境では、権限が未設定のまま静かに失敗する。
対処: osascript -e 'display notification "test"' を手動実行して権限ダイアログを出し、許可する。
VSCode / Cursor拡張では動かない
症状: ターミナルでは通知が来るが、VSCodeのClaude Code拡張では来ない。
原因: VSCode/Cursor の拡張機能として動作するClaude Codeは、ターミナルから起動するClaude Codeとは異なるプロセス環境で動作する。拡張機能のサンドボックス環境では、hookコマンドの実行が制限される場合がある。この問題はGitHub issueでも報告されており、"closed as not planned" となっている。
対処: Notification hookを確実に使いたい場合は、ターミナルで claude コマンドを直接実行する運用を採用する。
Ghostty などの対応ターミナルでの挙動
ターミナルエミュレータの中には、Claude CodeのNotificationイベントをOSC通知(Operating System Command)としてネイティブ通知に変換してくれるものがある。Ghosttyはその一例で、hookを書かずともターミナル側で通知を出してくれる。ただし全ての端末で同様に動くわけではないため、hookで自前通知を追加するのが最も汎用的な方法だ。
まとめ
Claude CodeのNotification hookは、Claudeのライフサイクルイベントとシェルコマンドを接続する公式の仕組みだ。承認待ち(permission_prompt)のmatcherと、osascript + afplay の組み合わせにより、macOSのネイティブ通知バナーと通知音を確実に鳴らすことができる。
VSCode/Cursor拡張では動作しない制限があるため、ターミナルでの運用が前提になるが、それさえ理解した上で使えば、Claude Codeの放置運用を大幅に改善できる。
Discussion