feat: browser_mcp — servidor MCP de control de navegador CDP (33 tools + pool de conexiones)
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
---
|
||||
name: browser_mcp
|
||||
lang: go
|
||||
domain: infra
|
||||
version: 0.1.0
|
||||
description: "Servidor MCP que expone control total del navegador via CDP (33 tools: navegación, DOM, cookies, iframes, teclado/scroll, diálogos, estado de sesión) reusando funciones del dominio browser del registry con un pool de conexiones CDP vivas."
|
||||
tags: [mcp, browser, cdp, automation, scraping]
|
||||
uses_functions:
|
||||
- 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_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
|
||||
uses_types: []
|
||||
framework: ""
|
||||
entry_point: "main.go"
|
||||
dir_path: "projects/web_scraping/apps/browser_mcp"
|
||||
repo_url: ""
|
||||
---
|
||||
|
||||
# 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.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 9222). Las tools de tabs
|
||||
(`tab_list`, `tab_new`, `tab_close`, `tab_activate`) usan el endpoint HTTP `/json` de CDP
|
||||
directamente (host `localhost`), no el pool, porque no requieren una sesión WebSocket viva.
|
||||
|
||||
## Tools (33)
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
- `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_eval_js` (MUTA) — `Runtime.evaluate`. args: port, expression.
|
||||
- `page_screenshot` — captura a archivo. args: port, path, 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).
|
||||
|
||||
### 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.
|
||||
|
||||
### 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):
|
||||
|
||||
```bash
|
||||
cd projects/web_scraping/apps/browser_mcp
|
||||
go build -o browser_mcp .
|
||||
./browser_mcp
|
||||
```
|
||||
|
||||
Transporte HTTP (Streamable HTTP):
|
||||
|
||||
```bash
|
||||
./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 14 tools de lectura (`browser_connect`, `browser_disconnect`, `tab_list`,
|
||||
`tab_activate`, `page_wait_load`, `page_wait_idle`, `page_get_html`, `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.
|
||||
|
||||
## 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`** — el árbol de accesibilidad se obtiene hoy via un pipeline Python;
|
||||
futuro a exponer via `fn run` en vez de duplicar la lógica aquí.
|
||||
- **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.
|
||||
Reference in New Issue
Block a user