Skip to main content
Glama

browserskill

Расширения для Firefox и Chrome + CLI + скилл для Claude Code и Codex, дающие агентам полный доступ к твоим настоящим браузерам. Никакого MCP-сервера: агент вызывает CLI как обычную команду, а как ей пользоваться — описано в скилле. Транспорт целиком локальный: stdio и Unix-сокет, ни одного HTTP-запроса и ни одного открытого порта.

Как устроено

                                ┌─ Unix-сокет ~/.browserskill/firefox.sock ─▶ browserskill-host.js ─▶ расширение Firefox
агент ──скилл──▶ browserskill.js
                                └─ Unix-сокет ~/.browserskill/chrome.sock ──▶ browserskill-host.js ─▶ расширение Chrome

У каждого браузера свой native-хост и свой сокет (права 0600). Хост поднимается самим браузером при старте расширения и работает как брокер: к нему подключаются вызовы CLI. Поэтому несколько агентов одновременно работают с одним браузером, а внешне не открыт ни один сетевой порт.

Один CLI обслуживает оба браузера — какой использовать, задаёт флаг -b chrome|firefox или переменная BROWSERSKILL_BROWSER; без них берётся единственный запущенный.

Ответы больше ~700 КБ (скриншоты, дампы DOM) автоматически режутся на чанки и собираются обратно в хосте — ограничение native messaging на размер сообщения обходится прозрачно.

Related MCP server: ruyipage-mcp

Установка

Одной командой (macOS):

curl -fsSL https://raw.githubusercontent.com/ruslankolmakov/browserskill/main/get.sh | bash

get.sh клонирует репозиторий в ~/.browserskill/app (другой путь — переменная BROWSERSKILL_SRC), ставит node через Homebrew, если его нет, запускает install.sh, открывает страницы расширений в найденных браузерах и кладёт путь к расширению в буфер обмена — остаётся нажать «Загрузить распакованное». Повторный запуск обновляет установку.

Из клона вручную:

git clone git@github.com:ruslankolmakov/browserskill.git ~/Projects/browserskill
cd ~/Projects/browserskill
./install.sh

install.sh кладёт манифесты native-хоста в каталоги Firefox и всех найденных браузеров на Chromium, создаёт лаунчеры с абсолютным путём к node (браузер из Finder не видит /opt/homebrew/bin) и ставит скилл для обоих агентов:

  • Claude Code: ~/.claude/skills/browserskill (другой каталог — CLAUDE_SKILLS_DIR).

  • Codex: ~/.codex/skills/browserskill (учитывается CODEX_HOME; другой каталог — CODEX_SKILLS_DIR).

Затем ставим расширения — нужно хотя бы одно.

Firefox:

  1. about:debugging#/runtime/this-firefox

  2. Загрузить временное дополнение… → выбрать extension/manifest.json

Временное дополнение живёт до перезапуска Firefox. Чтобы поставить навсегда, нужен Firefox Developer Edition / Nightly / ESR с xpinstall.signatures.required = false в about:config — тогда собери .xpi (npm run build-xpi) и установи через about:addons → Установить дополнение из файла. В обычном релизном Firefox неподписанные расширения навсегда поставить нельзя — это ограничение Mozilla.

Chrome:

  1. chrome://extensions → включить Режим разработчика

  2. Загрузить распакованное расширение → выбрать каталог extension-chrome

  3. ID должен получиться mifolcjccjkcdfdoebmkgklimogoemng — он зашит полем key в манифесте, и именно его разрешает манифест native-хоста

  4. Желательно: в карточке расширения включить Разрешить пользовательские скрипты

Последний пункт нужен для eval на сайтах со строгим CSP — подробности ниже. Распакованное расширение переживает перезапуск Chrome, но требует включённого режима разработчика.

Проверка обоих браузеров сразу:

./bin/browserskill.js doctor
./bin/browserskill.js browsers

