cdp について
cdpは、コーディングエージェント向けのブラウザー操作スキルです。CDP全体を扱う型付きSDKとbrowser-harness-js CLIを備え、常駐セッションでChrome接続と状態を呼び出し間で再利用します。
主な機能
- 01CDPメソッドの型付きラッパー
- 02CLIからJavaScriptスニペットを実行
- 03常駐CDPセッションを再利用
- 04ブラウザータブへの接続と切り替え
- 05イベントの購読と待機
活用シーン
- 01スクリプトでページを移動・検査
- 02呼び出し間でブラウザーセッションを再利用
- 03ページやネットワークイベントの監視
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ browser-use/ browser-harness-js --skill cdpインストール後、Claude Code で/cdpを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdLICENSE.gitignoreinteraction-skills/connection.mddialogs.mdcookies.mdcross-origin-iframes.mddrag-and-drop.mddownloads.mddropdowns.mdiframes.mdprint-as-pdf.mdscreenshots.mdnetwork-requests.mdscrolling.mdshadow-dom.mdtabs.mduploads.mdviewport.mdsdk/bun.lockbrowser-harness-jsgen.tsrepl.tspackage.jsonsession.tssession.test.tsjs_protocol.jsontsconfig.json詳細
CDP — browser-harness-js スキル
カスタム生成された CDP SDK(browser_protocol.json と js_protocol.json の全メソッドに型付きラッパーを用意)に加え、永続的な CDP Session を保持する小さな HTTP サーバーです。browser-harness-js CLI は初回使用時にサーバーを自動起動し、JavaScript スニペットをサーバーに転送します。
SDK はスキルの sdk/ ディレクトリにあります。このドキュメントでは、<skill-dir> は npx skills add でスキルをインストールした場所を指します(Claude Code:~/.claude/skills/cdp、Cursor:~/.cursor/skills/cdp、その他のエージェントでは異なります)。CLI は PATH 上で browser-harness-js として利用できる必要があります。
セットアップ(初回のみ)
npx skills add はエージェントのスキルディレクトリにスキルを配置しますが、CLI は PATH に追加しません。初回の呼び出し前に、CLI を利用できることを確認し、利用できない場合は PATH 上のディレクトリにシンボリックリンクを作成してください。
# macOS (Apple Silicon + Homebrew)
command -v browser-harness-js >/dev/null || ln -sf <skill-dir>/sdk/browser-harness-js /opt/homebrew/bin/browser-harness-js
# macOS (Intel) / most Linux — may need sudo
command -v browser-harness-js >/dev/null || ln -sf <skill-dir>/sdk/browser-harness-js /usr/local/bin/browser-harness-js
# Linux without sudo (ensure ~/.local/bin is on PATH)
command -v browser-harness-js >/dev/null || { mkdir -p ~/.local/bin && ln -sf <skill-dir>/sdk/browser-harness-js ~/.local/bin/browser-harness-js; }
初回実行時に bun がない場合、CLI は自動的にインストールします(サーバーは Bun ネイティブです)。オプトアウトするには BROWSER_HARNESS_SKIP_BUN_INSTALL=1 を設定してください。
使用方法
browser-harness-js '<JS>' を実行するだけです。初回の呼び出しではバックグラウンドでサーバーが起動し、以降の呼び出しでは同じプロセスに接続するため、同じ session、Chrome への同じ WebSocket、設定したグローバル変数を再利用します。
browser-harness-js 'await session.connect()'
browser-harness-js 'await session.Page.navigate({url:"https://example.com"})'
browser-harness-js '(await session.Runtime.evaluate({expression:"document.title",returnByValue:true})).result.value'
出力は生の結果コンテンツで、{ok,result} のラッパーはありません。
| 結果の型 | 標準出力 |
|---|---|
| 文字列 | JSON の引用符なしのテキスト(例:Example Domain) |
| 数値/ブール値 | 42、true |
| オブジェクト/配列(空でない場合) | コンパクトな JSON(例:{"frameId":"..."}、[1,2,3]) |
undefined/null/""/{}/[] | 空(出力なし) |
エラーは標準エラー出力に出力され、終了コードは 1 です。CDP のエラーメッセージと JavaScript のスタックが、そのまま表示されます。例:
Error: CDP -32602: invalid params
at _call (.../session.ts:117:33)
...
if browser-harness-js '...'; then ...; else handle_error; fi を使うか、$? を確認して失敗を検出してください。
標準入力から複数行のスニペットを渡す(ヒアドキュメント)。 重要:複数の文を含むスニペットでは、最後の式は自動的に返されません。return X を明示的に記述してください。最初の引数として渡す単一式のスニペットは、自動的に値が返されます。
browser-harness-js <<'EOF'
const tabs = await listPageTargets();
globalThis.tid = tabs[0].targetId;
await session.use(globalThis.tid);
return globalThis.tid;
EOF
CLI コマンド
| コマンド | 動作 |
|---|---|
browser-harness-js '<js>' | 必要に応じてサーバーを自動起動し、JavaScript を評価して結果を出力します。 |
browser-harness-js <<EOF…EOF | 同じ動作をします。標準入力からコードを受け取ります。 |
browser-harness-js --status | ヘルス情報の JSON(稼働時間、接続状態、sessionId)を出力します。停止中の場合は終了コード 1 で終了します。 |
browser-harness-js --start | 明示的に起動します(すでに起動している場合は何もしません)。 |
browser-harness-js --stop | 正常にシャットダウンします。セッションの状態は破棄されます。 |
browser-harness-js --restart | 停止して新たに起動します。 |
browser-harness-js --logs | サーバーのログ(/tmp/browser-harness-js.log)を tail -f で表示します。 |
環境変数:CDP_REPL_PORT(デフォルト 9876)、CDP_REPL_LOG(デフォルト /tmp/browser-harness-js.log)。
スニペット内の API
以下のグローバル変数はあらかじめ読み込まれているため、インポートは不要です。
session— 永続的なSessionです。すべての CDP ドメインがマウントされています:session.Page、session.DOM、session.Runtime、session.Network、……全 56 ドメイン、合計 652 メソッドです。listPageTargets()— CDP のTarget.getTargetsを使って実際のページターゲットを一覧表示します(Chrome 144 以降でも動作します)。chrome://およびdevtools://の URL は除外されます。引数は不要で、接続済みのセッションを使用します。detectBrowsers()— OS 固有のプロファイルディレクトリを検索し、リモートデバッグを有効にして実行中の Chromium ベースのブラウザーを検出します。[{name, profileDir, port, wsPath, wsUrl, mtimeMs}]を返し、起動時刻の新しい順に並べます。resolveWsUrl(opts)—{wsUrl}|{port, host?}|{profileDir}から WS URL を解決します。引数なしの自動検出フローでは、代わりにsession.connect()を直接呼び出してください。CDP— 生成された名前空間(CDP.Page、CDP.Runtime、……)です。型名の参照に使用します。
CDP メソッドの呼び出し
すべてのメソッドは、CDP のワイヤーパラメーターに対応するオブジェクト引数を 1 つ取ります。型付きの戻り値を返します(result のラッパーや id による対応付けはありません。これらは自動的に処理されます)。
// no params
await session.DOM.enable()
// required params
await session.Page.navigate({ url: 'https://example.com' })
// all-optional params (object also optional)
await session.Page.captureScreenshot()
await session.Page.captureScreenshot({ format: 'png', quality: 80 })
// returns are stripped to the typed shape
const { root } = await session.DOM.getDocument()
const { nodeId } = await session.DOM.querySelector({ nodeId: root.nodeId, selector: 'h1' })
接続
デフォルトでは、引数なしで session.connect() を呼び出すだけです。 OS ごとのプロファイルディレクトリをスキャンして DevToolsActivePort ファイルを探し、最後に起動したものから順に、WebSocket 接続を受け付ける最初のブラウザーを選択することで、実行中の Chromium ベースのブラウザー(Chrome、Chromium、Edge、Brave、Arc、Vivaldi、Opera、Comet、Canary)を自動検出します。OS を問わず、macOS、Linux、Windows で動作します。
await session.connect() // auto-detect
接続前に利用可能なブラウザーを確認したい場合(またはユーザーに選択してもらう場合)は、先に detectBrowsers() を使います。
const found = await detectBrowsers()
// [{ name: 'Google Chrome', profileDir, port, wsPath, wsUrl, mtimeMs }, ...]
明示的な指定方法 — 自動検出で誤ったブラウザーが選ばれた場合、または接続先がすでにわかっている場合にのみ使用します。
| 形式 | 使用する状況 |
|---|---|
{ profileDir } | 複数のブラウザーが実行中の場合に、特定のブラウザーを対象にします。<profileDir>/DevToolsActivePort を直接読み取ります。 |
{ wsUrl } | ws://…/devtools/browser/<uuid> がすでにわかっている場合(例:別の場所からパイプで渡された場合)。 |
await session.connect({ profileDir: '/Users/<you>/Library/Application Support/Google/Chrome' })
await session.connect({ wsUrl: 'ws://127.0.0.1:9222/devtools/browser/<uuid>' })
OS ごとのプロファイルパス — { profileDir } と組み合わせて使用します。
- macOS:
~/Library/Application Support/<Browser>(例:Google/Chrome、Comet、BraveSoftware/Brave-Browser、Arc/User Data) - Linux:
~/.config/<browser>(例:google-chrome、chromium、BraveSoftware/Brave-Browser) - Windows:
%LOCALAPPDATA%\<Browser>\User Data(例:Google\Chrome、Microsoft\Edge、BraveSoftware\Brave-Browser)
候補ごとの WS 接続タイムアウトのデフォルトは 5s です。実行中のブラウザーは約 100ms 以内に接続の開始・終了に応答するため、5s あれば十分です。5s では短すぎるのは、Chrome に Allow ポップアップが表示され、ユーザーのクリックを待っている場合だけです。その可能性がある場合は、timeoutMs: 30000 を渡します。
await session.connect({ profileDir: '/Users/<you>/Library/Application Support/Google/Chrome', timeoutMs: 30_000 })
No detected browser accepted a connection と表示された場合 — ブラウザーには DevToolsActivePort ファイルがありますが、現在 WS 接続を受け付けているブラウザーがありません。最もよくある原因は、リモートデバッグが有効になっているものの、ユーザーがまだプロンプトの Allow をクリックしていないことです。Allow をクリックしてから再試行するよう伝えてください(または timeoutMs を増やしてください)。
対象(タブ)の選択
connect() の後に session.use(targetId) を一度呼び出すと、それ以降のページレベルの呼び出し(Page/DOM/Runtime/Network など)は、その対象の sessionId に自動的にルーティングされます。Browser.* と Target.* の呼び出しは、常にブラウザーエンドポイントに送られます。
const tabs = await listPageTargets() // no args; uses the connected session
const sid = await session.use(tabs[0].targetId)
await session.Page.enable()
await session.Page.navigate({ url: 'https://example.com' })
listPageTargets() は CDP の Target.getTargets を使用するため(/json ではありません)、Chrome 144+ でも動作します。chrome:// と devtools:// の URL はすでに除外されています。同等の生の呼び出し:
const { targetInfos } = await session.Target.getTargets({})
const tabs = targetInfos.filter(t => t.type === 'page' && !t.url.startsWith('chrome://') && !t.url.startsWith('devtools://'))
タブを切り替えるには、session.use(otherTargetId) を呼び出します。デタッチするには、session.setActiveSession(undefined) を呼び出します。
イベント
// Subscribe (returns an unsubscribe fn)
const off = session.onEvent((method, params, sessionId) => { ... })
// Or wait for a single matching event with optional predicate + timeout
await session.Network.enable()
const ev = await session.waitFor(
'Page.frameNavigated',
(p) => p.frame.url.includes('example.com'),
10_000
)
呼び出し間での状態の保持
各スニペットは独自の非同期ラッパー内で実行されるため、let/const の宣言は戻り時に消失します。データを後続の呼び出しに引き継ぐには、globalThis にアタッチします。
browser-harness-js '(await listPageTargets()).forEach((t,i)=>globalThis["tab"+i]=t.targetId)'
browser-harness-js 'await session.use(globalThis.tab0)'
browser-harness-js 'await session.Page.navigate({url:"https://example.com"})'
session 自体、アクティブな sessionId、イベントサブスクライバーはサーバーによってすでに保持されます。グローバル変数が必要なのは、その場限りのデータだけです。
実行中の Chrome への接続(chrome://inspect の手順)
ユーザーがすでに実行しているブラウザーに接続する場合:
- まず引数なしで
await session.connect()を試します — 自動検出はDevToolsActivePortを介して、すべての Chromium ベースのブラウザーに対応します。これが成功すれば完了です。 - 自動検出で
No running browser with remote debugging detectedが返された場合、ユーザーがリモートデバッグを有効にする必要があります。inspect ページを開きます。
macOS の AppleScript 経由の方法だけがプロファイル選択画面を回避します。Linux/Windows では先にプロファイルを選択するよう求められる場合があります。# macOS — prefer AppleScript over `open -a` (reuses current profile, avoids the profile picker) osascript -e 'open location "chrome://inspect/#remote-debugging"' # Linux google-chrome 'chrome://inspect/#remote-debugging' # or: chromium, google-chrome-stable # Windows (PowerShell) Start-Process chrome 'chrome://inspect/#remote-debugging' - chrome://inspect で 「Discover network targets」 にチェックを入れ、Chrome のプロンプトが表示されたら Allow をクリックします。
- 自動検出で誤ったブラウザーが選ばれた場合(複数のブラウザーが実行中で、特定のものを使いたい場合):
await detectBrowsers()で一覧を取得してから、await session.connect({ profileDir: <the one you want> })を実行します。 session.connect()がNo detected browser accepted a connectionを返した場合、ユーザーはリモートデバッグを有効にしていますが、まだ Allow をクリックしていません。クリックして再試行するよう伝えるか、クリックを待つためにtimeoutMs: 30000を渡します。
ターゲット(タブ)の操作
- Chrome の内部ページを除外します。
listPageTargets()はすでにchrome://とdevtools://の URL を除外します。Target.getTargets()を直接呼び出す場合は、手動でフィルタリングしてください。 - CDP のターゲット順と、タブストリップに表示される順序は一致しません。 ユーザーが「見えている最初のタブ」と言った場合は、スクリーンショットまたはページタイトルで特定してください。
Target.activateTargetで切り替えられるのは、既知の targetId のみです。
メソッドを調べる
型定義を含む全 API は <skill-dir>/sdk/generated.ts(約 655 KB)にあります(読み込まれるのはこのファイルを読んだ場合のみです)。各メソッドには CDP の説明が JSDoc コメントとして記載され、ドメインごとの名前空間に型定義された *Params/*Return インターフェースがあります。
grep -n "navigate" <skill-dir>/sdk/generated.ts | head
SDK の再生成
上流のプロトコル JSON が変更されたら、sdk/browser_protocol.json および/または sdk/js_protocol.json を置き換えて、次を再実行します。
cd <skill-dir>/sdk && bun gen.ts
browser-harness-js --restart # pick up the new bindings
ファイル
すべてのパスは <skill-dir>(インストール先。このドキュメントの冒頭を参照)からの相対パスです。
/usr/local/bin/browser-harness-js→<skill-dir>/sdk/browser-harness-js(CLI)sdk/repl.ts— HTTP サーバー(127.0.0.1:9876でのBun.serve)sdk/session.ts—Sessionクラス(トランスポート、接続、ターゲットのルーティング、イベント)sdk/generated.ts— コード生成の出力:すべての CDP メソッドを型付きラッパーとして定義sdk/gen.ts— コード生成スクリプトsdk/{browser,js}_protocol.json— 上流のプロトコル(ベンダー化済み)