Skip to main content
Glama
HabaAndrei

custom-chrome-dev-mcp

by HabaAndrei

Custom Chrome Dev MCP

Локальный MCP-сервер (Model Context Protocol), позволяющий MCP-клиенту — Claude Code или чему угодно ещё, что умеет говорить через MCP, — управлять вашим настоящим Chrome так, как это сделал бы человек. Никакой телеметрии, сторонних сервисов и облака: всё работает на вашей машине за общим токеном.

Он предоставляет 45 инструментов для навигации, вкладок, восприятия, взаимодействия, доверенного ввода, наблюдаемости и захвата экрана.


Откуда это взялось

Этот проект вдохновлён официальным браузерным MCP от Chrome — сервером Chrome DevTools MCP, опубликованным командой Chrome DevTools, которая первой обосновала, что ИИ-агент должен управлять браузером через DevTools Protocol, а не через распарсенный HTML.

Мы воспроизводим и имитируем эту идею, а не поставляем её. Что мы заимствуем:

  • Суть — показываем агенту браузер как набор MCP-инструментов.

  • Восприятие через доступность — отдаём модели компактную a11y-выжимку со стабильными ссылками на элементы, а не простынёй сырого HTML.

  • Chrome DevTools Protocol как входной слой — настоящие доверенные события вместо синтетических, которые страница может обнаружить и проигнорировать.

Где этот проект намеренно расходится:

Chrome DevTools MCP

Custom Chrome Dev MCP

Браузер

По умолчанию запускает собственный Chrome с отдельным каталогом user-data-dir; может также подключаться к запущенному экземпляру через --browser-url

Всегда управляет только тем Chrome, который у вас уже открыт

Подключение

Подключается к браузеру через endpoint DevTools Protocol

Chrome-расширение, живущееnce внутри браузера, наведённое на любую выбранную вами вкладку

Основная цель

Отладка, инспекция и профилирование страницы

Поведение человека, работающего со страницей

Последняя строка — и есть весь смысл этого репозитория. Chrome DevTools MCP — это инструмент для отладки, который заодно управляет браузером; наш проект — инструмент подражания человеку, который заодно полезен для отладки.

⚠️ Не аффилирован с Google, не одобрен и не поддержан командой Chrome. Это независимая реимплементация, созданная, чтобы учиться на их дизайн и имитировать его. Если нужен официальный поддерживаемый вариант — ото пользуйтесь официальным сервером.


Related MCP server: monkeysee

Проект намеренно сохраняет стиль человека

Большинство средств автоматизации браузера легко обнаружить: синтетические события с isTrusted=false, фокус, который никогда по-настоящему не переходит, текст, появляющийся в поле целиком, и стерильный профиль автоматизации без истории. Каждый такой признак — сигнал.

Этот проект старается убрать эти сигналы:

  • Ваш реальный профиль. Действия выполются в Chrome, которым вы уже пользуетесь, — ваши cookies, логины, расширения и история. Ничто не выдаёт "новую автоматизацию".

  • Управляемый ввод. realClick, realType, press, hover и drag отправляют события через DevTools Protocol, поэтому страница получает их с isTrusted=true — тем же флагом, который порождают физические мышь и клавиатура.

  • Настоящий фокус. Клик по полю для фокусировки действительно двигает фокус, в правильном порядке, а не присваивает .value за спиной страницы.

  • Реальные нажатия клавиш. press формирует правильные последовательности rawKeyDown / char / keyUp с верными кодами клавиш и модификаторами, а не одно синтетическое событие input.

  • Проверка считыванием. fill подтверждает, что поле действительно содержит текст, и агент замечает, когда страница молча отклоняет ввод, — как это сделал бы живой человек.

Цель: страница должна вести себя с агентом, как с человеком за клавиатурой.

Быстрые синтетические инструменты (click, type) остаются на месте — они быстрее и работают на большинстве сайтов. Когда страница их игнорируulet, используйте доверенные аналоги.


Как это работает

Один транспорт. MCP-клиент общается с сервером через stdio; сервер ретранслирует сообщения в Chrome-расширение по локальному WebSocket, которым владеет отдельный долгоживущий hub-процесс.

MCP client 1 (Claude) <-stdio-> bin/custom-chrome-dev-mcp.js ─┐
MCP client 2 (Claude) <-stdio-> bin/custom-chrome-dev-mcp.js ─┼─ src/hub.js (127.0.0.1:9876)
MCP client N (Claude) <-stdio-> bin/custom-chrome-dev-mcp.js ─┘              │
                                                                            │ WebSocket
                                                                            ▼
                                                              Chrome extension -> active tab

Зачем нужен отдельный hub-процесс. Только один процесс может владеть портом 9876, но одновременно могут быть открыты несколько сессий Claude, и все они могут хотеть работать с браузером. Поэтому сокет живёт в src/hub.js, а не внутри какой-либо одной сессии. Каждая сессия подключается к hub под ролью role:"mcp", расширение — под ролью role:"extension", а hub мультиплексирует обмен между ними. Первая сессия создаёт hub отсоединённым, поэтому он переживает её; остальные сессии просто находят процесс уже слушающим порт.

Внутри расширения — трии слоя:

  1. Walker (page/walker.js) — внедряется в ISOLATED world страницы. Ведёт разрешение элементов, стабильную карту ссылок eN и быстрые синтетические DOM-операции.

  2. CDP (cdp/) — chrome.debugger для доверенных вводов, evaluate в контексте страницы, скриншоты всей страницы и буферов консоли/сети.

  3. Recording (recording/) — кадры CDP-screencast, кодируемые в .webm с помощью MediaRecorder во внеэкранном документе.

🔒 Расширение аутентифицируется на hub с помощью общего токена (AUTH_TOKEN, одинакового в src/config.js и extension/src/config.js). Hub теряет peed, который предъявит другое значение.


Предварительные требования

Требование

Проверка

Node.js

18 или новее (разработка на 22)

node --version

Chrome

Google Chrome или Chromium, любая современная версия

chrome://version

MCP-клиент

Claude Code или что-то ещё, что говорит по MCP через stdio

claude --version

Никаких глобальных установок, никакой сборки и никакой регистрации. Зависимостей всего две (@modelcontextprotocol/sdk и ws), и всё остаётся внутри 127.0.0.1.


Локальная настройка

Четыре шага и затем контрольний проход. Заложите пять минут.

1. Клонирование и установка

git clone <your-fork-url> custom-chrome-dev-mcp
cd custom-chrome-dev-mcp
npm install

Убедитесь, что с проектом всё в порядке, прежде чем подключать его к Chrome: офлайн-прогон не нуждается в браузере и занимает меньше секунды:

npm test

Вам нужно 22 passed. Если тест падает — сначала исправьте; всё, что дальше, без него работать не будет.

2. Загрузите расширение в Chrome

  1. Откройте chrome://extensions.

  2. Включите режим разработчика (переключатель справа вверху).

  3. Нажмите Load unpacked и выберите папку extension/ — именно папку, а не manifest.json внутри неё.

  4. Custom-chrome-dev-mcp появится в списке расширений.

⚠️ Загружайте его в тот профиль Chrome, в котором вы живёте. Chrome хранит расширения отдельно для каждого профицы, поэтому загруженное в «Profile 4» расширение невиден для окна под «Default». Если инструменты перестают видеть вкладки или hub не пишет extension connected — это первое, что нужно проверить. В chrome://version виден активный Profile Path.

Идентификатор расширения закреплён открытым ключом key в extension/manifest.json, поэтому он одинаков на каждой машине — ничего копировать не нужно.

3. Зарегистрируйте MCP-сервер для своего клиента

Используйте CLI и подставьте абсолютный путь к каталогу с репозиторием (pwd в корне проекта выведет его):

claude mcp add -s user custom-chrome-dev-mcp -- node /ABSOLUTE/PATH/TO/custom-chrome-dev-mcp/bin/custom-chrome-dev-mcp.js
  • -s user регистрирует сервер для всех ваших проектов; -s local — только для текущего.

  • Регистрировать нужно bin/custom-chrome-dev-mcp.js — именно этот файл есть точкой входа. Ссылаться на src/server.js не сработает.

  • Путь должен быть абсолютным. Относительный путь будет разрешён относительно каталога, и кубок которого клиент был запущен.

  • Убедитесь через claude mcp list — рядом должен появиться ✔ Connected.

