Skip to main content
Glama
HabaAndrei

custom-chrome-dev-mcp

by HabaAndrei

Custom Chrome Dev MCP

Un servidor MCP (Model Context Protocol) solo local que permite que un cliente MCP - Claude Code, o cualquier otra cosa que hable MCP - controle tu Chrome real de la misma manera que lo haría una persona. Sin telemetría, sin servicios de terceros, sin nube: todo se ejecuta en tu máquina detrás de un token compartido.

Expone 45 herramientas en navegación, pestañas, percepción, interacción, entrada confiable, observabilidad y captura.


De dónde viene esto

Este proyecto está inspirado en el MCP de navegador oficial de Chrome - el Chrome DevTools MCP servidor publicado por el equipo de Chrome DevTools, que fue el primero en argumentar que un agente de IA debería controlar un navegador a través del Protocolo DevTools en lugar de a través de HTML extraído.

Estamos replicando e imitando esa idea, no distribuyéndola. Lo que tomamos prestado:

  • La premisa - exponer el navegador a un agente como un conjunto de herramientas MCP.

  • Percepción basada en accesibilidad - entregar al modelo un esquema de accesibilidad compacto con referencias de elementos estables en lugar de un muro de HTML crudo.

  • El Protocolo DevTools de Chrome como capa de entrada - eventos reales y confiables en lugar de sintéticos que una página puede detectar e ignorar.

Donde este proyecto se desvía deliberadamente:

Chrome DevTools MCP

Custom Chrome Dev MCP

Navegador

Por defecto lanza su propio Chrome con un directorio de datos de usuario dedicado; también puede conectarse a una instancia en ejecución mediante --browser-url

Solo controla el Chrome que ya tienes abierto

Conexión

Se conecta al navegador a través del endpoint del Protocolo DevTools

Una extensión de Chrome que vive dentro del navegador, apuntando a la pestaña que elijas

Objetivo principal

Depurar, inspeccionar y perfilar una página

Comportarse como un humano que usa esa página

Esa última fila es el punto central de este repositorio. Chrome DevTools MCP es una herramienta de depuración que resulta que controla un navegador; esto es una herramienta de imitación-de-una-persona que resulta útil para depurar.

⚠️ No está afiliado, respaldado ni soportado por Google o el equipo de Chrome. Esta es una reimplementación independiente creada para aprender de su diseño e imitarlo. Usa el servidor oficial si quieres la versión soportada.


Related MCP server: monkeysee

Mantiene deliberadamente el estilo de un humano

La mayoría de la automatización de navegadores es trivialmente detectable: eventos sintéticos con isTrusted=false, foco que nunca se mueve realmente, texto que aparece en un campo de una sola vez, un perfil de automatización impecable sin historial. Cada uno de esos es una señal.

Este proyecto intenta eliminar esas señales:

  • Tu perfil real. Las acciones se ejecutan en el Chrome que ya usas: tus cookies, inicios de sesión, extensiones e historial. Nada que marcar como "automatización nueva".

  • Entrada confiable. realClick, realType, press, hover y drag se envían a través del Protocolo DevTools, por lo que la página recibe eventos con isTrusted=true - el mismo indicador que produce un mouse y teclado físicos.

  • Foco genuino. Hacer clic para enfocar un campo realmente mueve el foco, en orden, en lugar de asignar .value a espaldas de la página.

  • Pulsaciones de teclas reales. press emite secuencias adecuadas de rawKeyDown / char / keyUp con códigos de tecla y modificadores correctos, no un único evento sintético de input.

  • Verificación de lectura. fill confirma que el campo realmente contiene el texto, para que el agente note cuando una página rechazó silenciosamente la entrada, como lo haría una persona.

El objetivo: una página debe comportarse para el agente exactamente como se comporta para alguien sentado frente al teclado.

Las herramientas sintéticas rápidas (click, type) siguen ahí: son más rápidas y funcionan en la mayoría de los sitios. Cuando una página las ignora, recurre a las equivalentes confiables.


Cómo funciona

