p4ni.

比較

Chrome DevTools MCP は Google に弾かれ、Claude in Chrome は通る

· 読了まで約10分

目次

記事のネタは、狙うクエリの検索結果を見てからでないと書かないことにしている。ブラウザで Google を開き、1 ページ目を読み、その席がもう埋まっているかを判断する。手持ちの自動化のなかで一番地味な部類で、これまで一度も失敗したことがなかった。

今日それが失敗した。Claude in Chrome の拡張が接続されていなかったので、エージェントはもう一方の経路である Chrome DevTools MCP にフォールバックした。DevTools Protocol 越しに Chrome を操るほうだ。最初の検索で /sorry/index に飛ばされた。Google の CAPTCHA である。拡張を繋ぎ直して同じクエリを投げたら、何事もなく結果ページが返ってきた。

1 台の Mac に 2 つの経路があり、片方だけが弾かれた。この現象で検索上位に来る記事はどれも同じ説明をする。サイトが CDP 接続を検知しているのだから、ステルス用のプラグインを入れろ、と。説明を信じる代わりに両方を測ってみたところ、CDP の話は成り立たなかった。

3 つの経路は、どのブラウザを使うかが違う

Claude in Chrome はブラウザ拡張である。別のブラウザは立ち上がらない。すでに開いている自分の Chrome の中で動くので、プロファイルも Cookie もログイン済みのセッションもそのまま使う。

Chrome DevTools MCP は MCP サーバーで、自分が管理する Chrome に DevTools Protocol で話しかける。ブラウザは専用のプロファイルで新しく起動される。

Playwright MCP はこの 2 つとよく比較される 3 つ目の選択肢で、実行のたびに新しいブラウザコンテキストを作る。それが売りの道具だ。今回は入れていなかったし、測るためだけに入れるのも違うので、後述の数値には出てこない。

前の 2 つは同じ瞬間にこのマシンで生きていた。比較に意味があるのはそこで、ハードウェアも Chrome のビルドもネットワークも同じ、時刻すら同じ分である。

同じ URL を開いて両方の指紋を測る

両方の経路で https://www.google.com/ を開き、それぞれのスクリプト実行ツールから同じ関数を流した。

() => {
  const c = document.createElement('canvas');
  const gl = c.getContext('webgl');
  const dbg = gl && gl.getExtension('WEBGL_debug_renderer_info');
  return {
    ua: navigator.userAgent,
    webdriver: navigator.webdriver,
    cookieLen: document.cookie.length,
    hasSID: /(^|; )SID=/.test(document.cookie),
    screen: screen.width + 'x' + screen.height,
    webglRenderer: dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : null,
    plugins: navigator.plugins.length,
    deviceMemory: navigator.deviceMemory,
    hwConcurrency: navigator.hardwareConcurrency,
    pdfViewer: navigator.pdfViewerEnabled,
    chromeKeys: window.chrome ? Object.keys(window.chrome).join(',') : null,
  };
}

2026-08-19 の結果である。

項目Claude in ChromeChrome DevTools MCP
navigator.userAgentChrome/151.0.0.0HeadlessChrome/151.0.0.0
userAgentData.brandsGoogle Chrome 151, Chromium 151Google Chrome 151, Chromium 151
navigator.webdriverfalsefalse
document.cookie の長さ682112
Google の SID Cookieありなし
screen1920x1080800x600
WebGL レンダラーANGLE Metal, Apple M4ANGLE Metal, Apple M4
navigator.plugins.length55
deviceMemory / hardwareConcurrency16 / 1016 / 10
pdfViewerEnabledtruetrue
window.chrome のキーloadTimes,csi,apploadTimes,csi,app
Google 検索結果ページ/sorry/index

最後の行は結果なので、指紋にあたるのはその上の 11 項目である。うち 7 項目が完全に一致している。この記事の中身はこの表である。

webdriver フラグも SwiftShader も、両方で同じ値だった

弾かれたほうの navigator.webdriverfalse だった。この手のガイドが最初に名前を出すフラグで、puppeteer-extra-plugin-stealth がそもそも存在する理由でもある。Chrome DevTools MCP は最初からこれを立てない設定で動いていた。何かが私を弾いたにせよ、このプロパティを読んではいない。

GPU も本物だった。もう一つの定番は、ヘッドレスの Chrome は SwiftShader に落ちるので UNMASKED_RENDERER_WEBGL を見ればソフトウェアレンダラーが返る、という見立てである。実際には両方とも ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version) を返した。このラップトップに載っている GPU そのものだ。いまのヘッドレス Chrome はハードウェアを使う。

