Files
browser_mcp/app.md
T
egutierrez a48e262371 docs: app.md v0.8.0 — browser_set_mode + aceleraciones CDP
Documenta los cambios de la v0.8.0 en el app.md del browser_mcp:

- Bump de versión 0.7.0 -> 0.8.0 y descripción (45 -> 46 tools, mención del modo de velocidad de sesión).
- Sección Tools (46): añade browser_set_mode en el grupo Sesión.
- Capability growth log: entrada v0.8.0 detallando el flag de velocidad, el settle adaptativo, la escritura insertText en auto, el poll del puerto en launch_profile, los enable cacheados, wait_load por evento, el timeout de sendCDP y las nuevas CdpInsertText/CdpTypeRefFast, con los números del smoke contra Chrome 9333.
2026-06-13 14:28:03 +02:00

22 KiB

name, lang, domain, version, description, tags, e2e_checks, uses_functions, uses_types, framework, entry_point, dir_path, repo_url
name lang domain version description tags e2e_checks uses_functions uses_types framework entry_point dir_path repo_url
browser_mcp go infra 0.8.0 Servidor MCP que expone control total del navegador via CDP (46 tools: navegación, DOM, cookies, iframes, teclado/scroll, diálogos, estado de sesión, selección determinista de pestaña, modo de velocidad de sesión (browser_set_mode: 'auto' rápido por defecto / 'human' sigiloso anti-detección), lectura compacta texto/AX nativa + bucle percibir→actuar por #ref con auto-observe, percepción y lectura de texto dentro de iframes, click por coordenadas, screenshot devuelto como image content que el LLM ve, y gestión del ciclo de vida de Chromium por perfil: listar masters en ejecución, lanzar un perfil concreto con o sin CDP, y cerrar limpio) reusando funciones del dominio browser del registry con un pool de conexiones CDP vivas. Por defecto opera sobre un Chrome aislado (puerto 9333) separado del navegador diario.
mcp
browser
cdp
automation
scraping
id cmd timeout_s
build cd projects/web_scraping/apps/browser_mcp && go build -o browser_mcp . 120
id cmd timeout_s
unit cd projects/web_scraping/apps/browser_mcp && go test -count=1 ./... 120
id cmd timeout_s severity
leak_no_orphans cd projects/web_scraping/apps/browser_mcp && go test -c -o /tmp/bmcp_e2e.test . && systemd-run --user --quiet --collect --unit=bmcp_e2e_ci --wait -p Type=oneshot --setenv=BMCP_E2E=1 -p StandardOutput=journal /tmp/bmcp_e2e.test -test.run TestE2E -test.v 180 warning
chrome_launch_go_browser
cdp_connect_go_browser
cdp_close_go_browser
cdp_navigate_go_browser
cdp_list_tabs_go_browser
cdp_new_tab_go_browser
cdp_close_tab_go_browser
cdp_activate_tab_go_browser
cdp_nav_back_go_browser
cdp_nav_forward_go_browser
cdp_wait_load_go_browser
cdp_wait_idle_go_browser
cdp_get_html_go_browser
cdp_evaluate_go_browser
cdp_screenshot_go_browser
cdp_click_go_browser
cdp_click_human_go_browser
cdp_click_text_go_browser
cdp_type_text_go_browser
cdp_find_by_text_go_browser
cdp_find_ref_by_text_go_browser
cdp_wait_element_go_browser
cdp_press_key_go_browser
cdp_scroll_go_browser
cdp_handle_dialog_go_browser
cdp_set_cookie_go_browser
cdp_get_cookies_go_browser
cdp_delete_cookies_go_browser
cdp_clear_cookies_go_browser
cdp_list_frames_go_browser
cdp_eval_in_frame_go_browser
cdp_get_frame_html_go_browser
cdp_save_storage_state_go_browser
cdp_load_storage_state_go_browser
cdp_get_text_go_browser
cdp_get_text_in_frame_go_browser
cdp_connect_target_go_browser
cdp_get_ax_outline_go_browser
cdp_screenshot_bytes_go_browser
cdp_click_ref_go_browser
cdp_type_ref_go_browser
cdp_hover_ref_go_browser
cdp_click_xy_human_go_browser
main.go projects/web_scraping/apps/browser_mcp

browser_mcp

Servidor MCP (Model Context Protocol) en Go que expone el control de navegador via CDP del registry fn_registry como tools MCP. Cualquier cliente MCP (Claude Code, otros agentes) puede manejar un Chrome/Chromium vivo: navegar, leer el DOM, hacer clicks, gestionar cookies, evaluar JavaScript, operar iframes y persistir/restaurar sesiones.

Clona el patrón de apps/registry_mcp/ (librería github.com/mark3labs/mcp-go v0.52.0, server.NewMCPServer + server.ServeStdio, tools con mcp.NewTool + handlers tipados via mcp.NewTypedToolHandler, transporte stdio por defecto + HTTP opcional con --http, slog a stderr porque stdout pertenece al JSON-RPC).

Arquitectura: pool de conexiones CDP

A diferencia de registry_mcp (que abre la DB una vez), browser_mcp mantiene un pool de conexiones CDP vivas indexado por puerto (pool.go). Razón: browser.CdpConnect(port) hace un handshake WebSocket contra una tab "page" de Chrome (~50-200ms) y esa conexión ES una sesión viva (soporta Page.*, Runtime.*, Input.*). El agente llama muchas tools seguidas (navigate → wait → click → eval); reconectar en cada tool pagaría el handshake repetidamente y perdería estado entre tools (los event handlers persistentes, como el de handle_dialog, viven mientras la conexión esté viva). Por eso reusamos la conexión por puerto.

  • connPool.get(port) devuelve la conexión cacheada o abre una nueva.
  • connPool.drop(port) cancela el handler de diálogo (si lo hay) y cierra la conexión.
  • connPool.connectTarget(port, match) descarta la conexión actual y reconecta a un target determinista (por id o substring de URL). Es lo que usa tab_select para fijar la pestaña.
  • connPool.setCancel(port, cancel) registra el cancel del auto-handler de handle_dialog.
  • connPool.closeAll() se ejecuta con defer en main().
  • deps.withConn(port, fn) ejecuta fn con la conexión del pool y, si el error indica conexión muerta (isConnErr: connection close, broken pipe, use of closed, ws read, EOF), descarta la conexión y reintenta UNA vez (Chrome pudo cerrar la tab entre tools).

Toda tool con argumento port usa portOr(a.Port) (default 9333). Las tools de tabs (tab_list, tab_new, tab_close, tab_activate, tab_select) usan el endpoint HTTP /json de CDP directamente (host localhost), no el pool, porque no requieren una sesión WebSocket viva.

Seguridad: Chrome aislado por defecto (puerto 9333)

El default del MCP es operar sobre su PROPIO Chrome aislado, no sobre el navegador diario.

En este ecosistema el chromium diario del usuario tiene CDP habilitado globalmente en el puerto 9222 (via /etc/chromium.d/cdp). Si el MCP usara 9222 por defecto, el agente podría manipular pestañas ajenas del usuario (banca, correo). Para evitarlo:

  • portOr devuelve 9333 por defecto (no 9222) — el Chrome dedicado del MCP.
  • browser_launch sin user_data_dir usa un perfil DEDICADO y aislado: <tmp>/browser_mcp_userdata (se crea si hace falta) en el puerto 9333.
  • Para adjuntarte deliberadamente al navegador diario, pasa port: 9222 explícito en cada tool. Hazlo solo con cuidado.

Tools (46)

Sesión (tools_session.go)

  • browser_launch (MUTA) — lanza Chrome con CDP. args: port, headless, user_data_dir, url.
  • browser_connect — abre/poolea la conexión CDP del puerto. args: port.
  • browser_disconnect — cierra y descarta la conexión del puerto (no mata Chrome). args: port.
  • browser_set_mode — fija el modo de velocidad de sesión del puerto: auto (default, rápido) o human (sigiloso anti-detección). args: port, mode. Cada tool de acción puede overridearlo con su arg mode.

Ciclo de vida por perfil (tools_lifecycle.go)

Gestionan los Chromium del USUARIO por perfil (Personal, Work, ...), distintos del Chrome de automatización aislado de browser_launch. Las instancias lanzadas aquí NO se registran en el pool: son de uso humano y sobreviven a la muerte del MCP; se cierran explícitamente con browser_close.

  • browser_list — lista los procesos MASTER de Chromium en ejecución (con --user-data-dir, SIN --type=). Para cada uno: pid, profile, user_data_dir, cdp_port, has_cdp. Devuelve JSON array. Read-only. args: (ninguno).
  • browser_launch_profile (MUTA) — lanza Chromium para un perfil concreto en la pantalla del usuario, usando el binario REAL /usr/lib/chromium/chromium (salta el wrapper). Con cdp=false (default) NO añade flags de remote-debugging — necesario para perfiles humanos (Google mantiene la sesión; con CDP la trata como automatizada y la tira). Con cdp=true añade --remote-debugging-port + --remote-allow-origins=*. Detecta DISPLAY/XAUTHORITY de la sesión XFCE y lanza DESACOPLADO (setsid). Si un master ya posee el user_data_dir, Chromium reenvía la apertura a él (note en el resultado). args: profile (requerido), user_data_dir (default ~/.config/chromium-cdp), url, cdp (default false), cdp_port (default 9222).
  • browser_close (MUTA) — cierra un master limpio. Lo localiza por profile, cdp_port o pid. Envía SIGTERM, espera hasta 10s, y SIGKILL como último recurso (indicado en method). Devuelve {closed, pid, method}. args: uno de profile, cdp_port o pid.

Navegación + tabs (tools_nav.go)

  • tab_navigate (MUTA) — Page.navigate. args: port, url.
  • tab_list — lista targets via GET /json. args: port.
  • tab_new (MUTA) — abre tab via PUT /json/new. args: port, url.
  • tab_close (MUTA) — cierra tab por ID. args: port, tab_id.
  • tab_activate — pone tab en foreground. args: port, tab_id.
  • tab_select — fija la pestaña sobre la que operan las siguientes tools, eligiéndola por id o por substring de su URL (determinista). Usar tras tab_list para no operar sobre la pestaña equivocada. args: port, match.
  • nav_back (MUTA) — atrás en el historial. args: port.
  • nav_forward (MUTA) — adelante en el historial. args: port.
  • page_wait_load — espera el evento load. args: port, timeout_ms (default 10000).
  • page_wait_idle — espera red idle. args: port, timeout_ms (default 15000).

Lectura (tools_read.go)

  • page_get_html — HTML serializado (truncado a 200000 chars). args: port.
  • page_get_text — texto visible (innerText) de la página o de un elemento (selector CSS), truncado a max_bytes. Preferir sobre page_get_html cuando solo necesitas leer contenido — no revienta el contexto. args: port, selector (opcional), max_bytes (default 20000).
  • page_perceive — outline indentado y accionable del árbol de accesibilidad (roles, nombres, #ref): la forma compacta de que el agente "perciba" la página sin reventar el contexto. Nativo en Go sobre la conexión CDP viva del pool (cdp_get_ax_outline_go_browser) — ya no lanza subprocess fn run ni levanta el venv de Python. Para elegir la pestaña usa tab_select ANTES (la conexión del pool ya está fijada a esa pestaña); el campo tab_id queda obsoleto y se ignora (se conserva por compatibilidad). Si se pasa frame_id, percibe DENTRO de ese iframe (obtén el id con frame_list). args: port, tab_id (obsoleto), frame_id (opcional), max_chars (default 20000).
  • page_eval_js (MUTA) — Runtime.evaluate. args: port, expression.
  • page_screenshot — captura la página y la devuelve como image content para que el LLM vea los píxeles (vía cdp_screenshot_bytes_go_browser, sin tocar disco). Si se pasa path, además guarda la imagen en ese archivo; el image content se devuelve siempre. Útil cuando el outline de page_perceive no basta (canvas, mapas, layouts visuales): mira la captura y actúa con dom_click_xy. args: port, path (opcional), full_page.

DOM (tools_dom.go)

  • dom_click (MUTA) — click por selector. args: port, selector.
  • dom_click_human (MUTA) — click con movimiento humano. args: port, selector.
  • dom_click_text (MUTA) — click sobre el primer elemento con ese texto. args: port, text.
  • dom_type (MUTA) — escribe texto en el elemento enfocado. args: port, text.
  • dom_find_by_text — devuelve un selector CSS único para un texto visible. args: port, text.
  • dom_wait_element — espera a que aparezca un selector. args: port, selector, timeout_ms (default 10000).
  • dom_click_ref (MUTA) — click humanizado por #ref (backendDOMNodeId del outline de page_perceive) + auto-observe. args: port, ref, mode.
  • dom_type_ref (MUTA) — enfoca el #ref y escribe texto + auto-observe. args: port, ref, text.
  • dom_hover_ref (MUTA) — hover humanizado por #ref + auto-observe. args: port, ref, mode.
  • dom_click_xy (MUTA) — fallback de click por coordenadas absolutas (x, y en CSS pixels del viewport) con movimiento humanizado por defecto. Pensado para usarse sobre lo que el agente VE en page_screenshot cuando el outline no basta (canvas, mapas, layouts visuales); prefiere dom_click_ref cuando el elemento aparece en el outline. Devuelve el outline actualizado (auto-observe). args: port, x, y, mode.

Bucle percibir→actuar (por #ref)

page_perceive devuelve un outline accionable (generado de forma nativa en Go sobre la conexión CDP viva) donde cada elemento lleva un #ref estable (su backendDOMNodeId). Las tools dom_click_ref / dom_type_ref / dom_hover_ref actúan directamente sobre ese #ref — no necesitas resolver un selector CSS. Tras la acción esperan un settle breve (400ms) y devuelven el outline actualizado (auto-observe, truncado a 8000 chars), cerrando el bucle percibir→actuar:

page_perceive   →  outline con #ref de cada elemento
dom_click_ref   →  click humanizado + outline nuevo tras la acción
dom_type_ref    →  escribe + outline nuevo

Cuando el elemento no aparece en el outline (canvas, mapas, layouts puramente visuales), el fallback es mirar con page_screenshot (que devuelve la imagen al LLM) y actuar por coordenadas con dom_click_xy, que también devuelve el outline tras el click.

Las tools *_ref y dom_click_xy aceptan mode (human por defecto con Bézier+jitter anti-bot, fast para scraping masivo, instant sin movimiento de ratón). La humanización es el default en todas para no facilitar la detección.

Input (tools_input.go) — todas MUTA

  • press_key — presiona una tecla nombrada (Enter/Tab/Escape/ArrowDown/...). args: port, key.
  • scroll — scroll por (delta_x, delta_y). args: port, delta_x (default 0), delta_y (default 300).
  • handle_dialog — arma un auto-handler de diálogos JS (vive en la conexión del pool). args: port, accept (default true), prompt_text.

Cookies (tools_cookies.go)

  • cookie_get — todas las cookies como JSON. args: port.
  • cookie_set (MUTA) — set cookie. args: port, name, value, domain, path, http_only.
  • cookie_delete (MUTA) — borra cookies por nombre. args: port, name, domain.
  • cookie_clear (MUTA) — borra todas las cookies. args: port.

Iframes (tools_frames.go)

  • frame_list — lista frames con sus IDs. args: port.
  • frame_eval (MUTA) — evalúa JS dentro de un frame. args: port, frame_id, expression.
  • frame_get_html — HTML de un frame (truncado a 200000). args: port, frame_id.
  • frame_get_text — texto visible (innerText) de un iframe, truncado a max_bytes. Para leer contenido atrapado dentro de un iframe — page_get_text solo cubre el documento de nivel superior. args: port, frame_id, max_bytes (default 20000).

Estado de sesión (tools_storage.go)

  • storage_save — guarda cookies + localStorage a JSON. args: port, path.
  • storage_load (MUTA) — carga cookies + localStorage desde JSON. args: port, path.

Cómo lanzarlo

Transporte stdio (default, para clientes MCP):

cd projects/web_scraping/apps/browser_mcp
go build -o browser_mcp .
./browser_mcp

Transporte HTTP (Streamable HTTP):

./browser_mcp --http :7740                 # bind 127.0.0.1:7740
./browser_mcp --http :7740 --bind 0.0.0.0  # requiere REGISTRY_API_TOKEN (bearer auth)

Flag --read-only

Con --read-only, el servidor NO registra las tools mutantes (marcadas MUTA arriba): solo expone las 20 tools de lectura/control (browser_connect, browser_disconnect, browser_list, tab_list, tab_activate, tab_select, page_wait_load, page_wait_idle, page_get_html, page_get_text, page_perceive, page_screenshot, dom_find_by_text, dom_find_ref_by_text, dom_wait_element, cookie_get, frame_list, frame_get_html, frame_get_text, storage_save). Útil para sesiones de inspección sin riesgo de modificar el estado del navegador.

Omitido en v1

Funciones del dominio browser que NO se exponen como tools en esta versión, con su razón:

  • cdp_har_record_go_browser — graba el tráfico de red (HAR). Requiere un callback de larga duración (registrar handlers + un punto de "stop" que devuelve los datos acumulados); no encaja en el modelo request/response de una tool MCP simple. Pendiente de un diseño con tool de start + tool de stop.
  • cdp_get_ax_tree — expuesto via la tool page_perceive. Desde v0.6.0 el outline se genera de forma nativa en Go (cdp_get_ax_outline_go_browser) sobre la conexión CDP viva del pool; ya no se invoca el pipeline Python cdp_perceive_outline por subprocess (fn run). El acceso al árbol AX en bruto sigue sin exponerse: la tool devuelve directamente el outline accionable.
  • Funciones de perfiles Chrome (Bash: create/delete/appearance/reset) — requieren que Chrome esté CERRADO para modificar el Local State / Preferences del perfil; son incompatibles con un MCP cuyo propósito es controlar un Chrome vivo. Quedan disponibles como fn run aparte.

Capability growth log

  • v0.8.0 (2026-06-13) — Aceleración del manejo del navegador via CDP + flag de velocidad de sesión. (1) Nueva tool browser_set_mode (45 → 46 tools): fija el modo de velocidad por puerto en el pool — auto (default del MCP, rápido) vs human (sigiloso anti-detección). El modo se resuelve por acción con effectiveMode: arg mode de la tool > modo de sesión > auto. (2) Settle adaptativo: el sleep ciego fijo de 400ms tras cada acción mutante (dom_click_ref/dom_type_ref/ dom_hover_ref/dom_click_xy) pasa a settleForMode — 60ms en auto, aleatorio 250-650ms en human (ritmo no-máquina), 0 en instant. (3) dom_type_ref ahora tiene arg mode: en auto usa CdpTypeRefFast (Input.insertText, un solo round-trip) y en human teclea carácter a carácter (CdpTypeRef) con pausas aleatorias. (4) browser_launch_profile reemplaza el sleep(1s) ciego por un poll del puerto CDP (waitCDPPort). Cambios en el dominio browser del registry que aprovecha el MCP: Accessibility.enable/Network.enable/Page.enable cacheados por conexión (ensureAX/ensureNetwork/ensurePage en CDPConn) — se eliminan round-trips redundantes en cada percepción/espera; cdp_wait_load pasa de polling de document.readyState cada 200ms a esperar el evento Page.loadEventFired (fast path si ya está complete); sendCDP adquiere timeout (cdpCmdTimeout 30s) para no colgar el tool indefinidamente; nuevas CdpInsertText y CdpTypeRefFast (camino rápido de escritura); el modo auto se añade al perfil de ratón (MouseProfileForMode) como alias rápido de fast. Smoke contra Chrome 9333: percepción #2 con enable cacheado 1.7ms (vs 3.7ms la #1), wait_load fast-path 245µs (vs ≥200ms del polling previo).
  • v0.7.0 (2026-06-10) — Ciclo de vida de Chromium por perfil (tools_lifecycle.go). Tres tools nuevas: browser_list (enumera los procesos master de Chromium leyendo /proc/*/cmdline, filtrando por --user-data-dir presente y --type= ausente), browser_launch_profile (lanza un perfil concreto con el binario REAL /usr/lib/chromium/chromium para saltar el wrapper, con/sin CDP — sin CDP por defecto para que Google mantenga la sesión de perfiles humanos; detecta DISPLAY/XAUTHORITY de la sesión XFCE y lanza desacoplado con setsid) y browser_close (localiza el master por profile/cdp_port/pid, SIGTERM con espera de 10s, SIGKILL como último recurso). Las instancias por perfil NO se registran en el pool: son de uso humano y sobreviven a la muerte del MCP. 42 → 45 tools.
  • v0.6.0 (2026-06-06) — Percepción visual y de iframes + perceive nativo. (1) page_perceive se generó hasta ahora por subprocess fn run cdp_perceive_outline (Python); ahora es nativo en Go sobre la conexión CDP viva del pool (cdp_get_ax_outline_go_browser) — mata el subprocess, el venv y la dependencia del binario fn en runtime (se eliminó resolveRoot/exec.Command). (2) Acceso a datos dentro de iframes: nueva tool frame_get_text (innerText de un iframe, cdp_get_text_in_frame_go_browser) y nuevo parámetro frame_id en page_perceive para percibir DENTRO de un iframe. (3) Click por coordenadas absolutas: nueva tool dom_click_xy (cdp_click_xy_human_go_browser), humanizada por defecto, pensada para actuar sobre lo que el LLM ve en una captura. (4) page_screenshot ahora devuelve la imagen como image content (vía cdp_screenshot_bytes_go_browser + mcp.NewToolResultImage) para que el LLM vea los píxeles; path pasa a ser opcional (si se da, además guarda a disco). (5) El auto-observe de las tools *_ref subió su truncado de 4000 a 8000 chars (outlines grandes se cortaban). (6) Fix de seguridad documental: todas las descripciones del parámetro port que decían "Default 9222" (el navegador diario del usuario) corregidas a "Default 9333" (Chrome aislado del MCP); el código ya usaba 9333, la doc era falsa y podía inducir al modelo a tocar pestañas de banca/correo. 40 → 42 tools.
  • v0.5.0 (2026-06-06) — Fix del leak de RAM (chromium huérfanos, apagón 06/06/2026). El pool ahora registra el PID del Chrome que lanzó por puerto (pids map + setPID/getPID/clearPID/ launchedCount). browser_disconnect (drop) y el shutdown (closeAll) matan el grupo de proceso completo SOLO si el PID está registrado (lo lanzó el MCP) — un Chrome externo (navegador diario en 9222) nunca se mata, solo se cierra el WebSocket. browser_launch es idempotente por puerto, reusa un Chrome ya vivo (ChromeLaunch.ReuseExisting, pid 0 = no relanza) y aplica un tope duro de 4 instancias. Handler SIGTERM/SIGINT en main.go llama closeAll (los defers no corren con señal). withConn retry usa releaseConn (suelta solo el WS) en vez de drop. Tests: pool_test.go (lógicos) + pool_e2e_test.go (Chrome real, gate BMCP_E2E=1). e2e_checks añadidos.
  • v0.3.0 (2026-06-06) — Cierre del bucle percibir→actuar. Nuevas tools dom_click_ref, dom_type_ref, dom_hover_ref: actúan sobre el #ref (backendDOMNodeId estable) del outline de page_perceive con humanización por defecto (Bézier+jitter) y auto-observe (devuelven el outline actualizado tras la acción). Refactor: la generación del outline se extrajo a deps.perceiveOutline/perceiveOutlineTab, reusado por page_perceive y por las tools *_ref. 36 → 39 tools.
  • v0.2.0 (2026-06-06) — P0 LLM-readiness. Seguridad: Chrome aislado por defecto (puerto 9333
    • perfil dedicado <tmp>/browser_mcp_userdata), separado del navegador diario en 9222. Nuevas tools: tab_select (selección determinista de pestaña por id/URL), page_get_text (lectura compacta de innerText), page_perceive (outline AX via fn run cdp_perceive_outline). 33 → 36 tools.