Un transporte. El cliente MCP habla con el servidor a través de stdio; el servidor retransmite a una extensión de Chrome a través de un WebSocket local propiedad de un proceso hub pequeño y de larga duración.

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

Por qué un proceso hub separado. Solo un proceso puede ser dueño del puerto 9876, pero puedes tener varias sesiones de Claude abiertas y todas pueden querer el navegador. Así que el socket vive en src/hub.js en lugar de dentro de cualquier sesión. Cada sesión se conecta al hub como role:"mcp", la extensión se conecta como role:"extension", y el hub multiplexa entre ellos. La primera sesión que se inicia genera el hub de forma separada, por lo que sobrevive a esa sesión; las sesiones posteriores lo encuentran ya escuchando.

Dentro de la extensión hay tres capas:

  1. Walker (page/walker.js) - se inyecta en el mundo AISLADO de la página. Es dueño de la resolución de elementos, el mapa de referencias estables eN, y las operaciones sintéticas rápidas del DOM.

  2. CDP (cdp/) - chrome.debugger para entrada confiable, evaluate en el contexto de la página, capturas de pantalla de página completa, y los buffers de consola/red.

  3. Grabación (recording/) - fotogramas de screencast de CDP codificados a .webm por un MediaRecorder en un documento fuera de pantalla.

🔒 La extensión se autentica con el hub mediante un token compartido (AUTH_TOKEN, idéntico en src/config.js y extension/src/config.js). El hub descarta cualquier par que presente un valor diferente.


Requisitos previos

Requisito

Comprobación

Node.js

18 o más reciente (desarrollado en 22)

node --version

Chrome

Google Chrome o Chromium, cualquier versión reciente

chrome://version

Un cliente MCP

Claude Code, o cualquier otra cosa que hable MCP sobre stdio

claude --version

Sin instalaciones globales, sin paso de compilación, sin servicio al que registrarse. Dos dependencias de ejecución (@modelcontextprotocol/sdk y ws) y todo permanece en 127.0.0.1.


Configuración local

Cuatro pasos, luego una verificación. Presupuesta cinco minutos.

1. Clonar e instalar

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

Confirma que el árbol está sano antes de conectar cualquier cosa a Chrome: la vía sin conexión no necesita navegador y tarda menos de un segundo:

npm test

Quieres 22 passed. Si falla, arréglalo antes de continuar; nada más adelante funcionará.

2. Cargar la extensión en Chrome

  1. Abre chrome://extensions.

  2. Activa Modo de desarrollador (interruptor en la parte superior derecha).

  3. Haz clic en Cargar descomprimida y selecciona la carpeta extension/ - la carpeta en sí, no manifest.json dentro de ella.

  4. Custom-chrome-dev-mcp aparece en la lista.

⚠️ Cárgala en el perfil de Chrome en el que realmente navegas. Chrome mantiene extensiones por perfil, por lo que una extensión cargada en "Perfil 4" es invisible para la ventana que se ejecuta bajo "Default". Si las herramientas luego informan que no hay pestañas, o el hub nunca registra extension connected, esto es lo primero que debes verificar. chrome://version muestra la Ruta del perfil activa.

El ID de la extensión está fijado por la key pública en extension/manifest.json, por lo que es idéntico en todas las máquinas: no hay nada que copiar entre configuraciones.

3. Registrar el servidor MCP con tu cliente

Usa la CLI - sustituye la ruta absoluta donde clonaste el repositorio (pwd en la raíz del proyecto la imprime):

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 lo registra para todos tus proyectos; -s local lo limita a este.

  • Registra bin/custom-chrome-dev-mcp.js - ese archivo es el punto de entrada. Apuntar a src/server.js no funcionará.

  • La ruta debe ser absoluta. Una ruta relativa se resuelve contra el directorio desde el que el cliente se lanzó.

  • Confirma con claude mcp list - quieres un ✔ Connected junto a él.

⚠️ No edites manualmente ~/.claude.json. Es grande, y una coma mal colocada rompe Claude Code por completo. El comando anterior lo edita de forma segura.

