claude-remote-peers について
claude-remote-peers は SSH 経由で Unix ソケットを転送し、別々のマシン上の Claude Code セッション間のメッセージ送受信を可能にします。レジストリ情報やキーをコピーせず、リモートで Claude プロセスも起動しません。
主な機能
- 01SSH によるセッション受信ソケットの転送
- 02表示されたアドレスでのメッセージ送信
- 03ローカルセッションへの返信プッシュ
- 04転送確認とソケットのクリーンアップ
- 05リモート PID と SSH オプションの指定
活用シーン
- 012 台の開発マシンにある Claude Code セッションの接続
- 02特定のリモートセッションへのメッセージ送信
- 03マシン間の共同作業後の転送削除
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vivswan/ skills --skill claude-remote-peersインストール後、Claude Code で/claude-remote-peersを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSE.mdREADME.md.codex-plugin/plugin.jsonagents/openai.yamlreferences/protocol.mdscripts/bridge.mts詳細
Claude リモートピア
別々のマシンで動作する 2 つの Claude Code セッションが、同じマシン上にある 2 つのセッションと同じようにメッセージをやり取りします。Unix ソケットへの書き込みで相手側が起動します。ブリッジは各セッションの受信ソケットを、同じパスのまま ssh 経由で相手のマシンに転送します。何もコピーせず、ポーリングもしません。
実例
2 台の開発用マシンに、それぞれ Claude Code セッションが 1 つずつあります。マシン A で ListAgents を実行しても、ピアは表示されませんでした。ブリッジはマシン A のセッションのシェルから起動しました。
$ bun "<skill-dir>/scripts/bridge.mts" box-b
bridge up: session-b (pid 2002 on box-b), bridge pid 4242
SendMessage to: uds:/tmp/cc-socks-1000/2002.sock
they reply to: uds:/tmp/cc-socks-1000/1001.sock
to: uds:/tmp/cc-socks-1000/2002.sock を指定した SendMessage は、マシン B に届きました。マシン B のセッションは、メッセージ内の from アドレスに返信しました。返信はマシン A のターンにそのまま届きました。
<cross-session-message from="uds:/tmp/cc-socks-1000/2002.sock" from-name="session-b" from-mode="bypass">
bridge ok
</cross-session-message>
どちらのマシンの ~/.claude にもファイルは書き込まれませんでした。
許可
別のマシン上のセッションへのメッセージ送信は、セッションごとに一度だけユーザーに確認します。このマシン上のセッションには確認は不要です。
リモートアドレスへの最初の SendMessage の前に、ホストとセッション(box-b、session-b、pid 2002)を明示してユーザーに確認します。一度の許可で、そのピアへの以降のすべてのメッセージが対象になります。同じ許可を再度求めてはいけません。
ユーザーからの指示は、それに記された範囲に限って許可として扱います。「box-b のエージェントと話して」は、box-b 上のすべてのセッションを対象とします。「box-b の session-b と話して」は、そのセッションだけを対象とし、別途確認する必要はありません。
許可された範囲外の操作には新たな確認が必要です。別のホスト、またはセッション単位で許可されたホスト上の別のセッションが該当します。
許可の記録は、このセッション自身のメモ(タスクリストまたは計画)に残し、メモリには残さないでください。新しいセッションでは、もう一度確認します。ユーザーから取り消しの申し出があったら、メモを削除します。
ワークフロー
-
ブリッジをバックグラウンドで起動します。 このセッション内のシェルから実行してください(その Bash ツールは
CLAUDE_CODE_MESSAGING_SOCKETを引き継ぎます)。<destination>にはsshが受け付ける任意の値を指定できます。nohup bun "<skill-dir>/scripts/bridge.mts" <destination> > /tmp/claude-remote-peers.log 2>&1 < /dev/null & disown
特定のリモート pid を指定する場合や、-- の後に追加の ssh オプションを指定する場合:
bun "<skill-dir>/scripts/bridge.mts" box-b 2002
bun "<skill-dir>/scripts/bridge.mts" [email protected] -- -p 2222 -i ~/.ssh/other_key
--help:
usage: bridge.mts <ssh-destination> [remote-pid] [-- <extra ssh options>]
run from a shell inside a Claude Code session (it sets CLAUDE_CODE_MESSAGING_SOCKET)
prints: SendMessage to: uds:<remote socket> once both forwards are confirmed; stays up until ssh exits or a signal
exits: 0 clean shutdown (a signal, or ssh exited 0); 1 a setup, tunnel, or cleanup failure
-
ログに
SendMessage to:の行が表示されるまで待ちます。 スクリプトが両方の転送を確認した後にのみ(リモート側の転送は ssh 経由で確認します)、この行が出力されます。それより前にソケットファイルが現れても、まだ動作するとは限りません。 -
表示されたアドレスを
toに指定して送信します。 リモート側には通常の<cross-session-message>が届きます。相手はfromアドレスに返信するため、あなた側で何かする必要はありません。 -
返信はプッシュされます。
<cross-session-message>として届き、こちらでターンが始まります。ログ、ソケット、リモートレジストリをポーリングして返信を確認してはいけません。 -
完了したら、
bridge upの行にあるbridge pidを使ってブリッジを停止します。 終了処理では、転送した 2 つのソケットを削除し、cleanup doneをログに記録します。リモート側での削除に失敗した場合は、ログに記録して終了コード 1 で終了します。kill 4242
bun を使わない場合
同じ処理をシェルで行います。リモート pid とソケットは、そのリモートのレジストリから取得します。CLAUDE_CODE_MESSAGING_SOCKET は、このセッション自身の受信ソケットです。
どちらのチェックも、転送を開始する前に関数を停止します。いずれかの側ですでに有効な受信ソケットとして使われているパスが、転送または削除されることはありません。ソケットパスに引用符が含まれる場合は、リモートシェル向けに引用符を適切に処理するスクリプトが必要です。
h() { ssh -o ClearAllForwardings=yes box-b "$@"; } # helper commands drop config forwards that would collide with the tunnel's
h 'cat ~/.claude/sessions/*.json' # pick a live one; note its messagingSocketPath
remote_sock=/tmp/cc-socks-1000/2002.sock # that messagingSocketPath, e.g. this shape (uid dir, pid file)
bridge() {
unset bridge_pid # the cleanup below acts only on a forward this call started
mkdir -p -m 700 "${remote_sock%/*}" # their socket dir, needed here when the uids differ
test ! -e "$remote_sock" || { echo "a local session owns that path; stop"; return 1; }
h "mkdir -p -m 700 '${CLAUDE_CODE_MESSAGING_SOCKET%/*}' && test -w '${CLAUDE_CODE_MESSAGING_SOCKET%/*}' && test ! -e '$CLAUDE_CODE_MESSAGING_SOCKET'" \
|| { echo "our path on box-b is taken or its directory is not writable, or ssh failed; stop"; return 1; }
ssh -o ControlPath=none -o ExitOnForwardFailure=yes -N \
-L "$remote_sock:$remote_sock" \
-R "$CLAUDE_CODE_MESSAGING_SOCKET:$CLAUDE_CODE_MESSAGING_SOCKET" box-b & # no connection sharing: the forwards must die with this pid
bridge_pid=$!
}
bridge && sleep 2 && kill -0 "$bridge_pid" && h "test -S '$CLAUDE_CODE_MESSAGING_SOCKET'" && echo "bridge up" # a refused forward makes ssh exit; replies need the -R socket on box-b
# SendMessage to = uds:<the remote_sock value>
# afterwards; bridge_pid is set only by a bridge() call that started the forward
[ -n "$bridge_pid" ] && { kill "$bridge_pid"; rm "$remote_sock"; unset bridge_pid; h "rm -f '$CLAUDE_CODE_MESSAGING_SOCKET'" || echo "remove $CLAUDE_CODE_MESSAGING_SOCKET on box-b by hand"; }
ブリッジが行わないこと
- リモートを
ListAgentsに表示しません。 一覧表示で読み込むのはローカルレジストリだけです。スクリプトが出力するuds:パスを使って、リモートにアドレス指定します。 - レジストリレコードやキーのファイルをコピーしません。 それで機能する理由と、コピーするとかえって問題になる理由については、アドレス指定と認証およびブリッジが何もコピーしない理由を参照してください。
- 転送を維持する ssh セッション以外は、リモートで何も実行したままにしません。 検索、事前チェック、クリーンアップは、終了する短い ssh コマンドです。Claude プロセスが生成、再開、フォークされることはありません。
注意点
| 表示される内容 | 意味 | 対応 |
|---|---|---|
no live Claude Code session with a messaging socket on box-b | 接続先にセッションがないか、ソケットが失われています | リモートセッションを開始または確認してから、再実行します |
could not read the session registry on box-b | ssh が失敗した、接続先に python3 がない、または ~/.claude/sessions を読み取れません | ssh box-b python3 --version を実行します。その直前の行にリモート側のエラーが表示されます |
ssh to box-b failed (exit 255) | 空きパスの確認がリモートに到達しませんでした | まず ssh を修正します:ssh -v box-b true |
<path> already exists on this machine または on <destination> | 転送元または転送先のセッションに、転送対象と同じ pid があるか、古い転送が残っています | いずれかのセッションを再起動するか、古いソケットを削除してから再実行します |
ssh exited (255) before the forwards came up | 起動中にトンネルが切断されました。ssh 自体のエラーはその直前の行に表示されます(転送の拒否、ログイン失敗など) | ssh が報告した問題を修正してから、再実行します |
forwarded socket /tmp/cc-socks-1000/2002.sock never appeared within 60 s | ssh は接続状態でしたが、ここに -L ソケットが作成されませんでした | ssh -v box-b を確認し、両側で OpenSSH 6.7+ が動作していることを確認します |
reverse forward did not bind /tmp/cc-socks-1000/1001.sock on box-b within 60 s | ssh は接続状態でした(-R が拒否されていれば接続は終了していました)が、box-b のそのパスにソケットが現れませんでした。sshd セッションから見える /tmp が異なる(PrivateTmp、コンテナなど)か、バインドがまだ保留中です | ssh box-b のシェルから ls -ld /tmp/cc-socks-<uid> を実行します。両側から見えるディレクトリを CLAUDE_CODE_TMPDIR に指定します |
could not verify the reverse forward | トンネル確立後に、プローブ自体の ssh 接続が失敗し、ブリッジが停止しました | まず ssh を修正します:ssh -v box-b true を実行してから、再実行します |
could not create a writable /tmp/cc-socks-<uid> on box-b | リモートの /tmp でディレクトリの作成が拒否されたか、別のアカウントが所有する同名のディレクトリ(root セッションの残骸)が存在します | 接続先で ls -ld /tmp/cc-socks-<uid> を確認します。所有者を変更するか、両側で別の場所を CLAUDE_CODE_TMPDIR に指定します |
Timed out sending to /tmp/cc-socks-1000/2002.sock from SendMessage | リモートセッションが終了しており、転送先に接続できません | ブリッジを停止して再実行します。最新の稼働中セッションが選択されます |
[Cross-session delivery notice] ... held | 権限モードが一致していません。リモートユーザーの承認が必要です | 待つか、crossSessionInbound を accept に設定するよう依頼します |
2 つのホストで uid が異なると、ソケットディレクトリ(/tmp/cc-socks-<uid>)も異なります。スクリプトは両側で不足しているディレクトリを作成します。Windows の受信側には認証トークンが必要なため、コピー不要という設計は適用されません。
これらすべての背景にあるプロトコルの説明と図は、references/protocol.md にあります。