Claude Media
CLAUDE_CODE_MESSAGING_SOCKETをhookやBashから使う方法

CLAUDE_CODE_MESSAGING_SOCKETをhookやBashから使う方法

Claude Codeがhookやbashコマンドに自動で渡す受信箱ソケットのパスと、own-child配信の認証手順をまとめます。

このTipsでできること

Claude Codeはセッション間メッセージングを有効にしたセッションで、受信箱ソケットのパスをCLAUDE_CODE_MESSAGING_SOCKETという環境変数にして、hookとBashコマンドへ自動で渡します。この変数を使うと、hookスクリプトや外部プロセスから「いま動いているこのセッション」の受信箱へ直接書き込めます。v2.1.224以降のClaude Codeが対象です。

セットで渡されるCLAUDE_CODE_MESSAGING_TOKEN(v2.1.228以降)は、そのソケットへ書き込むプロセスが本当にこのセッションの子プロセスであることを示す認証トークンです。この2つの変数の使いどころと、OSごとの違い、そして「トークンを送っても承認待ちが残る」条件を扱います。

実際の値を見る

v2.1.289の対話セッションでBashコマンドを実行すると、2つの変数は次のように見えます(トークンの中身は伏せています)。

echo "$CLAUDE_CODE_MESSAGING_SOCKET"
test -S "$CLAUDE_CODE_MESSAGING_SOCKET" && echo is-socket
echo "token length: ${#CLAUDE_CODE_MESSAGING_TOKEN}"
stat -f '%Sp' "$CLAUDE_CODE_MESSAGING_SOCKET"   # Linuxなら stat -c '%A'
/tmp/cc-socks/52728.sock
is-socket
token length: 32
srw-------

ソケットはファイル名の末尾が.sockで、モードがsrw-------です。所有ユーザー以外は読み書きできません。親ディレクトリのモードもdrwx------でした。トークンは32文字の文字列で、セッションごとに別の値が入ります。

同じパスは、セッション内で/statusを実行するとPeer addressの行にuds:を付けて表示されます。ソケットを用意できなかったセッションでは、この行がunavailableと理由になります。理由の全文は--debugを付けて起動したログに残ります。

ソケットの種類はOSで違います。macOSとLinux(WSL 2を含む)ではUnixドメインソケット、ネイティブWindowsでは名前付きパイプです。macOSとLinuxでは、既定のディレクトリを受け入れられないとき(他のユーザーが所有しているときなど)、/tmp/cc-socks-<uid>という自分専用のディレクトリへ切り替えます。

hookから自セッションの受信箱へ接続する

CLAUDE_CODE_MESSAGING_SOCKETとCLAUDE_CODE_MESSAGING_TOKENはどちらもClaude Codeが決める値で、settingsのenvブロックでは設定できません。メッセージングを有効にして始まったセッションでは、SessionStartを含むどのhookよりも前にエクスポートされます。

手順

hookから受信箱に書くときの流れ

  1. 1

    変数が空ならそこで終わる

    メッセージングが無効なセッションや、--bareで起動したセッションではソケットがなく、変数は空です。空のままncへ渡さないよう、最初に弾きます。

  2. 2

    遅い処理を先に済ませる

    時間のかかるコマンドの出力は、接続を開く前に変数へ取り込みます。接続してから完全な1行を送るまでに30秒かかると、Claude Codeは接続を閉じます。

  3. 3

    最初の1行に認証行を置く

    {"type":"auth","token":"<token>"}を接続の最初の1行として送ります。macOSとLinuxでは省けますが、ネイティブWindowsでは必須です。

次のスクリプトは、macOSとLinuxで認証行だけを送って接続を成立させる最小形です。

hooks/notify-self.sh
#!/bin/bash
SOCK="$CLAUDE_CODE_MESSAGING_SOCKET"
TOKEN="$CLAUDE_CODE_MESSAGING_TOKEN"
 
# メッセージングが無効、または受信箱を用意できなかったセッション
[ -z "$SOCK" ] && exit 0
 