window.chromenavigator.pluginsdeviceMemoryhardwareConcurrencypdfViewerEnabled も一致した。かつてヘッドレスの見分けに使われていたプロパティは、Chrome 側があらかた塞いでいる。今月書いたエージェントの指紋の研究も、逆方向から同じ結論に着いていた。ブラウザの機能だけではエージェントと人間はほとんど分離できず(F1 0.80)、分離するのは振る舞いのほうだ、という話である。

違ったのは UA 文字列・画面サイズ・ログイン状態

3 つある。どれも凝った検知を必要としない。

User-Agent 文字列が HeadlessChrome を名乗っている。推測でも何でもなく、JavaScript が動くより前に、リクエストごとにヘッダーで自己申告している。表の一段上に注目してほしい。userAgentData.brands はどちらの経路でも素の「Google Chrome 151」を返す。Client Hints にはヘッドレスの印が乗らない。乗るのは旧来の UA 文字列のほうで、送らないようにするには明示的に外すしかない。

screen が 800x600 である。ヘッドレス Chrome の既定の仮想ディスプレイだ。1920x1080 の画面を持つ M4 のラップトップで 800x600 のまま browsing している人はいない。「Mac、16 GB、M4 の GPU、画面は 800x600」という組み合わせは、機械学習を持ち出すまでもなく辻褄が合っていない。

セッションが無い。Cookie は 682 バイトに対して 112 バイト、SID は無く、ページにはサインインのリンクが出ていた。google.com を一度も見たことがない新品のプロファイルである。拡張のほうは私が普段使っているブラウザそのもので、ログイン済みで履歴も背後にある。

どれか 1 つだけでも判断材料として足りる。CDP 接続は検知される必要すらなかった。ヘッドレスのブラウザから 800x600 でアカウントも無いというリクエストが届き、それ相応に扱われた、というだけだ。

1 つだけ捨てた計測がある。window.outerWidth はヘッドレス側で 1440、拡張側で 0 を返した。期待と逆である。これはブラウザではなく拡張の隔離実行コンテキストの都合で、ブラウザと同時にツールそのものを測ってしまっている。一致した行のほうに重みがあるのは、まさにこの理由による。

これは回避の手順書ではない

この問題で検索して出てくるのは、たいていステルス系のプラグインか unblock を謳う API である。そこに 1 本足すつもりはないし、CAPTCHA を突破しようともしていない。エージェントには突破しない指示を入れてあり、その指示は正しい。セッションも無いヘッドレスブラウザに Google が確認画面を出すのは、Google が正しく動いているということだ。プログラムから検索結果が欲しいなら、筋は API を使うか、人間が実際にそこにいるセッションで読むかのどちらかしかない。

有用な問いは、どうすれば人間らしく見えるか、ではない。どの経路を選ぶか、である。

どの経路を選ぶか

自分の身元が要る作業は拡張。ログインが要るダッシュボードを読む、検索エンジンが「自分に」何を見せているかを確認する、SSO の向こう側に用がある、といった作業だ。管理されたブラウザは空の Cookie で始まるので、これをやろうとするとロボットを自分のアカウントにログインさせるか、プロファイルを複製することになる。どちらも字面より厄介である。

公開ページが相手なら Chrome DevTools MCP。パフォーマンスのトレース、コンソールのエラー、ネットワークのウォーターフォール、レイアウトの調査。拡張では取れないプロトコル階層のデータが的を絞って返ってくるし、プロファイルが空なのは再現性の面ではむしろ利点だ。そもそも相手が誰なのかを気にするサイトではない。

無人で回すなら Playwright MCP。ローカルの Chrome を生かしておく必要も、拡張が繋がっているのを祈る必要もない。まさに今日踏んだ失敗がそれだった。ただしボットの壁に当たる確率が一番高い経路でもあるので、向ける先は自分が管理しているシステムにしておく。

分かれ目は、相手が「誰が聞いているか」を気にするかどうかである。私の SERP チェックは気にする側に立っていて、それに答えられない経路の上に静かに乗っていた。これまで動いていたのは、拡張がたまたま毎回繋がっていたからにすぎない。

Google 側の検知コストの安さも書いておく価値がある。振る舞いの分析も、マウスの動きのモデルも、CDP の探索も要らない。ヘッダーと画面サイズだけである。AI クローラーと JavaScript のときと同じ非対称で、技術的に面白い問い(エージェントの指紋は取れるか)は、もっと退屈な問い(そいつは自分について何を申告しているか)の下流にある。そして勝負を決めるのは退屈なほうだ。

エージェントにブラウザの道具を繋ぐなら、信用する前に上の関数を各経路で流してみるといい。10 分の計測のほうが、検索結果 1 ページ分より多くを教えてくれた。