ffmcp
A local MCP server for Firefox that gives AI agents full control over the browser via a Unix socket, enabling automation of tabs, pages, cookies, and more without exposing any network ports.
README
ffmcp
Расширение Firefox + MCP-сервер, дающие агентам полный доступ к твоему настоящему браузеру. Транспорт целиком локальный: stdio и Unix-сокет, ни одного HTTP-запроса и ни одного открытого порта.
Как устроено
MCP-агент ──stdio JSON-RPC──▶ ffmcp-mcp.js
│
│ Unix-сокет ~/.ffmcp/ffmcp.sock (права 0600)
▼
ffmcp-host.js ← запускается самим Firefox
│
│ native messaging (stdio, 4 байта длины + JSON)
▼
расширение ffmcp ──▶ WebExtension API
Native-хост поднимается Firefox'ом при старте расширения и работает как брокер: держит сокет, к которому подключаются MCP-серверы. Поэтому несколько агентов одновременно работают с одним браузером, а внешне не открыт ни один сетевой порт.
Ответы больше ~700 КБ (скриншоты, дампы DOM) автоматически режутся на чанки и собираются обратно в хосте — ограничение native messaging на размер сообщения обходится прозрачно.
Установка
cd ~/Projects/ffmcp
./install.sh
Скрипт кладёт манифест native-хоста в ~/Library/Application Support/Mozilla/NativeMessagingHosts/ffmcp.json
и создаёт лаунчер с абсолютным путём к node (Firefox из Finder не видит /opt/homebrew/bin).
Затем ставим расширение:
about:debugging#/runtime/this-firefox- Загрузить временное дополнение… → выбрать
extension/manifest.json - Проверить:
./bin/ffmcp.js doctor
Временное дополнение живёт до перезапуска Firefox. Чтобы поставить навсегда, нужен Firefox Developer Edition / Nightly / ESR с
xpinstall.signatures.required = falseвabout:config— тогда собери.xpi(cd extension && zip -r ../ffmcp.xpi .) и установи черезabout:addons→ Установить дополнение из файла. В обычном релизном Firefox неподписанные расширения навсегда поставить нельзя — это ограничение Mozilla.
Подключение к агентам
Claude Code:
claude mcp add firefox -- node ~/Projects/ffmcp/bin/ffmcp-mcp.js
Любой другой MCP-клиент (mcp.json, Cursor, Zed):
{
"mcpServers": {
"firefox": {
"command": "node",
"args": ["/абсолютный/путь/к/ffmcp/bin/ffmcp-mcp.js"]
}
}
}
Инструменты
35 инструментов. Основные:
| Группа | Инструменты |
|---|---|
| Рабочее окно | firefox_use_window, firefox_new_window |
| Вкладки и окна | firefox_tabs, firefox_open, firefox_navigate, firefox_close_tab, firefox_activate_tab, firefox_reload, firefox_back, firefox_forward, firefox_windows |
| Страница | firefox_snapshot, firefox_read, firefox_html, firefox_eval, firefox_click, firefox_fill, firefox_press, firefox_wait_for, firefox_screenshot |
| Отладка | firefox_console, firefox_network |
| Профиль | firefox_cookies, firefox_cookie_set, firefox_cookie_remove, firefox_history, firefox_bookmarks, firefox_downloads, firefox_storage, firefox_containers, firefox_recent_closed |
| Всё остальное | firefox_api, firefox_api_describe |
firefox_api — это и есть «полный доступ». Он вызывает любой метод WebExtension API
напрямую, так что агент не ограничен готовым списком:
{ "path": "tabs.query", "args": [{ "audible": true }] }
{ "path": "browsingData.removeCache", "args": [{}] }
{ "path": "proxy.settings.set", "args": [{ "value": { "proxyType": "none" } }] }
firefox_api_describe показывает, что вообще доступно ({"path": "cookies"} → список методов).
Рабочее окно
По умолчанию операции без явного tabId идут в активную вкладку текущего окна — то есть
туда, где сейчас работаешь ты. Чтобы агент не мешал, закрепи за ним окно:
firefox_new_window {"url": "https://example.com"} # своё окно, сразу закреплено
firefox_use_window {"windowId": 3} # закрепить существующее
firefox_use_window {} # показать текущее закрепление
firefox_use_window {"release": true} # снять
После закрепления в это окно уходят все операции без tabId, туда же открываются новые
вкладки, а firefox_tabs показывает только его вкладки (allWindows: true — все).
В firefox_windows закреплённое окно помечено target: true, а в попапе расширения видно
строкой «Рабочее окно». Закрепление переживает перезагрузку расширения и снимается само,
если окно закрыть. Из CLI — ffmcp use-window <id> и ffmcp new-window [url].
<img src="docs/popup.png" alt="popup ffmcp: состояние моста" width="300" />
Работа со страницей
firefox_snapshot возвращает интерактивные элементы с короткими uid — это дешевле
скриншота и точнее селекторов:
{ "uid": "e12", "tag": "input", "type": "email", "text": "Email address", "rect": {...} }
Далее firefox_fill {uid: "e12", value: "..."} и firefox_click {uid: "e15"}.
firefox_fill выставляет значение через нативный сеттер прототипа и шлёт input/change,
поэтому корректно работает с React, Vue и Svelte.
firefox_eval выполняет код в контексте страницы — это тело async-функции, доступны
await и return:
return [...document.querySelectorAll("h2")].map(h => h.innerText)
Логи консоли и необработанные исключения собираются автоматически на каждой загруженной
странице (буфер на 2000 записей), сетевые запросы — через webRequest. Читать через
firefox_console и firefox_network. Вывод console.* из самого firefox_eval тоже
попадает в буфер, хотя код выполняется в песочнице контент-скрипта.
CLI
Тот же мост доступен вручную, без агента:
./bin/ffmcp.js doctor # диагностика
./bin/ffmcp.js status # версия Firefox, число вкладок
./bin/ffmcp.js tabs # список вкладок
./bin/ffmcp.js open https://example.com
./bin/ffmcp.js snapshot 12 # элементы вкладки 12
./bin/ffmcp.js eval 'return document.title'
./bin/ffmcp.js api tabs.query '[{"pinned":true}]'
./bin/ffmcp.js call page.text '{"tabId":12}'
Тесты
./tests/run.sh
Живой Firefox не нужен — браузер и расширение заменяются заглушками. Проверяются: сборка JSON-RPC и все схемы инструментов, проход вызова MCP → сокет → хост → расширение, сборка чанкованных ответов, очистка сокета, отсутствие синхронных циклов при загрузке фоновой страницы и, отдельно, устойчивость перехватчика консоли к зацикливанию.
Последний тест не декоративный. В первой версии обмен со страницей шёл через
window.postMessage, а его видит и сама страница: если её код логирует полученные
сообщения (частый паттерн у виджетов и аналитики), возникала петля
console.log → postMessage → обработчик страницы → console.log. Каждая итерация уходила
в фон отдельным runtime.sendMessage, забивая главный поток родительского процесса
Firefox, и браузер переставал реагировать на ввод. Сейчас обмен идёт приватным
CustomEvent со случайным именем, стоят защита от реентрантности, лимит 100 сообщений
в секунду со страницы, батчинг раз в 250 мс и общий потолок 500 записей в секунду в фоне.
Безопасность
Это по построению очень мощный доступ: агент видит твои cookies, историю и авторизованные сессии и может действовать от твоего имени на любом сайте.
Подтверждение подключения
Каждое новое подключение MCP-клиента к сокету пользователь подтверждает вручную. Пока
не подтверждено — все вызовы (кроме health-check ping и __status) висят, а хост
показывает нотификацию Firefox. Клик по ней открывает popup расширения, где для запроса
предлагается выбор области:
<img src="docs/approval-popup.png" alt="popup ffmcp с запросом доступа" width="300" />
- это подключение — доступ только для текущего живого соединения; переподключился клиент — спросят снова;
- на эту сессию — до перезапуска Firefox (запоминается токен клиента в памяти хоста);
- навсегда — токен клиента сохраняется в
~/.ffmcp/allowed.json(0600) и переживает перезапуск.
Клиент идентифицируется стабильным токеном из ~/.ffmcp/client-token (0600); MCP-сервер
и CLI используют один и тот же токен, поэтому одобрение «навсегда» покрывает оба. Если
пользователь не реагирует за FFMCP_APPROVAL_TIMEOUT_MS (по умолчанию 60000 мс), доступ
отклоняется (fail-closed). Полностью выключить гейт — FFMCP_APPROVAL=0 (для headless/CI).
Ограничение платформы: кнопок в нотификациях Firefox нет, поэтому подтверждать нужно именно в popup (нотификация только сигналит и открывает его).
- Сокет лежит в
~/.ffmcpс правами0600— доступен только твоему пользователю, никакой сети, ни локальной, ни внешней. - Буферы
firefox_consoleиfirefox_networkпишутся постоянно, со всех вкладок, и в них оседает то, что страницы логируют сами. На практике это бывают почта, идентификаторы аккаунта, тариф и прочие персональные данные — например,claude.aiпечатает в консоль полный набор трейтов аналитики. Любой подключённый агент прочитает это одним вызовом. Чистить буфер:firefox_console {"clear": true}. allowed_extensionsв манифесте хоста ограничивает подключение расширениемffmcp@local.- Чтобы временно всё отключить — просто выгрузи расширение в
about:debuggingили закрой Firefox: сокет исчезает вместе с хостом. - Лог хоста:
~/.ffmcp/host.log.
Диагностика
| Симптом | Причина |
|---|---|
сокет не найден |
Firefox не запущен либо расширение не загружено |
расширение ffmcp не подключено к хосту |
хост поднялся, но расширение отвалилось — жми «Переподключить» в попапе |
Значок расширения показывает off |
native-хост не запускается; смотри ~/.ffmcp/host.log и проверь путь к node в лаунчере |
скрипт не вернул результат |
страница закрыта CSP или это внутренняя страница (about:, addons.mozilla.org) — там расширения работать не могут |
Вызовы висят, значок показывает ! |
ждёт твоего подтверждения — открой popup ffmcp и выбери область доступа |
доступ к Firefox не подтверждён (таймаут) |
никто не нажал подтверждение за отведённое время; повтори вызов и подтверди в popup |
| Агент переспрашивает доступ каждый раз | одобрено «на подключение»; выбери «на сессию» или «навсегда» |
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。