agent-browser
agent-browser
AIエージェント向けの堅牢なローカルブラウザ。依存関係ゼロ。 既存のChromeをDevToolsプロトコルで操作し、トークン効率の良いMarkdownを抽出し、自動化として簡単に検出されません。
サーフェス | インストール | 使用目的 |
MCPサーバー |
| Claude Code、Cursor、Codex、任意のMCPクライアント |
CLI |
| シェル、スクリプト、CI |
ライブラリ |
| 独自のNodeコード |
DSH / Cordisプラグイン | コンポジション行 | DSHハーネス内のネイティブツール |
npx -y @truenix/agent-browser markdown https://news.ycombinator.comすべてローカルで動作します。アカウント不要、APIキー不要、リモートサービス不要、クォータなし。
なぜ
エージェントに生のHTMLを渡すと、そのコンテキストのほとんどを無駄にします。実際のページで測定した結果:
そして、自動化であることを宣言するブラウザは、ブロックされたり、劣化したり、異なるコンテンツを提供されたりします。これは、エージェントが結論した内容を静かに破壊します。
ヘビーサイトベンチマーク(ライブ、curl vs markdown)
サイト | 生のHTML | markdown |
| 削減率 |
| 927 kB | 120 kB | 55 kB | 7.7× / 16.8× |
| 273 kB | 11 kB | 8.7 kB | 23.8× / 31.4× |
| 323 kB | 10 kB | 7.3 kB | 32.0× / 44.2× |
| 648 kB | 69 kB | 13 kB | 9.3× / 48.9× |
| 360 kB | 4.6 kB | 3.5 kB | 78.6× / 102.8× |
| 35 kB | 10 kB | 3.2 kB | 3.4× / 11.0× |
両方のチャートとこの表の基になる測定値は、npm run charts で再生成できます。ライブで測定し、agent-browser自身を使ってPNGを描画するため、画像内の数値がコードが生成するものからずれることはありません。
同じページでより少ないトークンを使う
URLはWikipediaの記事では出力の3分の1、github.com/trendingでは5分の4を占めます。エージェントがナビゲーションではなく読む場合には、それらを削除してください:
agent-browser markdown https://github.com/trending --links text # 69 kB -> 13 kB
agent-browser markdown https://en.wikipedia.org/wiki/Rust --links relativeモード | レンダリング | 使用する場面 |
|
| エージェントが次にナビゲートする場合 |
|
| 同一サイトのクロール用。ターゲットを保持し、オリジンを削除 |
|
| 読む、要約する、質問応答 |

YCチェックあたりのトークンコスト(--links text でHN + YCブログを読んだ場合、51.1k → 1.5kトークン、+500出力)— 2026年8月のリスト価格:Anthropic Haiku 4.5 $1/$5、Sonnet 5 $3/$15、Opus 5 $5/$25 • OpenAI GPT-4o $2.50/$10、GPT-4o mini $0.15/$0.60
ロード後にレンダリングされるページ
ナビゲーションはloadイベントを待ちます。これはポーリングループと固定遅延ではなく、CDPライフサイクルイベントによって駆動されます。実際のページでは、バイト単位で同一の出力に対して2〜3倍高速です:
ページ |
| 旧固定250ms待機 |
| 7 ms | 258 ms |
| 115 ms | 340 ms |
| 160 ms | 365 ms |
| 218 ms | 466 ms |
コンテンツがロード後に到着するクライアントレンダリングアプリには、より大きな推測ではなく、実際のシグナルが必要です。旧250msのセトルもそのコンテンツを見逃していました:
agent-browser markdown https://some-spa.example --wait idleモード | 待機対象 | 使用する場面 |
| DOMが解析される | HTMLに含まれるマークアップのみが必要な場合 |
| loadイベント | ほぼ常に |
| ネットワークが静かになる | 結果が空のシェルのように見える場合 |
Related MCP server: Browser-MCP Navigator
その他のハーネス
フレームワークがJSON Schemaを話すなら、このプロジェクトがフレームワークの存在を知らなくても、agent-browserを駆動できます。agent-browser tools は、必要な形状でカタログを出力します:
agent-browser tools # MCP: {name, description, inputSchema}
agent-browser tools --format openai # OpenAI: {type:"function", function:{...}}
agent-browser tools --format anthropic # Anthropic: {name, description, input_schema}各ツールは、ハーネスができる方法に応じて、どちらの方法でも実行されます:
import { findTool, TOOLS } from '@truenix/agent-browser/tools';
import { withBrowser } from '@truenix/agent-browser';
// in-process: the harness can hold a CDP session
const tool = findTool('browser_markdown');
const text = await withBrowser({}, async (session) => {
await session.navigate('https://example.com');
return tool.run(session, { url: 'https://example.com', links: 'text' });
});
// out-of-process: the harness can only run a command (sandboxes, shells)
tool.cli({ url: 'https://example.com', links: 'text' });
// => ['markdown', 'https://example.com', '--links', 'text']インストール
MCPサーバーとして
claude mcp add browser -- npx -y @truenix/agent-browser mcp{
"mcpServers": {
"browser": {
"command": "npx",
"args": ["-y", "@truenix/agent-browser", "mcp"]
}
}
}ツール:browser_markdown、browser_text、browser_html、browser_links、browser_screenshot、browser_evaluate、browser_accessibility_tree、browser_pdf、browser_probe。
すべてのサーフェスは、1つの共有カタログから同じ9つのツールを公開するため、CLI、MCPサーバー、DSHプラグインが乖離することはありません。
ライブラリとして
npm install @truenix/agent-browserimport { withBrowser } from '@truenix/agent-browser';
const md = await withBrowser({}, async (session) => {
await session.navigate('https://example.com');
return session.markdown();
});DSH / Cordisプラグインとして
ワンライナー(推奨 — DSHを意図する場合に自動配線):
npx -y @truenix/agent-browser install # adds the bundle to ~/.dsh/profiles/web/package.json, then pnpm install (the mount ships in the package's own cordis.patch.yml layer)
# npx -y @truenix/agent-browser install --profile web --dry-run # preview
# npx -y @truenix/agent-browser uninstall # remove againdsh を再起動すると、9つのbrowser_*ツールがすべてネイティブツールとして表示されます。通常のnpm installではハンドラーは実行されません。意図的なinstallが必要です。
手動(自分でコンポジションを編集したい場合):
npm i -g @truenix/agent-browser
# or inside the harness checkout: pnpm add @truenix/agent-browserNode ≥ 18とChrome/Chromiumのインストールが必要です。次に、ホストコンポジションに追加します(ツールレジストリはホスト上にあり、エージェントごとではありません):
# ~/.dsh/profiles/web/cordis.patch.yml — persists for every web session
- insert:
- id: agent-browser
name: '@truenix/agent-browser/cordis'
config:
timeoutMs: 180000 # per-tool call budget; default respects AGENT_BROWSER_BIN / ENDPOINT
# cli: 'npx -y @truenix/agent-browser' # override only if needed短縮形(コンポジションがすでにinsertをラップしている場合):
- '@truenix/agent-browser/cordis':
timeoutMs: 180000環境変数の上書き:AGENT_BROWSER_BIN(Chromeバイナリ)、AGENT_BROWSER_ENDPOINT(--endpoint を介して長期実行ブラウザに接続)、または config.cli。
CLI
agent-browser <command> [options]
markdown <url> Extract the whole page as Markdown (main content by default)
text <url> Visible text only
html <url> Full serialized DOM after JavaScript runs
links <url> Every anchor as JSON
screenshot <url> PNG/JPEG (-o file, --full)
pdf <url> PDF (-o file)
a11y <url> Filtered accessibility tree
eval <url> <expr> Evaluate JS, return only its value (cheapest)
probe Browser, GPU and capability report
mcp Run as an MCP server on stdio
tools Print tool schemas (--format mcp|openai|anthropic)オプション:--headful、--no-stealth、--block-images、--gpu/--no-gpu、--width、--height、--viewport WxH、--main、--raw、--links、--max-rows、--limit、--wait、--settle、--full、--endpoint <ws>、--timeout、--json、-o。
読むためのmarkdown、調べるためのeval
このツールでエージェントが犯す最も高くつく間違いは、1行の質問に答えるためにページ全体をレンダリングすることです。3つの重いページにわたる3つの対象を絞った質問は、eval 経由で235バイト、Markdownでは77 kBかかります:
agent-browser eval https://github.com/trending \
"JSON.stringify(Array.from(document.querySelectorAll('article h2 a')).slice(0,3).map(a=>a.innerText.trim()))"
# ["openai / codex","mattpocock / skills","affaan-m / ECC"] -> 58 bytestextContentではなくinnerTextを使用してください。textContentは生のソースの空白を返します("openai /\n\n codex")。innerTextは実際にレンダリングされたものを返します("openai / codex")。これを間違えると、エージェントはリトライの往復全体を支払うことになり、節約したバイトよりもはるかにコストがかかります。
ページを実際に読む、要約する、検索する必要がある場合は、markdown を使用してください。
各呼び出しは独自のブラウザを起動します(約1.4秒)。連続して複数のルックアップを行う場合は、1つのブラウザを起動したままにして、--endpoint をそれに向けてください。同じ3つの質問は、3回のコールドスタートで5.9秒、ウォームスタートでは3.2秒かかります。
--endpoint は、ブラウザを起動する代わりに、すでに実行中のブラウザに接続します。多くの呼び出しにわたって単一の長期実行ブラウザを再利用する場合に便利です。
メモリ
Chromeの下限は、何も読み込む前に18プロセスで約420 MB PSSです。その下限はChromeのものであり、このパッケージのものではありません。フラグ調整で約5%動きますが、さらに動かすフラグ(--enable-low-end-device-mode)は、24コアの隣でnavigator.deviceMemory: 2を報告します。これは不可能なマシンであり、ブラウザがフラグされるまさにその種の不整合です。したがって、レバーはより小さいブラウザではなくより少ないブラウザです。
MCPサーバーは1つのブラウザを保持し、各ツール呼び出しに独自の分離されたコンテキストを提供します。3つの重いページを同時に取得した場合:
ピークPSS | プロセス数 | ウォールクロック | |
呼び出しごとにブラウザ | 1289 MB | 44 | 2114 ms |
1つのブラウザ、3つのコンテキスト | 654 MB | 20 | 2049 ms |
分離は変わりません。呼び出しごとのブラウザコンテキストが常にそれを提供していました。ブラウザは30秒のアイドル後にシャットダウンします(AGENT_BROWSER_IDLE_MS、0で即座に閉じる)。そのため、長期実行サーバーは会話の間に420 MBを占有しません。ウォーム状態での繰り返し呼び出しは、起動を完全にスキップし、約2倍高速に実行されます。
トレードオフ:タスクはプロセスツリーを共有するため、ブラウザレベルのクラッシュは1つの呼び出しではなく、進行中のすべてを停止させます。死んだブラウザは検出され、次の呼び出しで再起動されます。タスクごとに爆発半径の分離が必要な場合は、withBrowser を使用してください。これにより、各呼び出しに独自のブラウザが提供されます。
import { withPooledSession, shutdownPool } from '@truenix/agent-browser/pool';
await withPooledSession({}, async (session) => {
await session.navigate('https://example.com');
return session.markdown();
});
await shutdownPool(); // or let it idle outCLIの場合、各呼び出しは独自のプロセスであるため、再利用とは、自分で起動したままのブラウザに--endpointを向けることを意味します。
デーモン:すべてのCLI呼び出しに1つのブラウザ
各CLI呼び出しは独自のプロセスであるため、デフォルトではそれぞれが独自のChromeをコールドスタートします。10の同時呼び出しは10のブラウザを意味します。--daemon は代わりに1つの常駐ブラウザを共有し、各呼び出しは依然として独自の分離されたコンテキストを取得します:
agent-browser markdown https://example.com --daemon
agent-browser daemon --status
agent-browser daemon --stopウォールクロック | ピークChromeプロセス数 | |
3回の順次ルックアップ、デーモンなし | 5330 ms | — |
3回の順次ルックアップ、 | 3254 ms | — |
10の同時呼び出し、デーモンなし | 1443 ms | 140 |
10の同時呼び出し、 | 1144 ms | 32 |
これはオプトインです(--daemon または AGENT_BROWSER_DAEMON=1)。コマンドより長く存続するバックグラウンドプロセスを開始することは、要求する価値のある副作用だからです。オンデマンドで起動し、5分間アイドル状態が続くと終了します(AGENT_BROWSER_DAEMON_IDLE_MS)。環境変数を設定すると、MCPサーバーもそれにルーティングされます。これは、複数のMCPクライアントがマシンを共有する場合に価値があります。
--headful、--width、--height、--block-images、--gpu はブラウザ起動時に固定されるため、共有ブラウザはそれらを尊重できません。これらのいずれかを渡すと優先されます。その呼び出しは静かに独自のプライベートブラウザを取得し、stderrにその旨を出力します。
これが構築されている4つの障害モード。それぞれテストで検証されています:
デーモンは正確に1つ。 10の同時の最初の呼び出し元はすべて1つを生成しますが、9つはソケットをバインドする競争に負けて、何も起動せずに終了します。ソケットをバインドすることがロックであるため、古くなるロックファイルはありません。
開いているソケットが参照カウントです。 クライアントは作業中に接続を保持するため、
SIGKILLで強制終了されたクライアントでも解放されます。カーネルがソケットを閉じます。「解放してください」というメッセージは、参照を永久にリークしていたでしょう。アイドル終了。会話の間に約420 MBを占有しません。
SIGKILLされたデーモンは何も残しません:そのプロファイルマーカーはpidを名前として持つため、以下のスイープが次の起動時にブラウザを回収します。
自己クリーンアップ
すべての起動は、一時プロファイルに所有者マーカーを書き込み、所有者が死亡したプロファイルをスイープして、それらにまだ接続されているブラウザを強制終了します。SIGINT/SIGTERM/SIGHUPハンドラーと組み合わせると、次のようになります:
呼び出しの終了方法 | 孤立プロセス | 次の起動後 |
通常 | 0 | 0 |
SIGTERM / SIGINT | 0 | 0 |
SIGKILL(捕捉不可) | 1ブラウザ | 0 |
つまり、ハードキルで失われるのは最大で取り残されたブラウザ1つだけで、中断された呼び出しごとに1つではありません。10件の同時CLI呼び出しを繰り返しても、何も残りません。実行中のブラウザは決して掃除されません。その所有者プロセスはまだ実行中であり、帰属先のないプロファイルは、それを開いているものが何もなくなり、60秒間アイドル状態になるまで放置されます。
ボット検出
自分で実行してください: npm run test:bot。最新の結果:
detector | result |
31件成功、0件失敗 | |
6件グリーン、0件レッド、 | |
|
素のヘッドレスChromeはsannysoftの4行(HEADCHR_UA、CHR_MEMORY、WebGL SwiftShader、古いUA)で失敗し、ボットとして報告されます。
到達不能な検出器はSKIPとしてカウントされ、合格とは決してみなされません。また、ゲートは到達可能な3つの合格を要求するため、テストサイトがダウンしている日を成功と誤認することはありません。
環境はパッチ適用よりも重要
同一のコードを2つの場所で測定した結果:
このワークステーション | GitHub Actionsランナー | |
IP | 住宅用 | データセンター |
GPU | 実GPU(NVIDIA) | なし → SwiftShader |
| 31件成功、0件失敗 | 30件成功、1件失敗( |
| 6件グリーン、0件レッド | 6件グリーン、0件レッド |
|
|
|
すべてのCDPレベルのシグナルは両方でクリーンなままです。その部分はコードであり、コードは正しいのです。判定を覆すのは環境です。データセンターのASNとソフトウェアレンダリングが、フィンガープリントのパッチ適用では対処できない弱シグナルの複合体を引き起こします。
これが問題の正直な姿です。ブラウザの堅牢化は些細な兆候を取り除きます。残りを決めるのは実行場所です。
堅牢化が行うこととその理由
すべての項目は、私たちが間違っていると告げる検出器から生まれました:
--enable-automationなし。 PuppeteerとPlaywrightが追加するこのフラグが、navigator.webdriver = trueを設定するものです。生のCDPはこれを設定しないため、何もパッチを当てずにfalseのままです。Runtime.enableなし。 これは最も目立つCDPの兆候であり、古典的なコンソール/Error.stack検出器を動かします。Runtime.evaluateはそれがなくても正常に動作します。ウィンドウとスクリーンは連動します。
--ozone-override-screen-sizeなしの--window-sizeはouterWidth > screen.widthを生み出しますが、これは物理的に不可能であり、素のヘッドレスよりも強いシグナルです。デフォルトのデバイスメトリクスオーバーライドなし。 通常の1280×720はPlaywrightのデフォルトビューポートであり、検出器はそれを名前でフラグ付けします。必要な場合にのみ
--viewportを設定してください。利用可能な場合は実GPUを使用し、SwiftShaderの代わりに本物の
ANGLE (NVIDIA …)レンダラーを提供します。UAはCDP経由だけでなく、起動時に設定。
Emulation.setUserAgentOverrideはWeb Workersに到達しないため、ページがクリーンなUAを報告している間、ワーカーはヘッドレスUAを報告し続けます(hasInconsistentWorkerValues)。acceptLanguageオーバーライドなし。 CDPはそのヘッダーを分割してnavigator.languagesを導出するため、"en-US,en;q=0.9"は["en-US","en;q=0.9"]になります。これはq値が法的に存在できない場所であり、ページ/ワーカーの不一致も生みます。--langはこれを正しく行います。Client Hintsはバイナリ自身のバージョンから導出されるため、
Sec-CH-UAがnavigator.userAgentと矛盾することはありません。セッションごとに分離されたブラウザコンテキストを、クローズ時に破棄します。2つ目のブラウザプロセスなしで、タスクごとにクリーンな状態を実現します。
繰り返し現れる教訓: 一貫性は網羅性に勝る。 そのうちの4つは、部分的なスプーフィングが検出を容易にしたケースであり、実際の検出器を実行することでのみ発見されました。
WebGLスプーフィングがデフォルトでオフな理由
spoofWebglは存在し、注意深く実装されています。ネイティブのgetParameterを囲むProxyであり、Function.prototype.toStringは依然として[native code]を報告します。測定結果が逆効果だと示しているため、オフになっています。 test/webgl-spoof-experiment.mjsより:
構成 | 主張されるレンダラー | maxTexture | extensions | sannysoft | 判定 |
実GPU、スプーフなし | NVIDIA | 32768 | 37 | 0件失敗 |
|
SwiftShader、正直 | SwiftShader | 8192 | 35 | 1件失敗 |
|
SwiftShader + スプーフ | NVIDIA | 8192 | 35 | 0件失敗 |
|
持っていないハードウェアを主張すると、見た目上の1行は修正されますが、複合検出器で失敗します: 注入されたスクリプトはWeb Workersに到達しないため、ワーカーは依然としてSwiftShaderを報告し、レンダラー文字列がディスクリートGPUを主張している間もMAX_TEXTURE_SIZEはソフトウェアの値のままです。
正直なSwiftShaderは合格します。説得力のある嘘は合格しません。代わりにブラウザに実GPUを与えてください。無料です。
これが行わないこと
フィンガープリントレベルの検出が全範囲です。以下を打ち負かすことはできず、試みもしません:
TLS/JA3-JA4およびHTTP/2フィンガープリンティング — JavaScriptが実行される前に決定されます
IPレピュテーション — データセンター対住宅用ASN、しばしば実際の障壁となります
行動分析 — マウスの軌跡、タイミング、滞在時間
商用チャレンジ製品はこれらに依存しているため、「ゲートを通過する」とは自動化として簡単にフラグ付けされないことを意味し、決して検出不可能ではありません。自分のサイト、テスト、アクセシビリティ作業、および通常のエージェントブラウジングを対象としています。
メモリ
Chromeスタックのコストはおよそ450 MBです。鍵となるのはアーキテクチャであり、フラグではありません。タスクごとに1つのブラウザではなく、1つのブラウザと多数の分離コンテキストを実行します。ブラウザを一度起動し、--endpoint / AGENT_BROWSER_ENDPOINTで毎回の呼び出しをそこに向けます。テキスト作業には--block-imagesが役立ちます。
ゼロ依存
dependenciesは空で、WebSocketトランスポートも含みません。
NodeのグローバルWebSocket(WHATWG)はリクエストヘッダーを送信できません。これは認証付きまたはプロキシ経由のCDPエンドポイントがすべて必要とする機能であり、undiciは単体ではインポートできません。そのため、src/ws.mjsはnode:http(s)上で直接RFC 6455を実装しています。ハンドシェイク、マスキング、継続フラグメント、64ビット長、ping/pongなど、CDPが要求するすべてをカバーします。
Markdownコンバーターは明示的なスタックでDOMを走査し、ネストの深さに関係なくJSの呼び出し深度をO(1)に保ち、リーフレベルのインラインノードにはネイティブのinnerTextを使用します。これにより、深くネストされたドキュメントで再帰安全であり、大きなページでは著しく高速です。
環境
| Chrome/Chromiumバイナリへのパス |
| 起動する代わりにこのCDPエンドポイントに接続 |
要件
Node ≥ 18とChrome/Chromiumのインストールが必要です。ビルドステップはありません。
クレジット
このプロジェクトの堅牢化は、ほぼ完全に他の人々が公開した検出研究から派生しています。CREDITS.mdを参照してください。これを適切に行う方法を示してくれたrebrowser-bot-detector、bot.sannysoft.com、deviceandbrowserinfo.com、およびCamoufoxに特に感謝します。
ライセンス
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Web scraping MCP server: any URL to Markdown or JSON, with JavaScript rendering and batch jobs.
MCP server for Firecrawl — web search, scraping, and biomedical/arXiv paper search.
Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.
Related MCP Servers
- AlicenseAqualityBmaintenanceMCP server for browser automation with anti-detection. Scout pages, find elements, interact with websites, and monitor network traffic from any AI client that supports the Model Context Protocol.211MIT
- FlicenseBqualityCmaintenanceUltra-fast browser automation server over Chrome DevTools Protocol (CDP), exposed as MCP, enabling AI agents to control a real Chrome browser with low latency and minimal token usage.212-
- AlicenseAqualityBmaintenanceA fault-tolerant, stealth-enabled Model Context Protocol (MCP) server for web searching and content fetching. Built for AI Agents (Cursor, Claude Code, OpenCode), it uses a stealth browser engine to fetch pages, dynamically handles SPAs/React, and converts bloat into token-optimized Markdown.213 npm3MIT
- AlicenseAqualityAmaintenanceMCP server that drives your real Chrome/Edge/Opera browser through a Chrome extension and DevTools Protocol, preserving logins and session state, and can also perform OS-level mouse and keyboard input behind approval.5134 PyPI2MIT