Claude Media
browser use toolのbrowser_stateでタブ管理を実装する

browser use toolのbrowser_stateでタブ管理を実装する

Browser use toolのnew_tab・list_tabs・switch_tab・close_tabとbrowser_stateブロックの入出力仕様、ダウンロード報告の実装手順をまとめます。

Browser use toolのタブ管理は、new_tab / list_tabs / switch_tab / close_tabの4メンバーとbrowser_stateコンテンツブロックの組み合わせで実装します。Claudeはタブをtab_idで識別しますが、タブの実体を持つのはアプリケーション側で、どのタブが開いていてどれがアクティブかをClaudeに伝える責務も実行環境にあります。本記事では4つのタブ管理メンバーの入出力仕様と、ダウンロード報告の実装パターンを扱います。

Browser use toolはタブの実体を持たない

Browser use toolはAnthropicが定義するクライアントツールセットです。browser_toolset_20260801エントリを1つtools配列に加えるだけで、navigateread_pageleft_clickscreenshotなど27のメンバーツールがClaudeに渡ります。任意の4メンバー(javascript_exec / file_upload / read_console / read_network)を有効化すると合計31メンバーになります。

実際のブラウザを操作するのはAnthropic側ではなく、アプリケーションが用意する実行環境です。タブの開閉・切り替え・現在アクティブなタブの管理もすべてアプリケーション側の責務で、Claudeはtab_idという文字列でタブを参照するだけです。

対応モデルはclaude-fable-5-1 / claude-mythos-5-1 / claude-fable-5 / claude-mythos-5 / claude-opus-5 / claude-sonnet-5 / claude-opus-4-8です。利用できるのはClaude APIとGoogle Cloudのみで、Claude Platform on AWS・Amazon Bedrock・Microsoft Foundryでは提供されていません。Claude Managed Agentsでも利用できないため、Managed Agents経由の実行環境を検討している場合は別のツールに切り替える必要があります。

curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 2048,
    "tools": [{ "type": "browser_toolset_20260801" }],
    "messages": [
      { "role": "user", "content": "Open example.com/docs and tell me how to get started." }
    ]
  }'

browser_stateブロックがタブ状態を運ぶ

browser_stateは、メンバー呼び出しの結果に含める専用のコンテンツブロックです。Claudeはこのブロックを直接見るわけではなく、APIがブロックの内容をテキストへレンダリングしてClaudeに渡します。tabsは呼び出し後に開いている全タブの一覧で、差分ではありません。空でもよく、空でなければ必ず1件だけactive: trueが付きます。

{
  "type": "browser_state",
  "tabs": [
    { "tab_id": "tab-1", "title": "Documentation", "url": "https://example.com/docs", "active": true },
    { "tab_id": "tab-2", "title": "Pricing", "url": "https://example.com/pricing" }
  ]
}

送信ルールは次のとおりです。

  • browser_stateはメンバー呼び出しに答える結果にだけ、1結果につき最大1つ送る。is_error: trueの結果には絶対に付けない
  • tab_idtitleurlは各4,096文字以内、tab_idは空文字不可、制御文字や改行を含められない
  • 1ブロックあたり最大100タブ・200件のstate_changes
  • tab_idはアプリケーション側が採番する任意の文字列でよいが、そのIDのタブがまだ「開いている」と報告されている間は再利用できない

タブのタイトルやURLはページ由来の値で、プロンプトインジェクションの経路になります。APIはタイトル中のダブルクォートとバックスラッシュを自動でエスケープしてレンダリングするため、事前エスケープすると二重エスケープになります。URLはそのままレンダリングされるので、ページが供給したURLは実行環境側でサニタイズしてから渡します。

タブ管理4メンバーの入出力

メンバー入力Claudeが読むテキスト
new_tab入力なしClaudeが読むテキストCreated new tab with tab_id: {tab_id}, URL: {url}. It is now the current tab.
list_tabs入力なしClaudeが読むテキストAvailable tabs: に続けてタブ一覧、なければNo tabs available
switch_tab入力tab_id(必須)Claudeが読むテキストSwitched to tab {tab_id}
close_tab入力tab_id(必須)Claudeが読むテキストClosed tab {tab_id}

この4メンバーの成功結果は、他のメンバーと違ってcontentbrowser_stateブロック1つだけになります。テキストや画像は含めません。テキストはAPI側がブロックから生成するので、実行環境が独自の文言を足す必要はありません。