Agrega el servidor bajo mcpServers:

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

4. Reiniciar el cliente MCP

Los clientes MCP enumeran las herramientas una vez, al inicio: un servidor registrado a mitad de sesión es invisible hasta que reinicies. Reinicia Claude y aparecerán las 45 herramientas.

Al reiniciar, el cliente lanza el servidor, que genera src/hub.js si nada está ya escuchando en 127.0.0.1:9876.

5. Verificar los tres eslabones de la cadena

La pila es cliente → servidor → hub → extensión → pestaña. Verifícala de extremo a extremo en lugar de adivinar qué eslabón está caído.

# 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

Luego pide a tu cliente listTabs. Un array JSON de tus pestañas abiertas significa que cada eslabón funciona. Continúa con screenshot - un PNG aterriza en ~/Downloads y vuelve en línea.

Para la consola propia de la extensión: chrome://extensionsCustom-chrome-dev-mcpservice workerInspect. Ahí es donde aparecen los errores del lado de la extensión; nunca llegan al cliente MCP.

6. Cambiar el token compartido antes del uso real

AUTH_TOKEN viene con un valor predeterminado, definido de manera idéntica en src/config.js y extension/src/config.js. Es lo único que impide que otro proceso en tu máquina controle tu navegador con sesión iniciada. Elige tu propio valor, cámbialo en ambos archivos (una prueba sin conexión verifica que coincidan) y recarga la extensión.


Después de cambiar código

Las dos mitades se recargan de manera diferente, y equivocarse en esto pierde más tiempo que cualquier otra cosa en el proyecto:

Editaste

Para que se aplique

Cualquier cosa bajo extension/

Haz clic en recargar ↻ en la extensión en chrome://extensions. Chrome sigue ejecutando la compilación cargada anteriormente hasta que lo hagas.

Cualquier cosa bajo src/

Reinicia el cliente MCP. El proceso del servidor es de larga duración y mantiene los esquemas de herramientas antiguos.

src/hub.js

pkill -f src/hub.js - la siguiente llamada a herramienta lo vuelve a generar.


Solución de problemas

Síntoma

Causa

Solución

claude mcp list muestra ✘ Failed to connect

Ruta incorrecta, o no es el punto de entrada bin/

Vuelve a registrarlo con la ruta absoluta a bin/custom-chrome-dev-mcp.js

Faltan herramientas en el cliente por completo

Registradas a mitad de sesión

Reinicia el cliente MCP

El registro del hub nunca dice extension connected

La extensión no está cargada, está cargada en otro perfil de Chrome, o AUTH_TOKEN difiere entre los dos archivos config.js

Comprueba chrome://version → Profile Path; confirma que ambos tokens coinciden

Una llamada a herramienta se cuelga y luego expira

El service worker murió, o hubo una excepción en la extensión

Abre la consola del service worker; haz clic en reload ↻

El puerto 9876 lo ocupa un proceso inesperado

Un hub de otra copia de este proyecto está ocupando el puerto

lsof -nP -iTCP:9876 -sTCP:LISTEN, y luego mata ese PID

Una edición en extension/ "no hizo nada"

Chrome sigue ejecutando la versión antigua

Haz clic en reload ↻

URL is banlisted

BANLIST en extension/src/config.js bloquea ese host

Edita la lista: viene con entradas de ejemplo

refusing to act: … does not contain expectUrl

La protección expectUrl se activó, correctamente

Elimina la protección, o apúntala a la URL real

Ruta de captura de pantalla rechazada

Las escrituras están restringidas al directorio de captura

Usa un nombre de archivo o una ruta dentro de él

Una herramienta apunta a la pestaña equivocada

Una pestaña en segundo plano robó el foco

Fija la pestaña de trabajo con useTab


Configuración

Ambas son variables de entorno opcionales que src/config.js lee al arrancar.

Variable

Por defecto

Qué hace

CUSTOM_CHROME_DEV_MCP_CAPTURE_DIR

~/Downloads

