browserskill MCP server
Commit Nov 9, 2026Enables AI agents to control Firefox and Chrome browsers locally via native messaging and Unix sockets without using HTTP or open ports.
Also known as ruslankolmakov/browserskill
- Stars
- 0
- Tools
- —
- Downloads, 30 days
- —
- Latest version
- —
- Last commit
- Nov 9, 2026
- License
- MIT
What is browserskill MCP?
browserskill enables AI agents to control Firefox and Chrome browsers locally via native messaging and Unix sockets without using HTTP or open ports.
Summary generated from the project's README
About browserskill MCP server
Giving AI agents direct control over your local Firefox or Chrome browsers is what browserskill enables. Instead of relying on network-based protocols, it uses a local transport layer consisting of stdio and Unix sockets to communicate between the agent and the browser extensions. This architecture ensures that no HTTP requests are made and no network ports are left open, maintaining a strictly local connection.
Developers can use this tool with agents like Claude Code or Codex by installing a skill that allows the agent to call a CLI. The system supports multiple agents working with a single browser simultaneously through a host broker. It handles large data transfers, such as DOM dumps or screenshots, by automatically chunking responses to bypass native messaging size limits. Users can target specific browsers using the -b flag or the BROWSERSKILL_BROWSER environment variable, and can even pin a specific window as a dedicated workspace for the agent to prevent interference with manual browsing.
Written from the project's README. Every use case is checked against a quote from it.
Configuration
BROWSERSKILL_BROWSER
| Name | Description | |
|---|---|---|
| BROWSERSKILL_BROWSER | — |
README
View on GitHubbrowserskill
Расширения для 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 на размер сообщения обходится прозрачно.
Установка
Одной командой (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:
about:debugging#/runtime/this-firefox- Загрузить временное дополнение… → выбрать
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:
chrome://extensions→ включить Режим разработчика- Загрузить распакованное расширение → выбрать каталог
extension-chrome - ID должен получиться
mifolcjccjkcdfdoebmkgklimogoemng— он зашит полемkeyв манифесте, и именно его разрешает манифест native-хоста - Желательно: в карточке расширения включить Разрешить пользовательские скрипты
Последний пункт нужен для 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 уснул и буферы обнулились — логи копятся заново с этого момента |
browserskill MCP questions
How does browserskill communicate with the browser?
It uses a local transport mechanism consisting of stdio and Unix sockets, ensuring no HTTP requests or open network ports are required.
Can I use browserskill with both Chrome and Firefox?
Yes, it supports both browsers. You can specify which one to use with the -b flag or the BROWSERSKILL_BROWSER environment variable.
Does browserskill support large data transfers like screenshots?
Yes, responses larger than 700 KB, such as screenshots or DOM dumps, are automatically chunked and reassembled by the host to bypass native messaging limits.
Can multiple agents control the same browser simultaneously?
Yes, because the host acts as a broker that multiple CLI calls can connect to.