# 遅い処理は接続の前に終わらせる(本文の送り方は文書化されていないので、ここでは認証行だけ送る)
long_running_check.sh >/dev/null
 
printf '{"type":"auth","token":"%s"}\n' "$TOKEN" | nc -U "$SOCK"

env-varsとcross-session-messagingの2ページに載っているのは、認証行の書式までです。そのあとに続けるメッセージ本文の送り方(書式)は両ページにありません。cross-session-messagingには、Claudeが送るのはプレーンテキストだけという記述があります。本文まで組み立てる前に、SendMessageツールで目的の配信ができないかを見るほうが手戻りは少なくなります。

認証行を送ると承認待ちはどうなるか

自セッションの子プロセス(hookやBashコマンド)が自分の受信箱へ書き込んだメッセージは、own-childとして扱われます。ただしown-childが効くのは、crossSessionInboundの値がどこにも適用されていないときだけです。値がholdなら保留、refuseなら破棄のままで、own-childでも変わりません。

own-childと確認できたメッセージは、設定値がなければそのまま配信されます。確認できなければ、権限の種別を主張しない通常のピアメッセージとして扱われます。受信側が権限確認を飛ばすセッション(bypassPermissionsなど)なら、これは承認待ちです。

確認の方法はOSと実行環境で変わります。

くらべる

own-childの確認方法

証跡で足りる

Linux(WSL 2含む)

子プロセスがすでに終了していても、プロセスの証跡で確認できます。トークンは省けます。

終了後はトークン

macOS・PID 1のコンテナ

macOSで確認できるのは、送信プロセスが動いている間だけです。送信後すぐ終わるスクリプトや、Claude CodeがPID 1で動くコンテナでは証跡がなく、認証行のトークンが頼りです。

トークンのみ

ネイティブWindows

証跡がなく、認証行が唯一の確認手段です。有効な認証行で始まらない接続は閉じられ、何も配信されません。

承認が必要なときの動きも、実行する場面で違います。対話端末のセッションでは、送信元と本文のプレビューを示す承認ダイアログが開きます。既定の期限は5分で、過ぎると本文は破棄されます。保留できるのは100件までで、超えると古いものから捨てられます。

claude -pで起動したワーカーにはダイアログを出す画面がありません。保留されたメッセージはdialogExpiryの期限(既定5分)まで待ち、過ぎると破棄されて、連絡できる送信元には期限切れが伝わります。無人で受けたいワーカーは、--settingsの値でcrossSessionInboundをacceptにして起動します。

-pとbareモードでの違い

claude -pのセッションは、対話セッションと同じく受信箱ソケットを用意し、変数もエクスポートします。長く動く-pワーカーへ外部から進捗の確認を送る使い方ができます。

--bareは、hook、skill、プラグイン、MCPサーバー、CLAUDE.mdなどの自動読み込みを省いて起動する最小モードです。ソケットも用意せず、セッションはListAgentsの一覧にも出ません。bareモードのスクリプトでCLAUDE_CODE_MESSAGING_SOCKETを参照すると空なので、空文字チェックを必ず入れます。

連投すると絞られる

受信側のセッションは、送信元ごとの繰り返しにレート制限をかけ、短い間に届いた同一の繰り返しは破棄します。Claudeが読める受け入れ済みメッセージは、最大50件までしかキューに積まれません。

監視スクリプトをwhileループで回して毎秒同じ内容を書くと、短い間隔の同一メッセージは破棄され、送信元ごとの頻度にも制限がかかります。2つのセッションが互いに送り合うループも、この制限で自然に止まります。

crossSessionInboundをrefuseにしても変数は残る

crossSessionInboundをrefuseにしても、受信箱ソケットの用意はやめません。CLAUDE_CODE_MESSAGING_SOCKETもいつも通りhookとBashへ渡り、変わるのは配信結果だけです。ソケットに届いたメッセージはClaudeへ渡されず、すべて破棄されます。

refuseのセッションは、自分の/statusには変化がありません。他セッションの一覧では「受信できない」と表示される場合があります。受信を止めたかどうかは変数の有無ではなく、そのセッションに適用される設定ファイルのcrossSessionInboundで確かめます。

サンドボックス内のBashから届かせる

サンドボックス化したBashコマンドがこのソケットへ書けるかどうかは、サンドボックスのUnixソケット設定で決まります。対象はsandbox.network.allowAllUnixSocketsとsandbox.network.allowUnixSocketsです。接続できないときは、この2つの設定を先に見ます。

ソケット周りの変更履歴

ソケットの挙動は、公開後も手直しが続いています。

あゆみ

v2.1.224以降の主な変更

  1. v2.1.224セッション間メッセージング(SendMessage)が登場

    セッション間のSendMessageが加わりました。macOSとLinuxが対象です。CLAUDE_CODE_MESSAGING_SOCKETの要件版は、環境変数ページでv2.1.224以降とされています。

  2. v2.1.228トークン追加と初回起動の修正

    CLAUDE_CODE_MESSAGING_TOKENが加わりました。インストールやアップグレード後の最初のセッションで、受信箱が用意されないことがある不具合も直っています。

  3. v2.1.232共有/tmpのソケットディレクトリを堅牢化

    あらかじめ仕込まれたシンボリックリンクや他ユーザーのディレクトリは、使わずに拒否されます。

  4. v2.1.239Windowsのメッセージングがchangelogに載る

    changelogでは、Windowsでのセッション間メッセージングがv2.1.239の項目に載っています。Availability節はネイティブWindowsの要件をv2.1.234以降としています。

  5. v2.1.243コンテナの不具合修正と30秒ルール

    v2.1.232の堅牢化で、user namespaceとrootlessコンテナのメッセージングが気づかないうちに止まっていた不具合が直りました。完全な1行を30秒以内に送らない接続を閉じる仕様も、この版からです。

  6. v2.1.248プロバイダーと一時ディレクトリ

    Amazon Bedrock、Google Cloud、Microsoft Foundryなどのプロバイダー、およびfeature-flag取得をオフにしたセッションでも、同じマシンの中ならメッセージングが使えます。既定のディレクトリが使えないときに/tmp配下の自分専用ディレクトリへ切り替える動きも、この版で入りました。

プロバイダー経由でClaude Codeを動かしているCIやセルフホスト環境では、claude --versionでv2.1.248以降かどうかを先に見ます。

バックグラウンドで走らせたコマンドの完了を知らせる

Bashの&で投げた長時間コマンドが終わったときに、自セッションの受信箱へ接続する使い方が考えられます。テスト実行やデプロイスクリプトのように、Claude Codeのターンと非同期に終わる処理が向く場面です。この記事で示せるのは、認証行を送って接続を成立させるところまでです。

別のセッションが次にアイドルになったとき通知を受け取るnotify_when_idleは別の仕組みです。こちらはv2.1.236以降で、同じマシン上のmacOSとLinuxのセッションが対象になります。変数を使う方法は、セッションの子プロセスならタイミングを問わず書き込める点が違います。使い分けの全体像はセッション間メッセージングの解説にあります。

よくあるつまずき

  • settingsのenvブロックで上書きしても反映されない: 2つの変数はClaude Codeが起動時に決めます。envブロックからは設定できません。
  • hookから書いたのにClaudeへ届かない: 受信側のcrossSessionInboundがholdなら保留、refuseなら破棄です。認証行を足しても結果は変わりません。
  • v2.1.232〜v2.1.242のコンテナで変数が空になる: user namespaceやrootlessコンテナで動かしていると、ソケットディレクトリの堅牢化がメッセージングを止めていました。v2.1.243以降に上げれば直ります。

まとめ

hookやBashから自セッションの受信箱に書くなら、macOSとLinuxは変数だけで始められますが、送信後すぐ終わるスクリプトやコンテナ、ネイティブWindowsでは認証行のトークンを付けます。自分の環境がどの行に当たるかは、先に/statusのPeer addressで受信箱の有無を見て、次に認証行を足すかどうかを決めます。トークンがあっても、crossSessionInboundが決めたholdやrefuseは上書きできません。

この記事を共有:XはてブLinkedIn