El único directorio donde se pueden escribir capturas de pantalla y grabaciones.

CUSTOM_CHROME_DEV_MCP_WS_PORT

9876

Puerto del hub. Cámbialo también en extension/src/config.js, o no se encontrarán.

También vale la pena cambiarlo para uso real: AUTH_TOKEN, definido de forma idéntica en src/config.js y extension/src/config.js. Elige tu propio valor: es lo que impide que otro proceso local controle tu navegador.


Herramientas disponibles (45)

Los elementos se apuntan de tres formas: selector (CSS), ref (un id eN estable de snapshotA11y), o name (nombre accesible, p. ej. la etiqueta de un botón). "target" abajo significa cualquiera de esas tres.

Parámetros universales, aceptados por todas las herramientas:

  • tabId - actuar sobre una pestaña específica en lugar de la activa del entorno.

  • frameId (de listFrames) - actuar dentro de un frame específico, incluidos iframes de origen cruzado que el documento superior no puede scriptear.

  • expectUrl - una protección: rechaza la acción a menos que la URL de la pestaña contenga esta subcadena.

Fija una pestaña de trabajo para toda la sesión con useTab para que una pestaña en segundo plano (un vídeo con reproducción automática, un popup de notificación) no pueda robar el foco y desviar una acción.

Navegación

Herramienta

Args

Descripción

navigate

url

Apunta la pestaña a una URL (reemplaza la página).

newtab

url

Abre una URL en una pestaña nueva en primer plano, dejando la página actual intacta.

back / forward

-

Historial: atrás / adelante.

reload

hard?

Recarga, opcionalmente omitiendo la caché.

getUrl / getTitle

-

La URL / el título de la pestaña (funciona también en páginas internas).

waitForLoad

timeout?

Bloquea hasta que la pestaña termine de cargar.

Pestañas y frames

Herramienta

Args

Descripción

listTabs

-

Cada pestaña abierta en todas las ventanas (id, title, url, active, pinned).

activateTab

tabId

Enfoca una pestaña y su ventana.

closeTab

tabId

Cierra una pestaña por su id.

useTab

tabId?

Fija la pestaña de trabajo para que todas las herramientas posteriores la apunten independientemente del foco del SO. Omite tabId para fijar la actual.

unpinTab

-

Libera la fijación; las herramientas vuelven a la pestaña activa.

listFrames

-

Cada frame, incluidos los de origen cruzado, como {frameId, parentFrameId, url, origin}.

Percepción

Herramienta

Args

Descripción

snapshotA11y

-

Esquema de accesibilidad compacto de los elementos interactivos visibles como role "name" ref=eN. Prefiere esto sobre snapshot. Los refs expiran al navegar o al hacer un nuevo snapshot.

snapshot

-

outerHTML crudo de <body>, truncado a 50k. Úsalo cuando necesites el marcado exacto.

getText

target

innerText de un elemento, recortado.

getAttribute

target, attr

Un atributo, con respaldo en la propiedad DOM viva (value, checked, href).

queryAll

selector, limit?

text/href/value/visible de todas las coincidencias a la vez.

viewport

-

devicePixelRatio, viewport CSS, desplazamiento: cómo mapear px de captura → px CSS.

Interacción: sintética, rápida

Eventos no confiables despachados por el walker. Rápida y suficiente para la mayoría de los sitios.

Herramienta

Args

Descripción

click

target

Clic de MouseEvent con propagación; también enfoca el elemento; respaldo con .click(). Devuelve {focused}.

type

target, text

Establece el valor de un campo mediante el setter nativo (maneja <input>, <textarea> y contenteditable). Devuelve {value}.

fill

target, text, verify?

Enfocar + establecer + leer de vuelta. Lanza un error si el texto no se mantuvo. La vía fiable para escribir texto: prefierela sobre clic-y-luego-escribir.

assert

target, text?, value?

Verifica texto (subcadena) y/o valor exacto sin captura de pantalla → {ok, checks}.

scroll

target?, direction?, amount?

