

Browser extension is not connected を返し続ける。公式ドキュメントに載っている手順(Chrome 再起動 / Reconnect / 再ログイン / 拡張機能の確認)を全部やっても直らない。この記事は、そこから諦めずに lsof とネイティブホストのログと Unix ソケットを直接叩いて真因を確定させるまでの全過程です。途中で私が一度「解決不能」と誤診し、それを観測データで覆すまでの流れも含めて残します。Claude Code には Chrome 拡張と連携してブラウザを自動操作する仕組みがあります。ある日これが動かなくなりました。
Browser extension is not connected. Please ensure the Claude browser extension
is installed and running, and that you are logged into claude.ai with the same
account as Claude Code. If this is your first time connecting to Chrome, you may
need to restart Chrome for the installation to take effect.
このエラーは公式ドキュメントの「Common error messages」に載っている最頻出エラーです。そして案内されている対処は概ね次の 5 つです。
Chrome と Claude Code を再起動する
/chrome コマンドから Reconnect する
chrome://extensions で拡張機能が有効か確認する
/login でアカウント認証する
ネイティブメッセージングホストの設定ファイルの存在を確認する
私はこれを全部やりました。全部効きませんでした。ここから先が本記事の中身です。結論だけ先に言うと、真因は「Claude Code はセッション起動時にしかブラウザ連携を確立しない」という挙動でした。この結論に至るまでに一度誤診しているので、その誤りも含めて書きます。
Claude Code ↔ Chrome 拡張の実際の通信アーキテクチャ(拡張 → ネイティブホスト → Unix ソケット → Claude Code)
Browser extension is not connected の切り分けを 2 コマンドで終わらせる方法
ネイティブホストのログの場所と、そこで見るべき 1 行
Unix ソケットに自分で接続して「サーバ側は正常」を確定させるテクニック
「設定は全部正常なのに動かない」ときに、推測で打ち切らず真因に到達するデバッグの型
Claude Code のブラウザ自動操作を使っていて、繋がらなくなった人
ネイティブメッセージング / MCP まわりをデバッグする人
「公式FAQの手順を全部試したが直らない」状態から先に進みたい人
macOS(パスは macOS 前提。Linux / Windows では読み替えが必要)
Claude Code CLI 2.1.x 系、Chrome 拡張「Claude in Chrome」1.0.8x 系
lsof / pgrep / python3 が使える環境
最初にやったのは、公式が案内している「設定ファイルは存在するか」の確認です。macOS ではネイティブメッセージングホストの定義がここに置かれます。
ls -la ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/
出てきたのは 2 つの Anthropic 系ファイルでした。
| ファイル | path が指す先 | 誰のホストか |
|---|---|---|
| com.anthropic.claude_code_browser_extension.json | ~/.claude/chrome/chrome-native-host | Claude Code |
| com.anthropic.claude_browser_extension.json | /Applications/Claude.app/Contents/Helpers/chrome-native-host | Claude デスクトップアプリ |
それぞれの中身も確認します。Claude Code 側はこうなっていました。
{
"name": "com.anthropic.claude_code_browser_extension",
"description": "Claude Code Browser Extension Native Host",
"path": "/Users/[user]/.claude/chrome/chrome-native-host",
"type": "stdio",
"allowed_origins": [
"chrome-extension://fcoeoabgfenejglbffodgkkbkcdhcgfn/"
]
}
allowed_origins の拡張機能 ID が、実際にインストールされている「Claude in Chrome」の ID と一致しているかを見ます。ついでに実体が実行可能かも確認します。
# ホストの実体が存在して実行可能か
test -x ~/.claude/chrome/chrome-native-host && echo "OK"
# 拡張機能がどのプロファイルに入っているか
ls -d ~/Library/Application\ Support/Google/Chrome/*/Extensions/fcoeoabgfenejglbffodgkkbkcdhcgfn
さらに、ホストの実体はラッパースクリプトで、中で CLI のバージョン付きバイナリを叩いていました。ここが古いバージョンを指していると「ホストが起動できない」典型的な故障になるので、実際のバージョンと突き合わせます。
cat ~/.claude/chrome/chrome-native-host
# → exec "~/.local/share/claude/versions/2.1.220" --chrome-native-host
ls ~/.local/share/claude/versions/ # 2.1.220 が実在するか
claude --version # 2.1.220 と一致するか
結果はすべて正常でした。拡張機能は 1 つだけで有効、設定ファイルの origin も一致、ホストの実体も実行可能でバージョンも一致。ここで手が止まります。
次に、実際に動いているプロセスを見ました。
pgrep -l -f "chrome-native-host"
# → 61450 /Applications/Claude.app/Contents/Helpers/chrome-native-host \
# chrome-extension://fcoeoabgfenejglbffodgkkbkcdhcgfn/
動いていたのはデスクトップアプリ側のホストだけで、Claude Code 側のホスト(~/.claude/chrome/chrome-native-host)は一度も起動していませんでした。しかもデスクトップアプリ本体は起動していないのに、そのヘルパープロセスだけが生きている状態です。
ここで私はこう結論しました。
この結論は誤りでした。 孤立ヘルパーを kill しても、Chrome を完全再起動しても状況は変わらず、それが「解決不能」の裏付けのように見えてしまったのです。実際には、後述するとおりデスクトップアプリ側のホストが動いているのは正常な状態でした。
観測できているのは「Claude Code 側のホストが起動していない」という事実だけで、「だから繋がらない」は推測にすぎません。プロセス一覧だけを根拠に「あるべきものが無い」と判断したのが誤りの原因です。
打ち切りをやめて、動いているホストプロセスが何と繋がっているのかを見ることにしました。lsof でプロセスが開いているファイルとソケットを列挙します。
PID=$(pgrep -f "chrome-native-host" | head -1)
lsof -p "$PID" | grep -Ei "sock|unix|TCP|claude"
これが転換点でした。
chrome-na 61450 [user] 9w REG ~/Library/Logs/Claude/chrome-native-host.log
chrome-na 61450 [user] 10u unix /tmp/claude-mcp-browser-bridge-[user]/61450.sock
2 つの重要な情報が出ました。
ログファイルが存在する: ~/Library/Logs/Claude/chrome-native-host.log
Unix ソケットを作っている: /tmp/claude-mcp-browser-bridge-$USER/<PID>.sock
ソケットの名前に mcp-browser-bridge と入っています。つまりこのホストは、Chrome と喋るだけでなく MCP クライアント(= Claude Code)を待ち受けるサーバでもあるということです。
ログとソケットから逆算すると、経路はこうなっていました。
+-------------------+
| Chrome 拡張 |
| (Claude in Chrome)|
+---------+---------+
| ネイティブメッセージング (stdio)
v
+--------------------------------------------------+
| /Applications/Claude.app/.../chrome-native-host |
| - Chrome とは stdio で会話 |
| - Unix ソケットを作って MCP クライアントを待つ |
+---------+----------------------------------------+
| /tmp/claude-mcp-browser-bridge-$USER/<PID>.sock
v
+-------------------+
| Claude Code (MCP) |
+-------------------+
つまり デスクトップアプリ側のホストが動いているのは正常で、Claude Code はそのホストが作ったソケットに接続する側です。「Claude Code 用のホストが起動していない」ことは異常ではありませんでした。第 2 節の誤診はここを取り違えたものです。
ログの中身も、この理解を裏付けます。
[INFO chrome-native-host] Chrome native host starting (version 0.1.0)
[INFO chrome-native-host] Creating socket listener: /tmp/claude-mcp-browser-bridge-[user]/61450.sock
[INFO chrome-native-host] Socket created successfully at: ...
[INFO chrome-native-host] Entering main message loop
[INFO chrome-native-host] Socket server listening for connections
ホストは正常に起動し、ソケットを作って待ち受けに入っています。
ここで見るべきは、ログに何が書かれているかではなく何が書かれていないかです。Chrome を何度も再起動しているのでログには何度も起動・終了が記録されていますが、その全期間を通して次の行が一度も出ていませんでした。
[INFO chrome-native-host] Accepted new MCP connection (client 1). Total clients: 1
これが意味するのは 1 つだけです。Claude Code は接続を試みてすらいない。接続して失敗しているのではなく、そもそもソケットを叩いていないのです。
「接続に失敗している」と「接続を試みていない」は原因がまったく別物です。前者ならソケットの権限・パス・プロトコルを疑いますが、後者ならクライアント側の状態を疑うべきです。この 1 行の有無で調査対象が半分に絞れます。
とはいえ「ソケット側が壊れていて、Claude Code が事前チェックで諦めている」可能性は残ります。これを潰すために、自分でソケットに接続してみます。
import socket
path = '/tmp/claude-mcp-browser-bridge-[user]/61450.sock'
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.settimeout(3)
try:
s.connect(path)
print('接続成功: ソケットは生きている')
s.close()
except Exception as e:
print('接続失敗:', type(e).__name__, e)
結果は接続成功。しかも、この接続がログにきちんと記録されました。
[INFO chrome-native-host] Accepted new MCP connection (client 1). Total clients: 1
[INFO chrome-native-host] MCP client 1 connected
[INFO chrome-native-host] MCP client 1 disconnected
これで確定します。ソケットは生きていて、接続を受け付け、ログに記録する。サーバ側は完全に正常。したがって問題は Claude Code 側にしかありません。ここまで来ると、もう推測の余地がほとんど残りません。
最後に、Claude Code 側が「なぜ試みないのか」を考えます。手がかりは時刻です。セッションの開始時刻、ソケットの生成時刻、認証した時刻を並べます。
# セッション用ディレクトリの作成時刻(= セッション開始時刻の目安)
stat -f "%SB %N" -t "%Y-%m-%d %H:%M:%S" <session_dir>
# 現在のソケットの生成時刻
stat -f "%SB %N" -t "%Y-%m-%d %H:%M:%S" /tmp/claude-mcp-browser-bridge-$USER/*.sock
並べた結果がこれです。
| 時刻 | 出来事 |
|---|---|
| 02:02:42 | ホストがソケットを作成(当時の PID のもの) |
| 02:03:15 | Claude Code のセッションが起動(33 秒後) |
| 02:03 台 | ログに Accepted new MCP connection が無い ← セッション起動時に接続していない |
| 09:55:10 | Chrome 終了 → ソケット削除 |
| 09:55:11 / 10:01:02 | Chrome 再起動 → 新しいソケットが 2 度作られる |
| 10 時台 | ここで初めて /login を実行(セッション起動の 8 時間後) |
| 10:19:32 | 手動テスト接続だけがログに記録される |
読み取れることは 2 つあります。
1 つ目。ソケットが存在していた時刻にセッションが起動しているのに、接続していない。 つまり単なる「ソケットが無かった」タイミング問題ではありません。
2 つ目。/login はセッション起動の 8 時間後だった。 Chrome 連携は claude.ai アカウント認証が前提です。セッション起動時点でその条件を満たしていなければ、連携は無効と判断されます。
ここから真因が言えます。Claude Code はセッション起動時にしかブラウザ連携を確立しない。起動時に認証条件を満たしていないと連携が無効のまま固定され、以降 Chrome を再起動しても /chrome から Reconnect しても復活しません。だから 8 時間ずっと繋がらなかったのです。
対処は 1 つだけです。
# /login 済みを確認したうえで、Claude Code 自体を再起動する
# 会話を継続したい場合は --continue
claude --continue
これで即座に繋がりました。逆に、次の操作はすべて無意味です。理由も添えておきます。
| やりがちな操作 | なぜ効かないか |
|---|---|
| Chrome を完全再起動 | ソケットが作り直されるだけ。接続する側が試みないので状況は変わらない |
| 孤立ヘルパープロセスを kill | ホスト側は元から正常。殺す必要がない |
| /chrome から Reconnect | セッション内の連携状態が無効固定されているため復活しない |
| 拡張機能の再有効化 | 拡張機能側は元から正常に動いている |
| セッション中に /login | 認証は通るが、そのセッションの連携判定はやり直されない |
次回同じ症状に遭ったら、以下の 2 コマンドだけで切り分けが終わります。
# 1. Claude Code は接続を試みているか?(Accepted new MCP connection があるか)
tail -20 ~/Library/Logs/Claude/chrome-native-host.log
# 2. ソケットは存在するか?
ls -la /tmp/claude-mcp-browser-bridge-$USER/
1 で Accepted が無く、2 でソケットが存在するなら、Claude Code を再起動すれば直る。長い調査は不要です。
この調査は一度誤診しています。そこから何を一般化できるかを整理します。
「あるべきものが無い」を原因と決めつけない。 第 2 節で私は「Claude Code 用のホストが起動していない」を根拠に解決不能と結論しました。しかし実際にはそれが正常な状態でした。自分が想定したアーキテクチャに欠けがあるとき、欠けているのは現実の側ではなく自分の理解の側かもしれません。
プロセス一覧の次は、必ず「何と繋がっているか」を見る。 pgrep で分かるのは「動いているか」だけです。lsof に進むと、ログの場所・ソケットの場所・通信相手が一気に判明します。この調査では lsof の 1 回が転換点でした。
ログは「書かれていること」より「書かれていないこと」を読む。 Accepted new MCP connection が無いという事実が、「接続失敗」と「接続を試みていない」を切り分けました。これは推測ではなく観測です。
サーバ側の正常性は、自分で叩いて確定させる。 ソケットに socket.connect() して成功しログに残ることを見れば、「サーバ側は正常」が推測から事実になります。切り分けの片側を潰すのに数行のスクリプトで足ります。
動かない事象は、時刻を並べると原因が見える。 「起動時刻」「リソース生成時刻」「設定変更時刻」を時系列表にすると、状態が固定されるタイプのバグは一目で分かります。今回は「認証したのがセッション起動の 8 時間後」が決定打でした。
「解決不能」は観測で裏付けてから言う。 対処が効かないことは「解決不能」の証拠にはなりません。効かない対処が全部見当違いだった、という可能性が常に残ります。
切り分けは pgrep → lsof → ログ → 実接続テスト → 時系列照合の順に進める。前の段が終わる前に次の推測をしない
lsof -p <PID> は「そのプロセスの世界地図」。ログの場所も通信相手もここで一度に分かるので、公式ドキュメントに書かれていないアーキテクチャを自力で復元できる
ログの grep は「出ているはずの行」を探す。Accepted / connected / listening のような接続ライフサイクルの語を狙うと、どの段で止まっているかが特定できる
Unix ソケットは python3 数行で叩ける。サーバ側の正常性確認に専用ツールは要らない
セッション起動時に状態が固定される系のバグは、時系列表で必ず露出する。「あとから設定を直したのに直らない」はこのパターンを疑う
AI エージェントに調査させるときも同じ。「解決不能」と報告してきたら、その根拠が観測なのか推測なのかを問い直すと先に進むことがある
Claude Code ↔ Chrome の経路は「拡張 → デスクトップアプリ側のネイティブホスト → Unix ソケット → Claude Code」。Claude Code 用のホストが起動していないのは異常ではない
ソケットは /tmp/claude-mcp-browser-bridge-$USER/<PID>.sock、ログは ~/Library/Logs/Claude/chrome-native-host.log
ログに Accepted new MCP connection が無ければ、Claude Code は接続を試みてすらいない。「接続失敗」と切り分けられる
ソケットに自分で socket.connect() して成功すれば、サーバ側の正常性が確定する
真因は「Claude Code はセッション起動時にしかブラウザ連携を確立しない」こと。起動時に claude.ai 認証が済んでいないと無効固定される
対処は /login 済みを確認して Claude Code を再起動(claude --continue で会話継続)。Chrome 再起動・プロセス kill・Reconnect はいずれも効かない
「あるべきものが無い」は原因ではなく、自分のアーキテクチャ理解の欠けかもしれない。「解決不能」は観測で裏付けてから言う