Подключение к агентам

Отдельной настройки нет: install.sh ставит общий skill/SKILL.md для Claude Code и Codex, подставляя абсолютный путь к репозиторию. Скилл описывает, как звать CLI, какие операции есть и на какие грабли не наступать.

В Codex скилл можно вызвать явно: $browserskill, либо выбрать через /skills. Если новый скилл не появился, перезапусти Codex. Для уже установленного browserskill достаточно повторно запустить ./install.sh.

Любому другому агенту достаточно уметь запускать команду:

node /путь/к/browserskill/bin/browserskill.js <команда>

Выбор браузера

browserskill browsers                  # кто доступен
browserskill -b chrome tabs            # разовый вызов в Chrome
BROWSERSKILL_BROWSER=chrome browserskill status

Без флага браузер выбирается сам: единственный запущенный, при двух живых — Firefox. Флаг всегда сильнее переменной окружения. Состояния «закреплённый браузер» между вызовами нет — в отличие от рабочего окна, которое живёт в расширении.

Команды и операции

Короткие подкоманды (browserskill без аргументов): doctor, browsers, status, windows, use-window, new-window, tabs, open, snapshot, read, eval, api, call.

Всё остальное — через call <op> '<json>', это ~40 операций моста:

Группа

Операции

Рабочее окно

session.newWindow, session.useWindow, session.target, windows.list/open/close

Вкладки

tabs.list, tabs.get, tabs.open, tabs.navigate, tabs.close, tabs.activate, tabs.reload, tabs.back, tabs.forward, tabs.waitLoad

Страница

page.snapshot, page.text, page.html, page.eval, page.click, page.fill, page.press, page.waitFor, page.find, page.screenshot

Отладка

logs.console, logs.network

Профиль

cookies.list/set/remove, history.search/delete, bookmarks.search/tree/create/remove, downloads.list/start, storage.get, containers.list, sessions.recent/restore

Всё остальное

browser.call, browser.describe

Полный справочник с примерами вызовов — в skill/SKILL.md.

api — это и есть «полный доступ». Он вызывает любой метод WebExtension API напрямую, так что агент не ограничен готовым списком:

browserskill api tabs.query '[{"audible":true}]'
browserskill api browsingData.removeCache '[{}]'
browserskill api proxy.settings.set '[{"value":{"proxyType":"none"}}]'

call browser.describe показывает, что вообще доступно ({"path": "cookies"} → список методов). Наборы API у Firefox и Chrome разные, так что спрашивать стоит у того браузера, в котором собираешься работать.

Рабочее окно

По умолчанию операции без явного tabId идут в активную вкладку текущего окна — то есть туда, где сейчас работаешь ты. Чтобы агент не мешал, закрепи за ним окно:

browserskill new-window https://example.com   # своё окно, сразу закреплено
browserskill use-window 3                     # закрепить существующее
browserskill call session.useWindow '{}'      # показать текущее закрепление
browserskill use-window release               # снять

После закрепления в это окно уходят все операции без tabId, туда же открываются новые вкладки, а tabs показывает только его вкладки (call tabs.list '{"allWindows":true}' — все). В windows закреплённое окно помечено target: true, а в попапе расширения видно строкой «Рабочее окно». Закрепление живёт отдельно в каждом браузере и снимается само, если окно закрыть.

Работа со страницей

snapshot возвращает интерактивные элементы с короткими uid — это дешевле скриншота и точнее селекторов:

{ "uid": "e12", "tag": "input", "type": "email", "text": "Email address", "rect": {...} }

Далее call page.fill '{"uid":"e12","value":"..."}' и call page.click '{"uid":"e15"}'. page.fill выставляет значение через нативный сеттер прототипа и шлёт input/change, поэтому корректно работает с React, Vue и Svelte.

eval выполняет код в контексте страницы — это тело async-функции, доступны await и return:

return [...document.querySelectorAll("h2")].map(h => h.innerText)