Desplaza un elemento hasta hacerlo visible, o la ventana (top/bottom saltan a los extremos).

select

target, value? / label?

Elige una opción de <select> por valor o por etiqueta visible.

check

target, checked

Marca una casilla o botón de radio, haciendo clic solo si no está ya marcado.

submit

target

requestSubmit() del formulario propietario: para formularios sin botón clicable.

waitForSelector

target o text, timeout?

Sondea hasta que un elemento se resuelva o aparezca una subcadena de texto.

Entrada confiable y emulación: CDP

Eventos reales con isTrusted=true. Estos adjuntan chrome.debugger, que muestra un banner amarillo persistente de "being debugged" en la pestaña.

Herramienta

Argumentos

Descripción

realClick

target / x,y, button?, clickCount?

Clic de confianza, incluidos el clic derecho y el doble clic.

realType

target?, text

Inserción de texto de confianza; si se pasa target, lo enfoca primero.

press

keys, target?

Teclas y combinaciones de confianza: "Enter", "Tab", "Meta+c", ["ArrowDown","Enter"].

hover

target / x,y

Mueve el ratón real sobre un elemento para disparar :hover (revela menús y tooltips).

drag

from, to

Arrastrar y soltar de confianza (pulsar, mover y soltar).

uploadFile

selector, paths[]

Establece archivos en un <input type=file> sin pasar por el selector del sistema. Rutas absolutas.

setViewport

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

Emula un viewport / dispositivo para comprobar el diseño responsivo.

handleDialog

accept?, promptText?

Preconfigura una respuesta para el siguiente alert/confirm/prompt. Establécala antes de la acción que dispara el diálogo.

detach

-

Desconecta el depurador y elimina el banner. Se vuelve a conectar en la siguiente llamada CDP.

Observabilidad - CDP, con búfer por pestaña

La captura comienza cuando se conecta el depurador, así que recarga la página después de la primera llamada CDP si quieres ver la actividad desde el momento de la carga.

Herramienta

Argumentos

Descripción

getConsole

level?, limit?, clear?

Logs de consola, advertencias, errores y excepciones no capturadas, con búfer.

listNetworkRequests

urlContains?, status?, failedOnly?, limit?

Solicitudes con búfer: método, url, estado, tipo, tiempo.

getNetworkRequest

requestId, includeBody?

Una solicitud completa; includeBody además obtiene el cuerpo (truncado) de la respuesta.

evaluate

expression

Ejecuta JS en el contexto real de la página mediante CDP - evita el CSP del content script que bloquea eval. Espera promesas. No disponible en páginas chrome://.

Captura

Se guarda en el directorio de captura (~/Downloads/ por defecto; consulta Configuración).

Herramienta

Argumentos

Descripción

screenshot

path?, format?, tabId?

Viewport visible como PNG/JPEG - se guarda en disco y se devuelve en línea con {devicePixelRatio, cssViewport}, para que el modelo lo vea en una sola llamada.

fullPageScreenshot

path?, tabId?

Toda la página desplazable más allá del viewport, mediante CDP.

record

action, path?, tabId?

start / stop / status para grabar la pestaña → .webm. Totalmente controlado por MCP - no necesita clic bar toolbar ni gesto del usuario. Graba la pestaña, no el escritorio.

path es un nombre de archivo o una ruta dentro del directorio de captura. Las subcarpetas que faltan se crean; cualquier cosa que resuelva fuera del directorio queda rechazada.


Una primera ejecución real

El paso 5 de la configuración prueba el conexionado. Aquí está la parte interesante: que una página vea una persona en lugar de un script. Apunta tu cliente a cualquier página y pide:

  1. snapshotA11y - el esquema compacto, con referencias eN para apuntar los blancos.

  2. realClick {ref:"e3"} - un clic de confianza. En la pestaña aparece un banner amarillo "siendo depurado"; esa es la conexión CDP, y es intencionalmente visible.

  3. evaluate {expression:"'ok'"} - JS en el contexto de la página, evitando el CSP del content script.

  4. screenshot - un PNG en tu directorio de captura y devuelto en línea.

  5. record {action:"start"}record {action:"stop", path:"clip.webm"} - un .webm de la pestaña. No hace falta clic en la barra de herramientas ni gesto del usuario; el icono de la barra es inerte por diseño y no inicia nada.

  6. detach - quita el banner.