⚠️ Не редактируйте ~/.claude.json вручную. Файл большой, и одна пропущенная боWwish запятая сломает Claude Code. Команда выше делает это безо пасного.

Добавьте сервер в раздел mcpServers:

{
  "mcpServers": {
    "custom-chrome-dev-mcp": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/custom-chrome-dev-mcp/bin/custom-chrome-dev-mcp.js"]
    }
  }
}

4. Перезапустите MCP-клиент

MCP-клиенты перечисляют инструменты один раз, при запуске, поэтому сервер, зарегистрированный посреди сессии, станет видимы только после перезапуска. Перезапустите Claude — появятся 45 инструментов.

При перезапуске клиент запускает сервер; если на 127.0.0.1:9876 ещё никто не слушает, сервер порождает src/hub.js.

5. Проверьте все три звена цепочки

Стек: client → server → hub → extension → tab. Проверяйте саму цепочку, а не гадайте, на каком звене обрыв.

# The hub is up, and the extension found it:
tail -f "$TMPDIR/custom-chrome-dev-mcp-hub.log"
#   [hub] listening on 127.0.0.1:9876
#   [hub] extension connected      <- this line is the handshake succeeding

# Who owns the port (should be src/hub.js from THIS repo):
lsof -nP -iTCP:9876 -sTCP:LISTEN

Затем попросите у клиента listTabs. JSON-массив открытых вкладок означает, что все звенья работают. Дополнительно выполните screenshot — PNG сохранится в ~/Downloads и вернётся обратно в ответе.

Для консоли самого расширения: chrome://extensionsCustom-chrome-dev-mcpservice workerInspect. Ошибки расширения появляются именно там; до MCP-клиента они не доходят.

6. Смените общий токен перед реальным использованием

AUTH_TOKEN поставляется со значением по умолчанию, одинаково заданным в src/config.js и extension/src/config.js. Это единственное, что мешает другому процессу на вашей машине управлять авторизованным браузером. Выберите свой собственный токен, измените его в обоих файлах (офлайн-тест проверяет их совпадение) и перезагрузите расширение.


После изменения кода

Две части перезагружаются по-разному, и из-за ошибки здесь вы теряете больше времени, чем на что-либо ещё в проекте:

Какое изменили

Как подхватить изменения

От extension/

Нажмите reload ↻ у расширения в chrome://extensions. Chrome продолжит запускатью прежнюю загруженную версию, пока вы этого не, сделавшись.

Всё под src/

Перезапустите MCP-клиент. Процесс сервера живет долго и удерживает старые схемы инструментов.

src/hub.js

Выполните pkill -f src/hub.js — следующий вызов инструмента запустит его заново.


Устранение неполадок

(Всё, что дальше, — о решении типичных проблем.)

Симптом

Причина

Исправление

claude mcp list показывает ✘ Failed to connect

Неверный путь или не та точка входа bin/

Перерегистрируйте с абсолютным путём к bin/custom-chrome-dev-mcp.js

Инструменты полностью отсутствуют в клиенте

Зарегистрированы в середине сессии

Перезапустите MCP-клиент

В логе хаба никогда не появляется extension connected

Расширение не загружено, загружено в другом профиле Chrome или AUTH_TOKEN отличается между двумя файлами config.js

Проверьте chrome://version → Profile Path; убедитесь, что оба токена совпадают

Вызов инструмента зависает, затем истекает по таймауту

Service worker завершился или произошло исключение на стороне расширения

Откройте консоль service worker; нажмите reload ↻

Порт 9876 занят неожиданным процессом

Хаб из другого клона этого проекта занимает порт

Выполните lsof -nP -iTCP:9876 -sTCP:LISTEN, затем убейте этот PID

Правка в extension/ «ничего не дала»

Chrome всё ещё запускает старую сборку

Нажмите reload ↻

URL is banlisted

BANLIST в extension/src/config.js блокирует этот хост

Отредактируйте список — он поставляется с заполнителями

refusing to act: … does not contain expectUrl

Сработала защита expectUrl, и это корректно

Уберите защиту или укажите в ней реальный URL

Путь к скриншоту отклонён

Запись ограничена каталогом захвата

Используйте имя файла или путь внутри него

Инструмент нацеливается не на ту вкладку

Фоновая вкладка перехватила фокус

Закрепите рабочую вкладку с помощью useTab


Конфигурация

Обе переменные окружения необязательны и читаются при запуске файлом src/config.js.

Переменная

По умолчанию

Что делает

CUSTOM_CHROME_DEV_MCP_CAPTURE_DIR

~/Downloads

Единственный каталог, в который можно записывать скриншоты и записи.

CUSTOM_CHROME_DEV_MCP_WS_PORT

9876

Порт хаба. Измените его также в extension/src/config.js, иначе они не найдут друг друга.

Также стоит изменить для реального использования: AUTH_TOKEN, определённый одинаково в src/config.js и extension/src/config.js. Выберите собственное значение — именно оно не даёт другому локальному процессу управлять вашим браузером.


Доступные инструменты (45)

Элементы адресуются тремя способами: selector (CSS), ref (стабильный id eN из snapshotA11y) или name (доступное имя, например, подпись кнопки). «target» ниже означает любой из этих трёх.

Универсальные параметры, принимаемые каждым инструментом:

  • tabId — действовать на конкретной вкладке вместо активной в данный момент.

  • frameId (из listFrames) — действовать внутри конкретного фрейма, включая кросс-доменные iframe, которые верхний документ не может скриптовать.

  • expectUrl — защита: отказать в действии, если URL вкладки не содержит эту подстроку.

Закрепите рабочую вкладку на всю сессию с помощью useTab, чтобы фоновая вкладка (автовоспроизводящееся видео, всплывающее уведомление) не могла перехватить фокус и направить действие не туда.

Навигация

Инструмент

Аргументы

Описание

navigate

url

Направить вкладку на URL (заменяет страницу).

newtab

url

Открыть URL в новой вкладке на переднем плане, не трогая текущую страницу.

back / forward

-

Назад / вперёд по истории.

reload

hard?

Перезагрузить, при необходимости минуя кэш.

getUrl / getTitle

-

URL / заголовок вкладки (работает и на внутренних страницах).

waitForLoad

timeout?

Блокировать выполнение, пока вкладка не завершит загрузку.

Вкладки и фреймы

Инструмент

Аргументы

Описание

listTabs

-

Все открытые вкладки во всех окнах (id, title, url, active, pinned).

activateTab

tabId

Сфокусировать вкладку и её окно.

closeTab

tabId

Закрыть вкладку по id.

useTab

tabId?

Закрепить рабочую вкладку, чтобы все последующие инструменты нацеливались на неё независимо от фокуса ОС. Опустите tabId, чтобы закрепить текущую.

unpinTab

-

Снять закрепление; инструменты снова работают с активной вкладкой.

listFrames

-

Все фреймы, включая кросс-доменные, в виде {frameId, parentFrameId, url, origin}.

Восприятие

Инструмент

Аргументы

Описание

snapshotA11y

-

Компактный accessibility-обзор видимых интерактивных элементов в виде role "name" ref=eN. Предпочитайте его snapshot. Ссылки ref истекают при навигации или повторном снимке.

snapshot

-

Сырой outerHTML элемента <body>, обрезанный до 50k. Используйте, когда нужна точная разметка.

getText

target

innerText одного элемента, обрезанный.

getAttribute

target, attr

Атрибут с запасным вариантом на живое DOM-свойство (value, checked, href).

queryAll

selector, limit?

text/href/value/visible для всех совпадений сразу.

viewport

-

devicePixelRatio, CSS-вьюпорт, смещение прокрутки — как сопоставить px скриншота с CSS-px.

Взаимодействие — синтетическое, быстрое

Недоверенные события, отправляемые обходчиком. Быстро и достаточно для большинства сайтов.

Инструмент

Аргументы

Описание

click

target

Всплывающий MouseEvent click; также фокусирует элемент; запасной вариант .click(). Возвращает {focused}.

type

target, text

Установить значение поля через нативный сеттер (работает с <input>, <textarea> и contenteditable). Возвращает {value}.

fill

target, text, verify?

Фокус + установка + чтение обратно. Выбрасывает исключение, если текст не закрепился. Надёжный путь ввода текста — предпочитайте его схеме click-затем-type.

assert

target, text?, value?

Проверить текст (подстроку) и/или точное значение без скриншота → {ok, checks}.

scroll

target?, direction?, amount?

Прокрутить элемент в зону видимости или окно (top/bottom переходят к крайним точкам).

select

target, value? / label?

Выбрать вариант <select> по значению или видимой подписи.

check

target, checked

Установить чекбокс/радиокнопку, кликая только если она ещё не в нужном состоянии.

submit

target

requestSubmit() для родительской формы — для форм без кликабельной кнопки.

waitForSelector

target или text, timeout?

Опрашивать, пока элемент не разрешится или не появится текстовая подстрока.

Доверенный ввод и эмуляция — CDP

Настоящие события с isTrusted=true. Они подключают chrome.debugger, из-за чего на вкладке появляется постоянный жёлтый баннер «being debugged».

Инструмент

Аргументы

Описание

realClick

target / x,y, button?, clickCount?

Доверенный клик, включая клик правой кнопкой и двойной клик.

realType

target?, text

Доверенный ввод текста, с фокусировкой на цели, если она указана.

press

keys, target?

Доверенные клавиши и комбинации: "Enter", "Tab", "Meta+c", ["ArrowDown","Enter"].

hover

target / x,y

Переместить реальную мышь над элементом, чтобы вызвать :hover (раскрывает меню и подсказки).

drag

from, to

Доверенное перетаскивание «нажал-переместил-отпустил».

uploadFile

selector, paths[]

Установить файлы на <input type=file>, минуя системный диалог выбора. Абсолютные пути.

setViewport

width, height, deviceScaleFactor?, mobile?, userAgent?

Эмуляция области просмотра / устройства для проверки адаптивности.

handleDialog

accept?, promptText?

Заранее задать ответ для следующего alert/confirm/prompt. Установите его до действия, вызывающего диалог.

detach

-

Отключить отладчик и убрать баннер. Повторно подключается при следующем вызове CDP.

Наблюдаемость — CDP, буферизация на вкладку

Запись начинается при подключении отладчика, поэтому перезагрузите страницу после первого вызова CDP, если хотите увидеть активность при загрузке.

Инструмент

Аргументы

Описание

getConsole

level?, limit?, clear?

Буферизованные логи консоли, предупреждения, ошибки и неперехваченные исключения.

listNetworkRequests

urlContains?, status?, failedOnly?, limit?

Буферизованные запросы: метод, url, статус, тип, время.

getNetworkRequest

requestId, includeBody?

Один запрос полностью; includeBody также загружает (усечённое) тело ответа.

evaluate

expression

Выполнить JS в реальном контексте страницы через CDP — в обход CSP content-script, который блокирует eval. Ожидает промисы. Недоступно на страницах chrome://.

Захват

Сохраняется в каталог захвата (~/Downloads по умолчанию — см. Конфигурация).

Инструмент

Аргументы

Описание

screenshot

path?, format?, tabId?

Видимая область просмотра в PNG/JPEG — сохраняется на диск и возвращается встроенно с {devicePixelRatio, cssViewport}, так что модель видит её за один вызов.

fullPageScreenshot

path?, tabId?

Вся прокручиваемая страница за пределами области просмотра, через CDP.

record

action, path?, tabId?

start / stop / status запись вкладки → .webm. Полностью управляется через MCP — не нужен клик по панели инструментов или жест пользователя. Записывается вкладка, а не рабочий стол.

path — это имя файла или путь внутри каталога захвата. Отсутствующие подпапки создаются; всё, что разрешается за пределы каталога, отклоняется.


Первый реальный запуск

Шаг 5 настройки доказывает соединение. Этот раздел доказывает самое интересное — что страница видит человека, а не скрипт. Наведите клиент на любую страницу и попросите:

  1. snapshotA11y — компактный обзор с ссылками eN для наведения.

  2. realClick {ref:"e3"} — доверенный клик. На вкладке появится жёлтый баннер «идет отладка»; это подключение CDP, и оно должно быть видимым.

  3. evaluate {expression:"'ok'"} — JS в контексте страницы, в обход CSP content-script.

  4. screenshot — PNG в вашем каталоге захвата и возвращённый встроенно.

  5. record {action:"start"}record {action:"stop", path:"clip.webm"}.webm вкладки. Не нужен клик по панели инструментов и не нужен жест пользователя; значок на панели инструментов инертен по замыслу и ничего не запускает.

  6. detach — убирает баннер.

Чтобы увидеть разницу, которую даёт доверенный путь, установите слушатель и сравните:

// via evaluate
window.__e = []; document.querySelector("button")
  .addEventListener("click", e => window.__e.push(e.isTrusted));

click сообщает false; realClick сообщает true. Этот контраст — вся суть проекта, и тестовая линия браузера проверяет именно его.


Запуск тестов

В наборе две линии, и это разделение — суть.

Офлайн-линия — без браузера, работает в CI

npm test        # node test/run.mjs --lane=offline

Завершается значительно быстрее секунды и требует только Node. Она выполняет реальное рукопожатие MCP в процессе против src/server.js (через внутрипроцессный транспорт SDK), поэтому проверяет именно ту поверхность, которую сервер реально публикует:

  • у каждого опубликованного инструмента есть обработчик расширения, и наоборот — та самая ошибка, к которой приглашает зеркальная архитектура

  • ни одно имя инструмента не заявлено двумя группами обработчиков (они объединяются через spread, поэтому дубликат молча потерялся бы)

  • каждый инструмент несёт реальное описание и универсальную область видимости tabId/frameId/expectUrl

  • каждый инструмент покрыт хотя бы одним тестом — добавьте инструмент без теста, и CI упадёт, без необходимости в браузере

  • список разрешённых путей захвата действительно отклоняет .., глубокие .., абсолютные пути и символьные побеги, проверено на реальном резолвере

  • список запретов проверяется поведенчески — он блокирует то, что заявляет, и не блокирует обычные сайты

  • хаб привязывается только к loopback, токены совпадают с обеих сторон, манифест не запрашивает излишне широкие разрешения, значок панели инструментов инертен, и ни один *.pem не закоммичен

Браузерная линия — управляет реальным Chrome

# 1. Disconnect the MCP client (close Claude Code, or disable this server for the run)
# 2. Free port 9876 - the hub is long-lived and outlives the session that spawned it
pkill -f src/hub.js
# 3. Start the suite; it binds 9876 itself and waits for the extension
npm run test:browser
# 4. Reload the extension in chrome://extensions so it connects to the suite

⚠️ Шаг 1 не обязателен. Подключённый MCP-клиент перезапускает хаб каждые ~1.2с, когда обнаруживает, что сокет исчез, поэтому он сразу же забирает порт 9876 обратно, и набор умирает с EADDRINUSE. Убийство хаба, пока клиент ещё подключён, не помогает — клиент просто запускает другой.

Набор поднимает фикстурный сервер и мост, говорящий на том же сетевом протоколе, что и реальный хаб, поэтому успешный прогон проверяет фактический контракт сообщений. Каждый набор зеркалит группу инструментов, и каждый тест начинается со сброшенной фикстурной страницы — ни один тест не наследует мутации другого.

В конце выводится покрытие инструментов, и набор падает, если какой-либо из 45 инструментов остался непроверенным.

Параметры

Команда

Эффект

npm test

только офлайн-линия — шлюз CI

npm run test:browser

только браузерная линия

npm run test:all

обе

npm run test:list

перечислить все наборы и тесты без запуска

node test/run.mjs --grep=fill

только тесты, чей набор/имя совпадает

Структура тестов

test/
├── run.mjs                    # CLI: lanes, filtering, coverage, reporting
├── lib/
│   ├── runner.js              # suite registry, isolation, timeouts
│   ├── assert.js              # assertions with diagnostic messages
│   ├── wait.js                # eventually() - polling, not fixed sleeps
│   ├── mcp-probe.js           # real in-process MCP handshake
│   ├── bridge.js              # stands in for the hub; tracks tool coverage
│   ├── fixture-server.js      # serves the fixture pages
│   └── page.js                # the browser session + per-test reset
├── fixtures/
│   ├── index.html             # the fixture page (a real file, with __reset())
│   └── frame.html             # child frame, for frameId targeting
└── suites/
    ├── 01-contract.suite.js   # offline
    ├── 02-security.suite.js   # offline
    ├── 10-navigation.suite.js
    ├── 20-tabs.suite.js
    ├── 30-perception.suite.js
    ├── 40-interaction.suite.js
    ├── 50-trusted-input.suite.js
    ├── 60-observability.suite.js
    └── 70-capture.suite.js

Замечания по безопасности