list_tabsが2タブ(1つ目がアクティブ)を返すと、次の形でレンダリングされます。

Available tabs:
  • tab_id tab-1: "Documentation" (https://example.com/docs) (current)
  • tab_id tab-2: "Pricing" (https://example.com/pricing)

new_tabの結果はstate_changestab_openedエントリをちょうど1つ持たせ、そのtab_idactive: trueのエントリと一致させます。一致しない、あるいはcontentが1ブロックちょうどでない結果は、APIがレンダリングできずinvalid_request_errorになります。

タブ管理以外の呼び出しでもtabsを報告する

new_tab系の4メンバー以外では、browser_stateは任意です。開いているタブの集合・アクティブタブ・タブのタイトルやURLが変わったとき、または報告すべきstate_changesがあるときに送ります。送る場合は必ずtabsのフル一覧を含めます。

テキストとbrowser_stateを同じ結果に含めると、APIはその結果のテキストの後に空行を挟んでTab Contextフッターを追加します。

Tab Context:
- Executed on tab_id: tab-1
- Available tabs:
  • tab_id tab-1: "Documentation" (https://example.com/docs)
  • tab_id tab-2: "Pricing" (https://example.com/pricing)

フッターが付かないケースが3つあります。zoom結果は常に付かない。textブロックを持たない結果(画像だけのscreenshot結果など)には付かず、タブの変化は次にテキストとbrowser_stateを両方持つ結果まで持ち越されます。tab_idを渡さなかった呼び出しでtabsが空の結果にも付きません。フッター自体は重複排除されるので、同じタブ状態を毎回律儀に送っても損はありません。

ダウンロードは3種類のstate_changesで相関させる

クリックやナビゲーションでファイルダウンロードが始まったら、そのダウンロードが発生した呼び出しの結果でstate_changesに報告します。ダウンロードは非同期に進み複数の結果をまたぐことがあるため、download_idをアプリケーション側で採番して各イベントを紐付けます。

typeフィールド送るタイミング
download_startedフィールドdownload_id, url送るタイミングダウンロードが始まった呼び出しの結果。urlはリダイレクト後の最終URL
download_completedフィールドdownload_id, url, path?, size_bytes?送るタイミングダウンロード完了時に実行中だった呼び出しの結果。pathはBashツールなど同じ環境の別ツールがそのパスを読める場合だけ含める
download_failedフィールドdownload_id, url, error?送るタイミング失敗またはキャンセル時。理由が分かればerrorに入れる

1ブロックにつき同じdownload_idのエントリは1件までで、同じ呼び出し中に開始と完了が終わるダウンロードはdownload_completedだけを送ります。state_changesは「進行中ダウンロードの一覧」ではなく、各イベントを一度だけ報告する仕組みです。is_error: trueの結果では絶対に送らず、失敗した呼び出し中に起きたダウンロードイベントは次の成功結果に回します。

urlはリダイレクト後の値で署名付きクエリパラメータを含むことがあるため、Claudeの文脈に残したくないパラメータは報告前に取り除きます。pathをファイルシステムのパスとしてそのまま使う前に、実行環境側での検証も必要です。

よくあるつまずき

  • tabsが空でないのにactive: trueが1件もない、または複数ある。空でないtabsは必ずちょうど1件がactive: trueである必要があり、違反するとinvalid_request_errorになります
  • is_error: trueの結果にbrowser_stateを付ける。エラー結果はテキストのみで、ブロックは付けられません
  • 「報告することがない」を空のstate_changesで表現するstate_changesを省略するか、フィールド自体を付けないかのどちらかで表現します。空配列は拒否されます
  • 同じtab_idを、そのIDのタブがまだopen扱いのうちに使い回す。重複tab_idはAPIが拒否します
  • new_tabの結果にtextimageを混ぜる。成功結果のcontentbrowser_state1つちょうどでなければなりません
  • タイトルを事前エスケープして渡す。APIが二重引用符とバックスラッシュを自動でエスケープするため、二重エスケープになります

エグゼキュータの実装で外せないセキュリティ対策

Browser use toolは実行環境が用意したブラウザをClaudeに操作させるため、ページが供給するテキストはすべて未検証の入力として扱う設計が前提です。公式ドキュメントは次の対策を挙げています。

  • ブラウザと実行環境は、資格情報を持たない専用のコンテナや仮想マシンで、最小権限で動かす
  • ブラウザが到達できるホストをネットワーク層の許可リストに絞り、navigateハンドラでリダイレクト後も再チェックする
  • タブのタイトル・URL、ダウンロードのurl / path / errorを含め、ページが供給するものはすべて未検証の入力として扱う。ページの読み取りはaccessibility treeや可視テキストから構築し、生のDOMソースは使わない(隠しテキストをClaudeに渡さないため)
  • navigateハンドラでは"back" / "forward" / "reload"のキーワードを受け付け、スキームなしのURLはhttps://とみなし、URLパーサーでスキームを判定してhttp/https以外(javascript: / file: / data:など)を拒否する。APIはナビゲーション先を見ないため、この拒否は実行環境側の責任です
  • javascript_execfile_uploadは、実装が済んでいてタスクが必要とするときだけ有効化する
  • 購入・アカウント変更・メッセージ送信・規約同意など取り消しにくい操作は、呼び出しごとに人間の確認を挟む

Anthropicはプロンプトインジェクションに抵抗するようモデルを訓練したうえで、追加の防御層を用意しています。browser use toolを使うと、ブラウザが返すページテキストやスクリーンショットを分類器が自動でスキャンし、プロンプトインジェクションの疑いを検知するとClaudeを誘導して、その指示が本当にユーザー由来かを確認させてから行動させます。ただしこの保護はhuman-in-the-loopがないユースケースなど、すべての用途に適するわけではなく、上記の実行環境側の対策の代わりにはなりません。

browser_stateで報告するタブのタイトル・URL、ダウンロードのurl / path / errorも同じ理由で未検証データです。実行環境側でサニタイズしてから渡す前提は、タブ管理の実装そのものに組み込んでおく必要があります。

Browser use toolと他のクライアントツールの使い分け

タスクの性質選ぶツール
Webページ内で要素を読み書き・クリックする選ぶツールBrowser use tool
デスクトップ全体の操作が必要選ぶツールComputer Useツールを自前実装する最小構成
ページを1回読む・Web検索するだけ選ぶツールWeb fetch tool / Web search tool(APIがサーバー側で実行するのでブラウザ実装は不要)

Computer use toolと同時に宣言することもできます。座標系はBrowser use toolがビューポートピクセル、Computer use toolがデスクトップスクリーンショットピクセルで別系統になり、screenshotのようにメンバー名が重複してもtoolset_nameで呼び出し先が区別されます。

トークンオーバーヘッドの実測値

browser_toolset_20260801をデフォルトのメンバー構成で宣言すると、リクエストに約6,600トークンが加算されます。Claude Fable 5・Claude Mythos 5・Claude Opus 5・Claude Opus 4.8では約6,610トークン、Claude Sonnet 5では約6,670トークンです。任意の4メンバーをすべて有効化すると約880トークン増え、configsでメンバーを無効化するとその分減ります。正確な値はレスポンスのusageで確認できます。

これに加えて、スクリーンショットやzoomの画像は画像入力として課金され、アクセシビリティツリーやページテキスト、コンソール・ネットワークのエントリはテキスト入力として課金されます。ダウンロード本体やアップロードファイルは実行環境側に留まり、APIには送られません。

実装時に効いてくる制約

要素参照(ref_*)はベストエフォートです。仮想化リストやCanvas描画のようなページでは安定した参照を持てず、Claudeはスクリーンショットと座標クリックにフォールバックします。read_consoleread_networkは実行環境のブラウザ自動化スタックが取得できた分しか返せず、取得はタブへのアタッチ以降に限られるので、空の結果が「元々トラフィックが無かった」ことを意味するとは限りません。ストリーミング時は各メンバーのinputが1つのinput_json_deltaとして届く仕様も、逐次パースを前提にした実装では踏みやすい点です。

まとめ

Browser use toolのタブ管理は、new_tab / list_tabs / switch_tab / close_tabという薄いインターフェースの裏に、browser_stateブロックのレンダリング規約を正確に守る実装が要求されます。タブ管理系メンバーの結果はbrowser_state1つちょうど、それ以外の結果では任意送信、ダウンロードはdownload_idで相関する3イベント。この3点を押さえれば、Claudeがlist_tabsを呼ばなくてもタブ状態を追える実行環境になります。

BashツールをAPIで実装するセンチネル行とタイムアウト処理は、ダウンロードしたpathを別ツールから読ませる構成の参考になります。実行環境をTypeScriptで書く場合はClaudeのTypeScript SDKで実装するStreaming・Tool・MCPヘルパーも合わせて参照してください。

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