Para ver la diferencia que hace la vía de confianza, instala un listener y compara:

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

click informa false; realClick informa true. Ese contraste es el sentido del proyecto, y la vía de pruebas de navegador lo comprueba directamente.


Ejecutar los tests

La suite tiene dos vías, y la división es la clave.

Vía sin conexión - sin navegador, se ejecuta en CI

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

Completa en poco menos de un segundo y no necesita más que Node. Ejecuta un handshake MCP real en proceso contra src/server.js (a través del transporte en memoria del SDK), por lo que verifica la superficie que el servidor realmente publica:

  • cada herramienta publicada tiene un handler en la extensión, y viceversa - el fallo que la arquitectura espejo invita a cometer

  • ningún nombre deherramienta es reclamado por dos grupos de handlers (se fusionan por spread, así que un duplicado se perdería en silencio)

  • cada herramienta lleva una descripción real y el ámbito universal tabId/frameId/expectUrl

  • cada herramienta está ejercitada por al menos un test - añade una herramienta sin test y CI falla, sin necesidades un navegador

  • la allowlist de rutas de captura rechaza de verdad .., los .. en profundidad, rutas absolutas y escapes mediante symlink, probado contra el resolver real

  • la lista de exclusión se comprueba por comportamiento: bloquea lo que dice bloquear y no sobrebloquea sitios ordinarios

  • el hub enlace solo loopback, los tokens coinciden en ambos lados, el manifest no pide permisos demasiado amplios, el icono de la barra es inerte, y no se incluye ningún *.pem

Vía de navegador - maneja Chrome real

# 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

⚠️ El paso 1 no es opcional. Un cliente MCP conectado relanza el hub cada ~1.2s siempre que ve el socket desaparecido, así que recupera el puerto 9876 al momento y la suite muere con EADDRINUSE. Matar el hub mientras hay cliente conectado no ayuda: el cliente simplemente abre otro nuevo.

La suite levanta un servidor fixture y un puente que habla el mismo protocolo de red que el hub de verdad, de modo que una ejecución que acierta ejercita el contrato del mensaje real. Cada suite refleja un grupo de herramientas, y cada test perfecciona el mismo fixture reset - no test recoge las mutaciones de otro.

Al final imprir la cobertura de herramientas y falla si alguna de las 45 herramientas quedó sin probarse.

Opciones

Comando

Efecto

npm test

solo la vía sin conexión - el control de CI

npm run test:browser

solo la vía de navegador

npm run test:all

ambas

npm run test:list

lista todas las suites y tests sin ejecutarlos

node test/run.mjs --grep=fill

solo tests cuya suite/nombre coincide con el patrón

Estructura de los tests

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

Notas de seguridad

Esta extensión puede controlar tu navegador con la sesión iniciada. Lee esta sección.

  • Solo loopback. El hub se vincula a 127.0.0.1, por lo que no es alcanzable desde la LAN: solo desde procesos de esta máquina.

  • Handshake de token. Cualquier compañero debe mostrar AUTH_TOKEN al conectar o el hub lo descarta. Cámbialo respecto al valor incluidosed (constante idéntica en src/config.js y extension/src/config.js): es lo que evita que otro proceso local maneje tu navegador.

  • Escrituras de archivos confinadas al directorio de captura. src/capture/capture-path.js resuelve cada ruta solicitada y rechaza cualquier cosa fuera de él, incluida la travesía con .. y mediante subdirectorios symlink. Esto importa más de lo que parece: escrituras incontroladas en rutas son efectivamente ejecución de código.

  • Lista de bloquear BANLIST en extension/src/config.js bloquea la navegación y scripting en sitios delicados (banca, PayPal, Gmail). Adáptala a tus necesidades. Nota: las capturas y grabaciones generan píxeles render mix y no las filtra la lista.

  • El banner de depurador es una característica. Las herramientas CDP invocan chrome.debugger, mostrando una barra amarilla persistente "siendo depurado". Esta es tu señal visible de que algo está conduciendo la pestaña. detach lo elimina.

  • evaluate ejecuta JS arbitrario en Contexto real de la página.

  • Las páginas internas no están disponibles: la extensión no puede ejecutar scripts en URLs chrome:// o chrome-extension://.

  • La clave de firma no está en el repo. El ID de la extensión queda fijado por la clave pública key en extension/manifest.json; la privada correspondiente debe estar fuera del control de versiones (.gitignore bloquea *.pem ). Solo vale para reempaquetar un .crx con el mismo ID: cargar "unpacked" no la utiliza.


Arquitectura

El servidor y la extensión son modulares. Cada grupo de herramientas en src/tools/ tiene un archivo de handlers del mismo nombre en extension/src/handlers/. Añadir una herramienta significa tocaragar exactamente ese par: su esquema y docs a un lado, su implementación al otro.

Grupo

Servidor (esquema + docs)

Extensión (implementación)

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

lo demás es infraestructura de apoyo:

  • bin/custom-chrome-dev-mcp.js - el ejecutable que registras en tu cliente MCP. No hace nada más que iniciar el servidor.

  • src/config.js / extension/src/config.js - todos los parámetros configurables, un archivo por lado. AUTH_TOKEN y el puerto deben coincidir en los dos.

  • src/relay/hub-client.js - se conecta al hub con el rol role:"mcp", lo inicia cuando no está presente y convierte cada llamada de herramienta en una solicitud/respuesta a través del socket.

  • src/hub.js - el relay de larga vida propietario de ws://127.0.0.1:9876. Mantiene el único socket de la extensión junto con el cliente de cada sesión y multiplexa entre ellos. Re-etiqueta los IDs en la comunicación (pueden colisionar entre sesiones) y se sale por sí mismo si el hub ya posee el puerto.

  • src/capture/capture-path.js - la lista blanca de escritura. Cada ruta de captura pasa por él.

  • extension/src/connection.js - el socket del hub y el latido. Un service worker de MV3 se termina tras ~30s de inactividad, lo que deja caer silenciosamente el socket; un latido de menos de 30s mantiene ambos con vida, y una alarma revive al worker tras un cierre forzado.

  • extension/src/tabs.js - qué pestaña actúa en una llamada (tabId explícito > pestaña fijada > pestaña activa), la protección de expectUrl y la comprobación de la lista negra.

  • extension/src/walker-bridge.js + extension/src/page/walker.js - el script inyectado en el mundo ISOLATED con el sistema estable de referencias a elementos, y el único módulo que sabe cómo llegar a él.

  • extension/src/cdp/ - session.js (adjuntar/separar, cdp(), centros de elementos), keyboard.js (nombres de teclas → eventos de teclado CDP), dialogs.js (política de diálogos nativos), buffers.js (búferes circulares de consola y red, con un límite de 500 por pestaña).

  • extension/src/recording/ - chrome.tabCapture necesita una interacción del usuario que una llamada MCP nunca tiene, así que la grabación usa el screencast de CDP en su lugar: fotogramas JPEG enviados a un MediaRecorder fuera de pantalla (el service worker no tiene DOM).

  • test/ - suite de dos carriles: una verificación de CI sin conexión que no necesita navegador, y una vía de navegador que controla Chrome real. Consulta Ejecución de las pruebas.


Estructura del proyecto

.
├── 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

Créditos

Inspirado por Chrome DevTools MCP del equipo de Chrome DevTools. Reimplementación independiente, no afiliada a Google, ni avalada ni respaldada por Google.


Licencia

MIT. Copyright (c) 2026 Haba Andrei.

Úsalo, hazle un fork, publícalo. La única condición es que el aviso de copyright y el aviso de permiso viajen con cualquier copia sustancial.

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