feat(browser_mcp): perceive nativo Go, datos de iframe, click XY y screenshot como imagen (v0.6.0)

Capacidades nuevas y cambios (40 -> 42 tools):

- page_perceive ahora se genera de forma NATIVA en Go sobre la conexion CDP
  viva del pool (cdp_get_ax_outline_go_browser). Elimina el subprocess
  `fn run cdp_perceive_outline` (Python), el venv y la dependencia del binario
  `fn` en runtime (se borra resolveRoot/exec.Command). Respeta tab_select.
- page_perceive acepta frame_id para percibir DENTRO de un iframe. El campo
  tab_id queda obsoleto (se ignora; usar tab_select) pero se conserva por
  compatibilidad.
- frame_get_text (nueva, lectura): innerText de un iframe via
  cdp_get_text_in_frame_go_browser. Activa tambien bajo --read-only.
- dom_click_xy (nueva, MUTA): click humanizado por coordenadas absolutas via
  cdp_click_xy_human_go_browser, con mode human/fast/instant y auto-observe.
  Fallback para actuar sobre lo que el LLM ve en page_screenshot.
- page_screenshot devuelve la imagen como image content
  (cdp_screenshot_bytes_go_browser + mcp.NewToolResultImage) para que el LLM
  vea los pixeles; path pasa a ser opcional (si se da, ademas guarda a disco).
- Auto-observe de las tools *_ref sube su truncado de 4000 a 8000 chars.
- Fix de seguridad documental: todas las descripciones del parametro port que
  decian "Default 9222" (navegador diario del usuario) corregidas a
  "Default 9333" (Chrome aislado del MCP). El codigo ya usaba 9333; la doc era
  falsa y podia inducir al modelo a tocar pestanas de banca/correo.

uses_functions del app.md: +cdp_get_ax_outline, +cdp_get_text_in_frame,
+cdp_screenshot_bytes; -cdp_perceive_outline_py_pipelines.

Verificacion: go build OK, go test OK (4 unit pass, 3 e2e skip gated BMCP_E2E=1),
go vet OK, gofmt limpio, sin "Default 9222" en el codigo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 17:35:33 +02:00
parent 9c170b9c43
commit fed245a738
9 changed files with 214 additions and 146 deletions
+62 -23
View File
@@ -2,8 +2,8 @@
name: browser_mcp
lang: go
domain: infra
version: 0.5.0
description: "Servidor MCP que expone control total del navegador via CDP (40 tools: navegación, DOM, cookies, iframes, teclado/scroll, diálogos, estado de sesión, selección determinista de pestaña, lectura compacta texto/AX + bucle percibir→actuar por #ref con auto-observe, incluyendo find-ref-by-text) 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."
version: 0.6.0
description: "Servidor MCP que expone control total del navegador via CDP (42 tools: navegación, DOM, cookies, iframes, teclado/scroll, diálogos, estado de sesión, selección determinista de pestaña, 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, y screenshot devuelto como image content que el LLM ve) 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."
tags: [mcp, browser, cdp, automation, scraping]
e2e_checks:
- id: build
@@ -52,8 +52,10 @@ uses_functions:
- 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_perceive_outline_py_pipelines
- 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
@@ -116,7 +118,7 @@ podría manipular pestañas ajenas del usuario (banca, correo). Para evitarlo:
- Para adjuntarte deliberadamente al navegador diario, pasa `port: 9222` explícito en cada
tool. Hazlo solo con cuidado.
## Tools (39)
## Tools (42)
### Sesión (`tools_session.go`)
- `browser_launch` (MUTA) — lanza Chrome con CDP. args: port, headless, user_data_dir, url.
@@ -144,11 +146,17 @@ podría manipular pestañas ajenas del usuario (banca, correo). Para evitarlo:
— 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.
Implementado por subprocess (`fn run cdp_perceive_outline`). Si `tab_id` se omite, usa la
primera pestaña page. args: port, tab_id (opcional), max_chars (default 20000).
**Gotcha:** requiere el binario `fn` y el venv de Python del registry disponibles en runtime.
**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 a archivo. args: port, path, full_page.
- `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.
@@ -157,17 +165,23 @@ podría manipular pestañas ajenas del usuario (banca, correo). Para evitarlo:
- `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.
- `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.
- `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 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), cerrando el bucle percibir→actuar:
`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
@@ -175,8 +189,14 @@ dom_click_ref → click humanizado + outline nuevo tras la acción
dom_type_ref → escribe + outline nuevo
```
Las tools `*_ref` usan humanización por defecto (Bézier+jitter). Una política de
sesión `fast`/`instant` para scraping masivo está pendiente (ver TODO en el código).
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.
@@ -193,6 +213,9 @@ sesión `fast`/`instant` para scraping masivo está pendiente (ver TODO en el c
- `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.
@@ -218,11 +241,11 @@ Transporte HTTP (Streamable HTTP):
### Flag `--read-only`
Con `--read-only`, el servidor NO registra las tools mutantes (marcadas MUTA arriba):
solo expone las 17 tools de lectura/control (`browser_connect`, `browser_disconnect`, `tab_list`,
solo expone las 19 tools de lectura/control (`browser_connect`, `browser_disconnect`, `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_wait_element`, `cookie_get`,
`frame_list`, `frame_get_html`, `storage_save`). Útil para sesiones de inspección sin riesgo de
modificar el estado del navegador.
`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
@@ -232,8 +255,10 @@ Funciones del dominio `browser` que NO se exponen como tools en esta versión, c
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`** — ya expuesto desde v0.2.0 via la tool `page_perceive`, que invoca
el pipeline `cdp_perceive_outline` por subprocess (`fn run`) en vez de duplicar la lógica aquí.
- **`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
@@ -241,6 +266,20 @@ Funciones del dominio `browser` que NO se exponen como tools en esta versión, c
## Capability growth log
- 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