Это расширение может управлять вашим браузером, в котором вы вошли. Прочтите этот раздел.

  • Только loopback. Хаб привязывается к 127.0.0.1, поэтому он недоступен из LAN — только с процессов на этой машине.

  • Рукопожатие по токену. Пиру необходимо предъявить AUTH_TOKEN при подключении, иначе хаб его отбрасывает. Измените его с поставляемого по умолчанию (идентичная константа в src/config.js и extension/src/config.js) — именно он мешает другому локальному процессу управлять вашим браузером.

  • Запись файлов ограничена каталогом захвата. src/capture/capture-path.js разрешает каждый запрошенный путь и отклоняет всё, что за его пределами, включая обход через .. и через символьные подкаталоги. Это важнее, чем кажется: запись по произвольному пути — фактически выполнение кода.

  • Список запретов хостов. BANLIST в extension/src/config.js блокирует навигацию и скрипты на чувствительных доменах (банки, PayPal, Gmail). Настройте под свои нужды. Примечание: скриншоты и запись захватывают отрисованные пиксели и не фильтруются списком запретов.

  • Баннер отладчика — это фича. Инструменты CDP подключают chrome.debugger, показывая постоянную жёлтую полосу «идет отладка». Это ваш видимый сигнал, что кто-то управляет вкладкой. detach убирает его.

  • evaluate выполняет произвольный JS в реальном контексте страницы.

  • Внутренние страницы запрещены — расширение не может выполнять скрипты на URL chrome:// или chrome-extension://.

  • Ключ подписи не в этом репозитории. ID расширения закреплён публичным ключом key в extension/manifest.json; соответствующий приватный ключ должен оставаться вне системы контроля версий (.gitignore блокирует *.pem). Он нужен только для переупаковки .crx под тем же ID — загрузка распакованного расширения его не использует.


Архитектура

Сервер и расширение зеркальны. Каждая группа инструментов в src/tools/ имеет файл обработчика с тем же именем в extension/src/handlers/. Добавление инструмента означает изменение ровно этой пары — схема и документация на одной стороне, реализация на другой.

Группа

Сервер (схема + документация)

Расширение (реализация)

navigation

src/tools/navigation.js

extension/src/handlers/navigation.js

tabs

src/tools/tabs.js

extension/src/handlers/tabs.js

perception

src/tools/perception.js

extension/src/handlers/perception.js

interaction

src/tools/interaction.js

extension/src/handlers/interaction.js

trusted input

src/tools/trusted-input.js

extension/src/handlers/trusted-input.js

observability

src/tools/observability.js

extension/src/handlers/observability.js

capture

src/tools/capture.js

extension/src/handlers/capture.js

Всё остальное — вспомогательная инфраструктура:

  • bin/custom-chrome-dev-mcp.js - исполняемый файл, который вы регистрируете в вашем MCP- клиенте. Он ничего не делает, кроме запуска сервера.

  • src/config.js / extension/src/config.js - все настраиваемые параметры, по одному файлу на сторону. AUTH_TOKEN и порт должны совпадать в обоих.

  • src/relay/hub-client.js - подключается к хабу как role:"mcp", запускает его, когда отсутствует, и превращает каждый вызов инструмента в запрос/ответ через сокет.

  • src/hub.js - долгоживущий ретранслятор, владеющий ws://127.0.0.1:9876. Удерживает один сокет расширения и клиентов каждой сессии и мультиплексирует между ними. Пере-тегирует идентификаторы в канале (они могут конфликтовать между сессиями) и сам завершает работу, если порт уже занят другим хабом.

  • src/capture/capture-path.js - белый список для записи. Каждый путь захвата проходит через него.

  • extension/src/connection.js - сокет хаба плюс heartbeat. Сервис-воркер MV3 уничтожается после ~30 секунд простоя, что молча разрывает сокет; а heartbeat с интервалом менее 30 секунд поддерживает оба в живых, а будильник возрождает воркера после жёсткого завершения.

  • extension/src/tabs.js - на какую вкладку действует вызов (явный tabId > закреплённая вкладка > активная вкладка), защита expectUrl и проверка списка запрещённых.

  • extension/src/walker-bridge.js + extension/src/page/walker.js - внедряемый скрипт в ISOLATED-мире со стабильной системой ссылок на элементы, и единственный модуль, который знает, как до него добраться.

  • extension/src/cdp/ - session.js (присоединение/отсоединение, cdp(), центры элементов), keyboard.js (имена клавиш → события клавиш CDP), dialogs.js (политика нативных диалогов), buffers.js (кольцевые буферы консоли и сети, ограниченные 500 на вкладку).

  • extension/src/recording/ - chrome.tabCapture требует пользовательского жеста, которого у вызова MCP никогда нет, поэтому запись использует вместо этого CDP screencast: кадры JPEG передаются в закадровый MediaRecorder (у сервис-воркера нет DOM).

  • test/ - двухполосный набор: офлайн-шлюз CI, которому не нужен браузер, и браузерная полоса, управляющая реальным Chrome. См. Запуск тестов.