Логи консоли и необработанные исключения собираются автоматически на каждой загруженной странице (буфер на 2000 записей), сетевые запросы — через webRequest. Читать через call logs.console и call logs.network. Вывод console.* из самого eval тоже попадает в буфер.

Чем Chrome отличается от Firefox

Расширение для Chrome — Manifest V3, и часть вещей там устроена иначе. Набор операций одинаковый, отличается поведение:

Firefox

Chrome

Фон

постоянная фоновая страница

service worker, который браузер усыпляет

Произвольный JS

tabs.executeScript со строкой

мир страницы + new Function, при отказе CSP — мир пользовательских скриптов

Скриншот всей страницы

одним вызовом

прокрутка и сшивка кусков (медленнее, до 25 экранов)

Скриншот неактивной вкладки

как есть

вкладку приходится на миг активировать

Контейнеры

containers.list работает

аналога нет, вызов честно падает

Что из этого важно на практике:

  • eval и CSP. Код выполняется в мире страницы, а там действует её Content-Security-Policy. На сайтах со строгим script-src (например GitHub) eval запрещён. Обход — включить в карточке расширения «Разрешить пользовательские скрипты»: тогда browserskill выполнит код в отдельном мире, где CSP страницы не действует. Без этого на таких сайтах придёт понятная ошибка с этой же подсказкой. Операции page.click, page.fill, page.snapshot и остальные структурные операции CSP не касается вообще — они внедряются готовыми функциями, а не строками.

  • Буферы логов и сон воркера. Service worker MV3 живёт от события до события. Расширение держит его будильником и переподключается автоматически, но если Chrome всё же усыпит воркер, накопленные logs.console и logs.network обнулятся. Закрепление рабочего окна переживает это — оно лежит в storage.session.

  • Служебные страницы. chrome://*, Chrome Web Store и PDF расширениям недоступны — там любая операция со страницей вернёт ошибку. В Firefox то же самое с about:* и addons.mozilla.org.

Примеры CLI

./bin/browserskill.js doctor                      # диагностика обоих браузеров
./bin/browserskill.js browsers                    # кто сейчас на связи
./bin/browserskill.js status                      # версия браузера, число вкладок
./bin/browserskill.js tabs                        # список вкладок
./bin/browserskill.js open https://example.com
./bin/browserskill.js snapshot 12                 # элементы вкладки 12
./bin/browserskill.js eval 'return document.title'
./bin/browserskill.js api tabs.query '[{"pinned":true}]'
./bin/browserskill.js call page.text '{"tabId":12}'

Браузер выбирается флагом --browser=chrome (или -b chrome), переменной BROWSERSKILL_BROWSER, иначе автоматически:

./bin/browserskill.js -b chrome tabs
BROWSERSKILL_BROWSER=chrome ./bin/browserskill.js status

Тесты

./tests/run.sh

Живой браузер не нужен — браузеры и расширения заменяются заглушками. Проверяются: проход вызова CLI → сокет → хост → расширение, выбор браузера флагом, переменной и автоматически с маршрутизацией между двумя хостами, сборка чанкованных ответов, очистка сокета, отсутствие синхронных циклов при загрузке фона (отдельно для фоновой страницы Firefox и для service worker Chrome) и, отдельно, устойчивость обоих перехватчиков консоли к зацикливанию.

Последний тест не декоративный. В первой версии обмен со страницей шёл через window.postMessage, а его видит и сама страница: если её код логирует полученные сообщения (частый паттерн у виджетов и аналитики), возникала петля console.log → postMessage → обработчик страницы → console.log. Каждая итерация уходила в фон отдельным runtime.sendMessage, забивая главный поток родительского процесса браузера, и он переставал реагировать на ввод. Сейчас обмен идёт приватным CustomEvent, стоят защита от реентрантности, лимит 100 сообщений в секунду со страницы, батчинг раз в 250 мс и общий потолок 500 записей в секунду в фоне.

