Claude Code の通知を hooks でスマホに届ける

Claude Code が許可待ちやタスク完了になったらスマホに通知。Notification と Stop の hook と Webhook を使った、コピペで動く設定です。

更新日

目次

Claude Code が止まる場面で重要なのは 2 つあります。あなたの入力待ちでブロックされているとき(許可の確認や質問)と、作業が終わったときです。ターミナルを見ていなければ、どちらも同じに見えます。何も起こらず、あなたが戻るまでセッションはずっと待機したままです。解決策は hook です。settings.json に数行書くだけで、どちらかが起きた瞬間にスマホへ Webhook を送れます。

このガイドでは、コピーして使える設定を示し、状況ごとにどの hook イベントを使うかを説明します。さらに、確実に動かすための細部、つまり hook の入力の読み方、シェルのクォート、通知に埋もれない工夫も取り上げます。

どの状況でどの hook を使うか

Claude Code はライフサイクルイベントで hook を実行します。この用途には次の 2 つが当てはまります(hooks リファレンス)。

知りたいことHook イベントMatcher / フィルタ
Claude が許可の判断を待っているNotificationpermission_prompt
Claude がアイドル状態であなたの入力を待っているNotificationidle_prompt
Claude が応答を終えたStop不要

Notification hook の matcher は通知の種類で絞り込むので、どの通知をスマホに届けるかを正確に選べます。入力は stdin に JSON で渡され、notification_type、message、cwd、session_id が含まれます。Notification hook は何かをブロックしたり変更したりすることはできません。観測するだけで、今回の目的にはそれで十分です。

Stop の入力には、Claude が最後に出力したテキストである last_assistant_message が含まれます。通知の要約行としてそのまま使えます。

必要なもの

  • スマホに Echobell(App Store / Google Play)
  • Claude Code を実行するマシンの curl と jq
  • 2 分

ステップ 1 — チャンネルを 2 つ作る

すべてを 1 つのチャンネルに送るのは避けましょう。Echobell で次の 2 つを作ります。

  • Claude Blocked:タイプは 時間依存(Time Sensitive) または 着信(Calling)。許可待ちの用。エージェントが止まっており、1 分ごとに計算資源と時間が無駄になっています
  • Claude Done:タイプは 通常(Normal)。いつ見てもよい普通のプッシュ通知です

各チャンネルの Webhook URL をコピーします。https://hook.echobell.one/t/<channel-token> のような形です。パスワードと同じように扱ってください。これを知っている人は誰でもあなたのスマホを鳴らせます。

コミットされる設定ファイルに残らないよう、両方を環境変数に保存します。

export ECHOBELL_CLAUDE_BLOCKED_URL="https://hook.echobell.one/t/<blocked-token>"
export ECHOBELL_CLAUDE_DONE_URL="https://hook.echobell.one/t/<done-token>"

Claude Code が引き継げるよう、これらはシェルのプロファイルに書いておきます。

ステップ 2 — hooks を追加する

すべてのプロジェクトに適用するなら ~/.claude/settings.json、1 つのリポジトリだけなら .claude/settings.local.json に追加します。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{title: \"Claude が許可を求めています\", body: .message, project: (.cwd | split(\"/\") | last)}' | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- \"$ECHOBELL_CLAUDE_BLOCKED_URL\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{title: \"Claude が完了しました\", body: (.last_assistant_message // \"完了\" | .[0:200]), project: (.cwd | split(\"/\") | last)}' | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- \"$ECHOBELL_CLAUDE_DONE_URL\""
          }
        ]
      }
    ]
  }
}

各部分の役割は次のとおりです。

  • jq -c '{...}' は stdin から hook の JSON を読み、小さなペイロードを組み立てます。これにより Echobell には hook のドキュメント全体ではなく、必要なフィールドだけが届きます。
  • .cwd | split("/") | last は /home/me/code/api を api に変換し、どのプロジェクトからの通知かが分かるようにします。
  • .[0:200] は最後のメッセージを切り詰めます。Echobell の worker は通知本文を 1500 バイトまでに制限していますし、ロック画面に表示できる量はそれよりずっと少ないからです。
  • -m 10 は curl に 10 秒のタイムアウトを設定します。ネットワークに問題があっても、Claude Code が hook を待ち続けることはありません。

Echobell は送信したキーをチャンネルのテンプレートで描画しますが、新しいチャンネルにはそれらを読むテンプレートがありません。チャンネル設定でタイトルテンプレートを {{title}}、本文テンプレートを {{body}} にしてください。プロジェクト名も表示したい場合は、{{title}} ({{project}}) のようにどちらかに {{project}} を加えます。

ステップ 3 — 本物のプロンプトを待たずにテストする

偽の hook ペイロードを使って、同じパイプラインを手で実行します。

echo '{"message":"Claude needs your permission to use Bash","cwd":"/home/me/code/api","notification_type":"permission_prompt"}' \
  | jq -c '{title: "Claude が許可を求めています", body: .message, project: (.cwd | split("/") | last)}' \
  | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- "$ECHOBELL_CLAUDE_BLOCKED_URL"

スマホが光れば Echobell 側は問題なく、残る問題は Claude Code が設定を読み込む部分にあります。Claude Code で /hooks を実行して hook が読み込まれたことを確認してください。セッション実行中に変更した設定は、hooks メニューを開くか再起動しないと反映されない場合があります。

ステップ 4 — ブロック用チャンネルを確実に鳴らす

プッシュ通知はおやすみモードには勝てません。着信(Calling) チャンネルは本物の着信のように動作し、iOS の集中モードを突破します。長いタスクを始めて夜に席を外すときに、これがいちばん効いてきます。一部のプロンプトでだけ中断されたい場合は、hook を編集するのではなく、送信する変数に対して Echobell のチャンネル条件(たとえば project == "api")で絞り込みます。変数の一覧は条件リファレンスを参照してください。

通知の洪水を避ける

忙しいセッションでは Stop が大量に発火することがあります。応答が終わるたびに 1 回発火するからです。必要な通知だけを残す方法は 3 つあります。

  • Stop を鳴るチャンネルに送らない。 完了は緊急ではないので、通常のままにします。
  • Stop の代わりに idle_prompt を使う。 応答のたびではなく、Claude が止まってあなたを待っているときだけ知りたい場合に有効です。
  • プロジェクトで絞り込む。 条件を使えば、試し書き用のリポジトリからは一切通知が来なくなります。

リモート環境や CI で Claude Code を動かす

クラウドやリモート環境でも同じ方法が使えます。hook は外向きの HTTPS リクエストにすぎず、インターネットから到達できるものは何も要らないからです。2 つの環境変数をシェルのプロファイルではなく環境のシークレットに設定すれば、セッションがどこで動いていても通知はスマホに届きます。

トラブルシューティング

何も届かず、手動テストも失敗する。 Claude Code を起動するシェルで URL 変数が設定されているか確認してください:echo "$ECHOBELL_CLAUDE_BLOCKED_URL"。変数が空だと curl は黙って失敗します。

手動テストは通るが hook が発火しない。 matcher を確認してください。permission_prompt と idle_prompt は別物で、一致しない matcher は単に実行されません。/hooks を実行し、JSON が正しいか確認します。末尾のカンマが 1 つあるだけで、その settings ファイル全体の hook が無効になります。

通知の本文が空白になる。 jq フィルタが、そのイベントに存在しないフィールドを参照しています。message は Notification の入力に、last_assistant_message は Stop の入力にあります。上の設定のように // でデフォルト値を指定してください。

発火が多すぎる。 前のセクションを参照してください。Stop から idle_prompt に切り替えるか、条件を追加します。

よくある質問

Claude Code が許可を求めたとき、どうすれば通知を受け取れますか?

matcher を permission_prompt にした Notification hook を追加し、チャンネルに Webhook を送ります。Echobell がそのリクエストを、スマホへのプッシュ通知または着信型アラートに変えます。

Claude Code がタスクを終えたことを知るには?

Stop hook を追加します。Claude が応答を終えたときに実行され、入力に含まれる last_assistant_message を通知本文に使えます。

通知はおやすみモードを突破できますか?

はい、受信するチャンネルが着信(Calling)チャンネルであれば可能です。本物の着信のように動作するため、iOS の集中モードも通します。どのアプリの通常のプッシュ通知もそうはいきません。

Web 版の Claude Code やリモートセッションでも動きますか?

はい。hook は外向きの HTTPS リクエストを送るだけなので、セッションがインターネットに接続できる場所ならどこでも動きます。チャンネルの URL は環境のシークレットから渡してください。

Webhook URL を settings.json に書いても安全ですか?

上で示したように環境変数を使い、トークンがリポジトリにコミットされないようにするのがおすすめです。漏れてしまった場合は、新しいチャンネルを作って古いものを廃止してください。

まとめ

チャンネル 2 つ、hook 2 つ、そして jq | curl のパイプライン。permission_prompt には 着信(Calling) または時間依存のチャンネル、Stop には通常のチャンネルを使います。これで長いエージェントタスクを走らせて席を離れても、エージェントが本当にあなたを必要としたときだけ呼び戻されます。

iPhone 版 Echobell をダウンロードするか、Google Play で入手して、長いタスクを任せる前に上の手動テストを実行しておきましょう。

関連記事