Структура проекта

.
├── bin/
│   └── custom-chrome-dev-mcp.js   # executable entry - register THIS with your client
├── src/
│   ├── server.js                  # composes config + relay + tool registry
│   ├── config.js                  # port, token, capture dir, timeouts
│   ├── hub.js                     # long-lived relay owning :9876
│   ├── relay/
│   │   └── hub-client.js          # session -> hub socket; call()
│   ├── capture/
│   │   └── capture-path.js        # write allowlist for screenshots/recordings
│   └── tools/                     # ONE FILE PER TOOL GROUP - the public surface
│       ├── index.js               # the registry
│       ├── schemas.js             # shared arg shapes + passthrough helper
│       ├── navigation.js
│       ├── tabs.js
│       ├── perception.js
│       ├── interaction.js
│       ├── trusted-input.js
│       ├── observability.js
│       └── capture.js
├── extension/                     # Chrome MV3 extension
│   ├── manifest.json
│   └── src/
│       ├── background.js          # service worker entry - wiring only
│       ├── config.js              # token, banlist, buffer caps, asset paths
│       ├── connection.js          # hub socket + MV3 keepalive heartbeat
│       ├── tabs.js                # tab resolution, pinning, ban check
│       ├── walker-bridge.js       # channel to the injected page script
│       ├── cdp/
│       │   ├── session.js         # attach/detach, cdp(), element centres
│       │   ├── keyboard.js        # key names -> CDP key events
│       │   ├── dialogs.js         # native alert/confirm/prompt policy
│       │   └── buffers.js         # console + network ring buffers
│       ├── recording/
│       │   ├── recorder.js        # CDP screencast -> offscreen encoder
│       │   ├── offscreen.html
│       │   └── offscreen.js       # MediaRecorder host
│       ├── page/
│       │   └── walker.js          # injected DOM driver (ISOLATED world)
│       └── handlers/              # MIRRORS src/tools/ - one file per group
│           ├── index.js           # the handler table + dispatch
│           ├── navigation.js
│           ├── tabs.js
│           ├── perception.js
│           ├── interaction.js
│           ├── trusted-input.js
│           ├── observability.js
│           └── capture.js
└── test/                          # two lanes: offline (CI) + browser
    ├── run.mjs                    # CLI entry
    ├── lib/                       # runner, assertions, bridge, fixtures, session
    ├── fixtures/                  # the fixture pages, as real files
    └── suites/                    # one suite per tool group

Благодарности

Вдохновлено Chrome DevTools MCP от команды Chrome DevTools. Независимая реализация, не связана с Google, не одобрена и не поддерживается ею.


Лицензия

MIT. Авторское право (c) 2026 Haba Andrei.

Используйте, форкайте, выпускайте. Единственное условие — уведомление об авторском праве и уведомление о разрешении должны сопровождать любую существенную копию.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP clients to drive a real, logged-in Chrome browser for web automation tasks like navigation, clicking, typing, and screenshotting.
    1
    1
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Drive your real, signed-in Chrome browser from any MCP client, enabling browser automation such as navigation, clicking, typing, and screenshots through standard MCP tools.
    1
  • A
    license
    C
    quality
    A
    maintenance
    MCP server for browser automation that drives Chrome via an extension, preserving login state and offering 45 tools for navigation, interaction, scraping, and screenshots.
    53
    4
    MIT

View all related MCP servers

Related MCP Connectors

  • Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.

  • Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.

  • Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.

View all MCP Connectors

Latest Blog Posts

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/HabaAndrei/custom-chrome-dev-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server