Безопасность

Это по построению очень мощный доступ: агент видит твои cookies, историю и авторизованные сессии и может действовать от твоего имени на любом сайте.

Подтверждение подключения

Каждое новое подключение клиента к сокету пользователь подтверждает вручную. Пока не подтверждено — все вызовы (кроме health-check ping и __status) висят, а хост показывает нотификацию браузера. Клик по ней открывает popup расширения, где для запроса предлагается выбор области:

  • это подключение — доступ только для текущего живого соединения; переподключился клиент — спросят снова;

  • на эту сессию — до перезапуска браузера (запоминается токен клиента в памяти хоста);

  • навсегда — токен клиента сохраняется в ~/.browserskill/allowed-<браузер>.json (0600) и переживает перезапуск.

Одобрение действует в пределах одного браузера: разрешив агенту Firefox, ты не разрешил ему заодно и Chrome — там он спросит отдельно.

Клиент идентифицируется стабильным токеном из ~/.browserskill/client-token (0600) — он общий для всех вызовов CLI с этой машины, поэтому одобрение «навсегда» даётся один раз. Если пользователь не реагирует за BROWSERSKILL_APPROVAL_TIMEOUT_MS (по умолчанию 120000 мс), доступ отклоняется (fail-closed). Полностью выключить гейт — BROWSERSKILL_APPROVAL=0 (для headless/CI).

Ограничение платформы: кнопок в нотификациях Firefox нет, поэтому подтверждать нужно именно в popup (нотификация только сигналит и открывает его).

  • Сокеты лежат в ~/.browserskill с правами 0600 — доступны только твоему пользователю, никакой сети, ни локальной, ни внешней.

  • Буферы logs.console и logs.network пишутся постоянно, со всех вкладок, и в них оседает то, что страницы логируют сами. На практике это бывают почта, идентификаторы аккаунта, тариф и прочие персональные данные — например, claude.ai печатает в консоль полный набор трейтов аналитики. Любой подключённый агент прочитает это одним вызовом. Чистить буфер: browserskill call logs.console '{"clear":true}'.

  • Манифест хоста ограничивает подключение конкретным расширением: allowed_extensions с browserskill@local у Firefox, allowed_origins с chrome-extension://mifolcjccjkcdfdoebmkgklimogoemng/ у Chrome.

  • Чтобы временно всё отключить — выгрузи расширение (about:debugging / chrome://extensions) или закрой браузер: сокет исчезает вместе с хостом.

  • Логи хостов: ~/.browserskill/firefox.log и ~/.browserskill/chrome.log.

Диагностика

Симптом

Причина

сокет не найден

браузер не запущен либо расширение не загружено; browserskill browsers покажет, кто на связи

расширение browserskill не подключено к хосту

хост поднялся, но расширение отвалилось — жми «Переподключить» в попапе

Значок расширения показывает off

native-хост не запускается; смотри ~/.browserskill/<браузер>.log и проверь путь к node в лаунчере

В Chrome ID расширения не mifolcjccjkcdfdoebmkgklimogoemng

загружен каталог без поля key в манифесте — переустанови из extension-chrome

страница запрещает выполнение кода своей CSP

включи «Разрешить пользовательские скрипты» в карточке расширения Chrome

скрипт не вернул результат

это внутренняя страница (about:, chrome://, магазин расширений) — там расширения работать не могут

Вызовы висят, значок показывает !

ждёт твоего подтверждения — открой popup browserskill и выбери область доступа

доступ к … не подтверждён (таймаут)

никто не нажал подтверждение за отведённое время; повтори вызов и подтверди в popup

Агент переспрашивает доступ каждый раз

одобрено «на подключение»; выбери «на сессию» или «навсегда»

В Chrome пропали логи консоли

service worker уснул и буферы обнулились — логи копятся заново с этого момента

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers