agent-browser
agent-browser
Укреплённый локальный браузер для ИИ-агентов. Ноль зависимостей. Управляет уже установленным Chrome через DevTools Protocol, извлекает эффективный по токенам 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 против 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 — он измеряет вживую, а затем рисует PNG с помощью самого agent-browser, так что числа на изображениях не могут разойтись с тем, что выдаёт код.
Тратим меньше токенов на ту же страницу
URL-адреса составляют треть вывода в статье Википедии и четыре пятых на github.com/trending. Когда агент читает, а не перемещается, отбрасывайте их:
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 (HN + YC Blog прочитаны с --links text, 51,1k → 1,5k токенов, +500 на выходе) — цены по прайс-листу августа 2026: 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 раза быстрее при побайтно идентичном выводе:
страница |
| старая фиксированная задержка 250 мс |
| 7 мс | 258 мс |
| 115 мс | 340 мс |
| 160 мс | 365 мс |
| 218 мс | 466 мс |
Клиентское приложение, контент которого приходит после загрузки, нуждается в реальном сигнале, а не в более крупной догадке — старая задержка 250 мс тоже пропускала этот контент:
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.
Каждый способ предоставляет одни и те же девять инструментов из общего каталога, так что 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 againПерезапустите dsh — все девять инструментов browser_* появятся как нативные. Никакой обработчик не запускается при простом npm install; требуется осознанная install.
Вручную (если вы предпочитаете редактировать композицию самостоятельно):
npm i -g @truenix/agent-browser
# or inside the harness checkout: pnpm add @truenix/agent-browserТребуется Node ≥ 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Переопределения через env: 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 для поиска информации
Самая дорогая ошибка агента при работе с этим инструментом — рендеринг целой страницы для ответа на вопрос в одну строку. Три точечных вопроса на трёх тяжёлых страницах стоят 235 байт через eval против 77 кБ Markdown:
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 bytesИспользуйте innerText, а не textContent. textContent возвращает исходные пробелы из исходного кода ("openai /\n\n codex"); innerText даёт то, что реально отрендерено ("openai / codex"). Агент, который ошибается в этом, платит целый цикл повторной попытки, что стоит гораздо дороже сэкономленных байт.
Используйте markdown, когда вам действительно нужно читать, суммаризировать или искать по странице.
Каждый вызов запускает собственный браузер (~1,4 с). Для нескольких последовательных запросов держите один браузер живым и направьте на него --endpoint — те же три вопроса занимают 5,9 с при трёх холодных стартах и 3,2 с при тёплом.
--endpoint подключается к уже запущенному браузеру вместо запуска нового — полезно для повторного использования одного долгоживущего браузера во многих вызовах.
Память
Минимальный уровень Chrome составляет около 420 МБ PSS на 18 процессов до загрузки чего-либо, и этот уровень принадлежит Chrome, а не этому пакету — настройка флагов сдвигает его на ~5%, а флаг, который сдвигает его дальше (--enable-low-end-device-mode), сообщает navigator.deviceMemory: 2 рядом с 24 ядрами — невозможная машина и именно та несогласованность, из-за которой браузер помечают. Поэтому рычаг — меньше браузеров, а не меньше размер.
MCP-сервер держит один браузер и даёт каждому вызову инструмента собственный изолированный контекст. Три тяжёлые страницы, загруженные одновременно:
пиковый PSS | процессы | время выполнения | |
браузер на каждый вызов | 1289 МБ | 44 | 2114 мс |
один браузер, 3 контекста | 654 МБ | 20 | 2049 мс |
Изоляция не меняется — именно контекст браузера на каждый вызов всегда её обеспечивал. Браузер завершается после 30 с простоя (AGENT_BROWSER_IDLE_MS, 0 для немедленного закрытия), так что долгоживущий сервер не висит на 420 МБ между разговорами. Повторный вызов, пока он тёплый, полностью пропускает запуск и работает примерно вдвое быстрее.
Компромисс: задачи разделяют дерево процессов, поэтому сбой на уровне браузера убивает всё, что в полёте, а не один вызов. Мёртвый браузер обнаруживается и перезапускается при следующем вызове. Если вам нужна изоляция радиуса поражения для каждой задачи, используйте 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 outДля CLI каждый вызов — это отдельный процесс, поэтому повторное использование означает направление --endpoint на браузер, который вы держите живым самостоятельно.
Демон: один браузер для каждого вызова CLI
Каждый вызов CLI — это отдельный процесс, поэтому по умолчанию каждый холодно запускает свой Chrome — десять одновременных вызовов означают десять браузеров. --daemon вместо этого делит один резидентный браузер, при этом каждый вызов по-прежнему получает собственный изолированный контекст:
agent-browser markdown https://example.com --daemon
agent-browser daemon --status
agent-browser daemon --stopвремя выполнения | пиковое количество процессов Chrome | |
3 последовательных запроса, без демона | 5330 мс | — |
3 последовательных запроса, | 3254 мс | — |
10 одновременных вызовов, без демона | 1443 мс | 140 |
10 одновременных вызовов, | 1144 мс | 32 |
Это opt-in (--daemon или AGENT_BROWSER_DAEMON=1), потому что запуск фонового процесса, который переживает вашу команду, — это побочный эффект, о котором стоит спросить. Он запускается по требованию и завершается после пяти минут простоя (AGENT_BROWSER_DAEMON_IDLE_MS). Установка переменной окружения также направляет на него MCP-сервер, что стоит сделать, когда несколько MCP-клиентов используют одну машину.
--headful, --width, --height, --block-images и --gpu фиксируются при запуске браузера, поэтому общий браузер не может их соблюсти. Передача любого из них побеждает: этот вызов тихо получает собственный приватный браузер и сообщает об этом в stderr.
Четыре режима отказа, вокруг которых он построен, каждый проверен тестом:
Ровно один демон. Десять одновременных первых вызовов порождают один; девять проигрывают гонку за привязку сокета и выходят, ничего не запустив. Привязка сокета и есть блокировка, поэтому нет lock-файла, который мог бы устареть.
Открытый сокет — это счётчик ссылок. Клиент держит соединение, пока работает, поэтому клиент, убитый с помощью
SIGKILL, всё равно освобождает — ядро закрывает сокет. Сообщение «пожалуйста, освободи» утекло бы ссылку навсегда.Выход по простою, чтобы он не висел на ~420 МБ между разговорами.
Убитый через
SIGKILLдемон ничего не бросает: его маркер профиля называет его pid, поэтому следующая зачистка возвращает браузер при следующем запуске.
Уборка за собой
Каждый запуск записывает маркер владельца в свой временный профиль и вычищает профили, чей владелец умер, убивая браузер, всё ещё прикреплённый к ним. В сочетании с обработчиками SIGINT/SIGTERM/SIGHUP это даёт:
как завершается вызов | осиротевшие процессы | после следующего запуска |
обычно | 0 | 0 |
SIGTERM / SIGINT | 0 | 0 |
SIGKILL (неперехватываемый) | 1 браузер | 0 |
Таким образом, жёсткое завершение стоит не более одного зависшего браузера, а не одного на каждый прерванный вызов. Десять одновременных вызовов CLI, повторённых, не оставляют ничего. Живой браузер никогда не вычищается — его процесс-владелец всё ещё работает, и неприписанный профиль остаётся нетронутым, пока ничто не держит его открытым и он не простаивает 60 с.
Обнаружение ботов
Запустите сами: npm run test:bot. Последний результат:
detector | result |
31 пройдено, 0 провалено | |
6 зелёных, 0 красных, | |
|
Обычный headless Chrome не проходит четыре строки sannysoft (HEADCHR_UA, CHR_MEMORY, WebGL SwiftShader, old UA) и определяется как бот.
Недоступный детектор считается SKIP, а не прохождением, и для прохождения требуется три доступных успешных результата — так что день, когда тестовые сайты недоступны, не может быть ошибочно принят за успех.
Окружение важнее, чем патчинг
Один и тот же код, измеренный в двух местах:
эта рабочая станция | GitHub Actions runner | |
IP | резидентный | дата-центр |
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 и он питает классический детектор console/Error.stack.Runtime.evaluateотлично работает без него.Окно и экран двигаются вместе.
--window-sizeбез--ozone-override-screen-sizeдаётouterWidth > screen.width, что физически невозможно — более сильный сигнал, чем просто headless.Без переопределения метрик устройства по умолчанию. Обычный 1280×720 — это стандартный вьюпорт Playwright, и детекторы помечают его по имени. Устанавливайте
--viewportтолько если нужно.Реальный GPU, когда доступен, дающий настоящий рендерер
ANGLE (NVIDIA …)вместо SwiftShader.UA задаётся при запуске, а не только через CDP.
Emulation.setUserAgentOverrideне достигает Web Workers, поэтому воркер продолжает сообщать headless 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.Изолированный контекст браузера для каждой сессии, уничтожаемый при закрытии — чистое состояние для каждой задачи без второго процесса браузера.
Повторяющийся урок: согласованность важнее покрытия. Четыре из них — случаи, когда частичный спуфинг облегчал обнаружение, и это было выявлено только при запуске реальных детекторов.
Почему спуфинг WebGL отключён по умолчанию
spoofWebgl существует и реализован аккуратно — Proxy вокруг нативного getParameter, так что 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 провалено |
|
Заявление о наличии оборудования, которого у вас нет, исправляет одну косметическую строку и проваливает композитный детектор: внедрённый скрипт не достигает Web Workers, поэтому воркер по-прежнему сообщает SwiftShader, а MAX_TEXTURE_SIZE остаётся на программном значении, в то время как строка рендерера заявляет дискретный GPU.
Честный SwiftShader проходит. Убедительная ложь — нет. Вместо этого дайте браузеру реальный GPU — это бесплатно.
Чего это НЕ делает
Обнаружение на уровне отпечатков — это вся область действия. Он не побеждает и не пытается победить:
TLS/JA3-JA4 и HTTP/2 отпечатки — определяются до выполнения любого JavaScript
Репутация IP — дата-центр против резидентного ASN, часто реальный блокировщик
Поведенческий анализ — траектории мыши, тайминги, время пребывания
Коммерческие продукты для защиты от ботов опираются на это, поэтому «прохождение ворот» означает не быть тривиально помеченным как автоматизация, а не необнаруживаемым. Предназначено для ваших собственных сайтов, тестирования, работ по доступности и обычного просмотра страниц агентом.
Память
Стек Chrome стоит примерно 450 МБ. Рычаг — это архитектура, а не флаги: запускайте один браузер и много изолированных контекстов, а не один браузер на задачу. Запустите браузер один раз, затем направляйте каждый вызов на него с помощью --endpoint / AGENT_BROWSER_ENDPOINT. --block-images помогает при работе с текстом.
Ноль зависимостей
dependencies пуст, включая транспорт WebSocket.
Глобальный WebSocket Node (WHATWG) не может отправлять заголовки запросов, которые нужны любому аутентифицированному или проксированному CDP-эндпоинту, а undici не импортируется отдельно. Поэтому src/ws.mjs реализует RFC 6455 напрямую через node:http(s) — рукопожатие, маскирование, фрагменты продолжения, 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
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Headless-browser-as-JSON with memorymarket cache economics. Real Chromium, crypto settlement.
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
- FlicenseBqualityBmaintenanceUltra-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.21-
- AlicenseAqualityAmaintenanceA 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.2333MIT
- AlicenseBqualityAmaintenanceMCP 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.552MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/TrueNix/agent-browser'
If you have feedback or need assistance with the MCP directory API, please join our Discord server