23f9aa90e8
- .mcp.json - CAPABILITIES_TODO.md - demo_e2e/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
461 lines
33 KiB
Markdown
461 lines
33 KiB
Markdown
---
|
|
title: Capacidades de navegador (CDP) + construcción del MCP full-CDP
|
|
artefacto: project · projects/web_scraping
|
|
created: 06/06/2026 00:00
|
|
updated: 06/06/2026 07:00
|
|
status: in_progress
|
|
related_issues: []
|
|
related_flows: []
|
|
---
|
|
|
|
## Objetivo
|
|
|
|
Dos objetivos encadenados:
|
|
|
|
1. **Inventario** — mapear todas las capacidades de control de navegador del proyecto `web_scraping`
|
|
contra el dominio `browser` del registry (funciones Go, Bash y pipelines Python) y la app
|
|
`script_navegador`. Marcar qué está cubierto, qué está a medias y qué falta.
|
|
2. **MCP full-CDP** — construir un servidor MCP (`browser_mcp`) que exponga TODAS estas capacidades como
|
|
tools, para que cualquier agente Claude controle el navegador de punta a punta. Los gaps que faltan
|
|
se construyen en paralelo con varios `fn-constructor`, y el MCP los envuelve a medida que aparecen.
|
|
|
|
Este documento es la lista de trabajo viva del proyecto: cada gap es una tarea candidata a delegar a
|
|
`fn-constructor`, y cada función del registry es una tool candidata del `browser_mcp`.
|
|
|
|
Convención de estado por capacidad:
|
|
|
|
- `[x]` Cubierto — hay función(es) del registry dedicadas y probadas.
|
|
- `[~]` Parcial — se puede hacer pero indirecto (vía `cdp_evaluate`) o incompleto (falta parte del CRUD).
|
|
- `[ ]` Falta — no existe ninguna función para esto.
|
|
|
|
---
|
|
|
|
## Resumen ejecutivo
|
|
|
|
| # | Capacidad pedida | Estado | Notas |
|
|
|---|---|---|---|
|
|
| 1 | CRUD de perfiles | `[x]` | Create + Read + Delete + Update(apariencia/clonar/reset). Completo. |
|
|
| 2 | CRUD de ventanas | `[ ]` | No hay nada. Falta crear/listar/mover/redimensionar/cerrar ventanas (Browser.*WindowBounds). |
|
|
| 3 | CRUD de pestañas | `[x]` | Create/List/Navigate + **close/activate** (cdp_close_tab/activate_tab) + **back/forward**. Completo. |
|
|
| 4 | Lanzador personalizado | `[x]` | Perfil, flags, extensiones, proxy, headless. Completo. |
|
|
| 5 | Configuración de detalles | `[~]` | Apariencia + flag CDP global + policy de extensiones. Falta config genérica de prefs. |
|
|
| 6 | Datos del navegador (cookies, historial, marcadores) | `[~]` | Cookies: **CRUD completo** (get/set/delete/clear). Historial perfil: nada. Marcadores: backup/restore. |
|
|
| 7 | Lectura de página (HTML, AX tree, texto) | `[~]` | HTML sí, AX tree sí. Texto plano solo vía `cdp_evaluate` (no dedicado). |
|
|
| 8 | Selección de elementos del DOM | `[x]` | find_by_text, wait_element, picker interactivo, querySelector vía evaluate. |
|
|
| 9 | CRUD de iframes | `[x]` | **list_frames + eval_in_frame + get_frame_html**. Manejo de frames completo. |
|
|
| 10 | Lanzamiento de JS en la página | `[x]` | `cdp_evaluate` + steps `js` de `cdp_extract_recipe`. Completo. |
|
|
|
|
Extras que ya tenemos y no estaban en la lista (capital acumulado): captura de tráfico (HAR +
|
|
mitmproxy), interacción humanizada (curva Bézier + jitter anti-bot), esperas inteligentes
|
|
(idle/load/element), screenshot.
|
|
|
|
---
|
|
|
|
## Catálogo completo — todas nuestras habilidades de un vistazo
|
|
|
|
39 funciones del dominio `browser` (23 Go + 12 Bash + 4 pipelines Python) + 1 pipeline Bash de reset.
|
|
Cada una es una tool candidata del futuro `browser_mcp`. Columna "MCP tool" = nombre propuesto de la tool.
|
|
|
|
### CDP core — Go (`functions/browser/`, importables directo por el MCP)
|
|
|
|
| Función (registry id) | Qué hace | MCP tool propuesta |
|
|
|---|---|---|
|
|
| `chrome_launch_go_browser` | Lanza Chrome/Chromium con remote debugging, mata árbol de proceso | `browser_launch` |
|
|
| `cdp_connect_go_browser` | Handshake WebSocket CDP sobre localhost:port → conexión lista | (interno, lo usa el MCP) |
|
|
| `cdp_close_go_browser` | Cierra conexión WS y/o mata proceso Chrome por PID | `browser_close` |
|
|
| `cdp_navigate_go_browser` | Navega la pestaña a una URL (`Page.navigate`) | `tab_navigate` |
|
|
| `cdp_new_tab_go_browser` | Abre pestaña nueva (`/json/new`) → CdpTab | `tab_new` |
|
|
| `cdp_list_tabs_go_browser` | Lista pestañas/targets (`GET /json`), sólo HTTP | `tab_list` |
|
|
| `cdp_get_html_go_browser` | HTML del DOM vivo post-JS (`outerHTML`) | `page_get_html` |
|
|
| `cdp_screenshot_go_browser` | Screenshot PNG/JPEG, viewport o página completa | `page_screenshot` |
|
|
| `cdp_evaluate_go_browser` | Ejecuta JS arbitrario (`Runtime.evaluate`, soporta await) | `page_eval_js` |
|
|
| `cdp_click_go_browser` | Click por selector CSS (scroll + mousedown/up) | `dom_click` |
|
|
| `cdp_click_human_go_browser` | Click humanizado (Bézier + jitter + micro-pausa) anti-bot | `dom_click_human` |
|
|
| `cdp_click_text_go_browser` | Click sobre el elemento cuyo innerText matchea | `dom_click_text` |
|
|
| `cdp_type_text_go_browser` | Escribe texto char a char en el elemento activo | `dom_type` |
|
|
| `cdp_move_mouse_human_go_browser` | Mueve ratón con curva Bézier humanizada | `mouse_move_human` |
|
|
| `cdp_find_by_text_go_browser` | Texto visible → selector CSS único | `dom_find_by_text` |
|
|
| `cdp_wait_element_go_browser` | Espera a que un selector exista en el DOM | `dom_wait_element` |
|
|
| `cdp_pick_element_js_go_browser` | Picker interactivo: hover overlay + click captura selector/XPath/bbox | `dom_pick_element` |
|
|
| `cdp_wait_load_go_browser` | Espera `document.readyState === complete` | `page_wait_load` |
|
|
| `cdp_wait_idle_go_browser` | Espera red en idle (inflight ≤ N durante quietMs) | `page_wait_idle` |
|
|
| `cdp_set_cookie_go_browser` | Set cookie (incl. HttpOnly) vía `Network.setCookie` | `cookie_set` |
|
|
| `cdp_har_record_go_browser` | Captura HAR 1.2 de todo el tráfico de una acción | `traffic_record_har` |
|
|
| `list_chrome_profiles_go_browser` | Lista perfiles de un user-data-dir | `profile_list` |
|
|
| `list_chrome_profile_extensions_go_browser` | Lista extensiones instaladas de un perfil | `profile_list_extensions` |
|
|
|
|
### Perfiles + sistema — Bash (`bash/functions/browser/`, el MCP las invoca vía `fn run`/shell)
|
|
|
|
| Función (registry id) | Qué hace | MCP tool propuesta |
|
|
|---|---|---|
|
|
| `create_chrome_profile_bash_browser` | Crea perfil nuevo (con/sin lanzar headless para policy) | `profile_create` |
|
|
| `delete_chrome_profile_bash_browser` | Borra perfil(es) + limpia Local State (backup automático) | `profile_delete` |
|
|
| `prepare_chrome_profile_bash_browser` | Clona user-data-dir limpio (whitelist de extensiones) | `profile_prepare` |
|
|
| `set_chrome_profile_appearance_bash_browser` | Avatar + color de tema de un perfil | `profile_set_appearance` |
|
|
| `backup_chrome_bookmarks_bash_browser` | Backup byte a byte de Bookmarks (preserva checksum) | `bookmark_backup` |
|
|
| `restore_chrome_bookmarks_bash_browser` | Restaura Bookmarks desde backup | `bookmark_restore` |
|
|
| `chrome_load_extensions_bash_browser` | Lanza Chrome con extensiones unpacked (`--load-extension`) | `ext_load_unpacked` |
|
|
| `clean_chrome_profile_extensions_bash_browser` | Purga extensiones fuera de la whitelist de un perfil | `ext_clean` |
|
|
| `apply_chromium_extension_policy_bash_browser` | Policy managed: forcelist + blocklist de extensiones | `ext_apply_policy` |
|
|
| `install_chromium_proxy_extension_bash_browser` | Instala extensión unpacked en todos los perfiles (persistente) | `ext_install_persistent` |
|
|
| `apply_chromium_cdp_flag_bash_browser` | Activa CDP global del sistema (`/etc/chromium.d/cdp`) | `system_cdp_flag` |
|
|
| `launch_chromium_proxy_bash_browser` | Lanza Chromium con perfil aislado apuntando a proxy mitm | `browser_launch_proxy` |
|
|
|
|
### Pipelines — Python + Bash (`*/functions/pipelines/`, el MCP los invoca vía `fn run`)
|
|
|
|
| Pipeline (registry id) | Qué hace | MCP tool propuesta |
|
|
|---|---|---|
|
|
| `cdp_get_ax_tree_py_pipelines` | Accessibility tree completo de un tab | `page_get_ax_tree` |
|
|
| `cdp_open_url_and_wait_py_pipelines` | Crea tab + navega + espera loadEventFired → tab_id | `tab_open_and_wait` |
|
|
| `cdp_extract_recipe_py_pipelines` | Ejecuta recipe YAML (wait_selector/js) contra Chrome | `run_recipe` |
|
|
| `extract_hls_from_cdp_tab_py_pipelines` | Extrae manifests HLS de tabs + iframes | `extract_hls` |
|
|
| `reset_chrome_profiles_bash_pipelines` | Reset destructivo de perfiles (preserva bookmarks) | `profile_reset` |
|
|
|
|
> El MCP tendrá ~28 tools al ensamblar lo existente, y crecerá hasta ~40 al cerrar los gaps de abajo.
|
|
|
|
---
|
|
|
|
## Benchmark vs estado del arte (Playwright MCP + Chrome DevTools MCP)
|
|
|
|
Comparación contra los dos servidores MCP de referencia de la comunidad para fijar qué nos falta
|
|
para tener paridad con un "lanzador típico":
|
|
|
|
- **Microsoft Playwright MCP** — ~60+ tools (incluye testing/assertions/video). Modo por defecto =
|
|
accessibility snapshot, no HTML crudo. Fuente: github.com/microsoft/playwright-mcp.
|
|
- **Google Chrome DevTools MCP** — 26 tools en 6 categorías (input, navegación, emulación,
|
|
performance, network, debugging), CDP crudo igual que nosotros. Fuente: github.com/ChromeDevTools/chrome-devtools-mcp.
|
|
|
|
### Tabla de paridad por categoría
|
|
|
|
| Categoría | Ellos tienen | Nosotros | Veredicto |
|
|
|---|---|---|---|
|
|
| Navegación URL | navigate, back/forward | navigate ✅, back/forward ❌ | falta back/forward |
|
|
| Pestañas | list/new/close/select | list/new ✅, close/activate ❌ (planeado) | en gaps tanda 1 |
|
|
| Lectura DOM | snapshot (AX) + HTML | AX tree ✅, HTML ✅ | paridad |
|
|
| Selección | locators, find by text/role | find_by_text ✅, picker ✅, wait_element ✅ | paridad |
|
|
| Click / type | click, type, **press_key**, **hover** | click(+human) ✅, type ✅, press_key ❌, hover ❌ | falta press_key, hover |
|
|
| Formularios | **fill_form**, **select_option** | ❌ (manual con click+type) | falta |
|
|
| Mouse | xy click/move/**wheel(scroll)**/drag | move_human ✅, click ✅, wheel(scroll) ❌, drag ❌ | falta scroll + drag |
|
|
| Diálogos | **handle_dialog** (alert/confirm) | ❌ | falta (bloquea flujos) |
|
|
| Subida archivos | **file_upload** | ❌ | falta |
|
|
| JS | evaluate | evaluate ✅ | paridad |
|
|
| Consola | **console_messages** | ❌ | falta (debug + detección) |
|
|
| Cookies | get/list/set/delete/clear | set ✅, resto ❌ (planeado) | en gaps tanda 1 |
|
|
| Storage local | **localStorage/sessionStorage** CRUD | ❌ | falta |
|
|
| Estado de sesión | **storage_state** save/restore | ❌ | falta (login persistente) |
|
|
| Network captura | requests list + inspect, **HAR** | HAR ✅, list/inspect en vivo ❌ | HAR cubre; falta inspección puntual |
|
|
| Network mock | **route/abort/fulfill** (intercept) | ❌ (sí mitmproxy externo) | falta intercept inline CDP |
|
|
| Network emulación | online/offline, throttle | ❌ | falta |
|
|
| Emulación device | **emulate** (device/CPU), **resize** viewport | ❌ | falta |
|
|
| Screenshot | screenshot, **PDF** | screenshot ✅, PDF ❌ | falta PDF |
|
|
| Performance | **trace + lighthouse** | ❌ | falta (nicho) |
|
|
| Anti-bot humanizado | — (ellos NO tienen) | click_human, move_human, jitter ✅ | **ventaja nuestra** |
|
|
| Captura tráfico proxy | — (vía HAR) | web_proxy mitmproxy ✅ | **ventaja nuestra** |
|
|
| Perfiles (CRUD disco) | — (ellos NO gestionan perfiles) | create/delete/prepare/appearance/reset ✅ | **ventaja nuestra** |
|
|
|
|
**Conclusión**: en lectura/selección/JS/click hay paridad. Nuestras ventajas: humanización anti-bot,
|
|
captura mitmproxy y gestión de perfiles en disco (Playwright/CDP-MCP no hacen nada de esto). Nos
|
|
faltan, además de los gaps de la lista 1 (tabs/iframes/cookies/ventanas/historial), las capacidades
|
|
de abajo (tanda 2) para alcanzar paridad de automatización.
|
|
|
|
---
|
|
|
|
## Pendiente (gaps a construir)
|
|
|
|
> ✅ **CERRADOS en la tanda mínima viable (06/06/2026)**: #2 (tabs close/activate), #3 (cookies get/delete/clear),
|
|
> #6 (iframes) + tanda 2 #10 (press_key), #11 (handle_dialog), #13 (storage_state), #14 (scroll), #15 (nav back/forward).
|
|
> Ver sección **Hecho**. Lo de abajo es lo que QUEDA.
|
|
|
|
- [ ] 1. **CRUD de ventanas** — funciones nuevas dominio `browser`:
|
|
- `cdp_list_windows` — `Browser.getWindowForTarget` por cada target → id de ventana + bounds.
|
|
- `cdp_set_window_bounds` — mover/redimensionar/maximizar/minimizar (`Browser.setWindowBounds`).
|
|
- `cdp_new_window` — abrir ventana nueva (Target con `newWindow:true`) vs pestaña.
|
|
- `cdp_close_window` — cerrar una ventana concreta sin matar todo el proceso.
|
|
- [ ] 2. **Cerrar/activar pestaña individual** — hoy `cdp_close` mata el proceso entero o cierra la conexión:
|
|
- `cdp_close_tab` — `Target.closeTarget(targetId)` (cierra UNA pestaña).
|
|
- `cdp_activate_tab` — `Target.activateTarget` / `/json/activate/<id>` (traer al frente).
|
|
- [ ] 3. **Cookies completas** — hoy solo `cdp_set_cookie`:
|
|
- `cdp_get_cookies` — `Network.getCookies` / `getAllCookies` (leer, filtrar por dominio).
|
|
- `cdp_delete_cookies` — `Network.deleteCookies`.
|
|
- `cdp_clear_cookies` — `Network.clearBrowserCookies` (wipe completo).
|
|
- [ ] 4. **Historial del navegador** — no existe nada:
|
|
- `cdp_get_history` — leer historial (vía `History` DB del perfil o `Page.getNavigationHistory` para la sesión actual).
|
|
- `cdp_clear_history` — limpiar historial del perfil (decidir: SQLite del perfil con Chromium cerrado, como bookmarks).
|
|
- [ ] 5. **Marcadores CRUD individual** — hoy solo backup/restore byte a byte:
|
|
- `cdp_add_bookmark` / `cdp_remove_bookmark` / `cdp_list_bookmarks` — editar el archivo `Bookmarks` (JSON)
|
|
preservando el checksum, o vía CDP si hay endpoint. Complementa, no sustituye, backup/restore.
|
|
- [ ] 6. **CRUD de iframes** — solo hay lectura indirecta en `extract_hls_from_cdp_tab`:
|
|
- `cdp_list_frames` — árbol de frames (`Page.getFrameTree`): id, url, parent.
|
|
- `cdp_eval_in_frame` — ejecutar JS en el **contexto de ejecución** de un iframe concreto
|
|
(`Runtime.evaluate` con el `executionContextId`/`uniqueContextId` del frame).
|
|
- `cdp_get_frame_html` — HTML de un iframe específico.
|
|
- (opcional) `cdp_navigate_frame` — navegar un iframe a otra URL.
|
|
- [ ] 7. **Texto plano de página dedicado** — hoy se saca con `cdp_evaluate("document.body.innerText")`:
|
|
- `cdp_get_text` — función dedicada que devuelve el texto visible limpio (útil para LLM/scraping rápido).
|
|
Decidir si vale la pena o si el patrón vía evaluate es suficiente (no inflar por inflar).
|
|
- [ ] 8. **Configuración de detalles genérica** — hoy solo apariencia + flag CDP + policy extensiones:
|
|
- Evaluar si hace falta `set_chrome_profile_pref` (editar `Preferences` del perfil: idioma, descargas,
|
|
permisos por defecto, etc.) o si se cubre caso por caso. NO construir hasta tener caso real.
|
|
- [ ] 9. **Playground del proyecto** — `web_scraping` **no tiene** `playground/` (sí lo tienen `analysis/nats`
|
|
y `message_bus/unibus`). Candidato: un `playground/` con UI mínima (server single-file) que liste las
|
|
capacidades CDP y deje lanzarlas contra una pestaña viva para probarlas visualmente. Opcional, solo si
|
|
aporta para validar las funciones nuevas.
|
|
|
|
### Tanda 2 — gaps detectados en el benchmark (paridad con Playwright/CDP-MCP)
|
|
|
|
Prioridad ALTA (bloquean automatización real, los construiría antes que ventanas/historial):
|
|
|
|
- [x] 10. **`cdp_press_key`** ✅ HECHO — `Input.dispatchKeyEvent` con tabla de teclas especiales.
|
|
- [x] 11. **`cdp_handle_dialog`** ✅ HECHO — auto-handler `Page.javascriptDialogOpening` (con `go sendCDP` anti-deadlock).
|
|
- [ ] 12. **`cdp_get_console`** — capturar mensajes de consola y excepciones JS
|
|
(`Runtime.consoleAPICalled` + `Runtime.exceptionThrown`). Debug + detección de errores de la página.
|
|
**PENDIENTE** — único ALTA de tanda 2 sin construir. Sube a P1 (ver análisis LLM-readiness).
|
|
- [x] 13. **`cdp_save_storage_state` / `cdp_load_storage_state`** ✅ HECHO — cookies + localStorage a archivo.
|
|
⚠️ Falta `sessionStorage` y forzar navigate-first (ver deuda P2 del análisis).
|
|
- [x] 14. **`cdp_scroll`** ✅ HECHO — `Input.dispatchMouseEvent mouseWheel`. ⚠️ punto (100,100) hardcodeado (deuda P1).
|
|
- [x] 15. **`cdp_nav_back` / `cdp_nav_forward`** ✅ HECHO — `Page.getNavigationHistory` + `navigateToHistoryEntry`.
|
|
|
|
Prioridad MEDIA (formularios, storage fino, subida, intercept):
|
|
|
|
- [ ] 16. **`cdp_select_option`** — seleccionar valor en `<select>` (vía `cdp_evaluate` envuelto o CDP).
|
|
- [ ] 17. **`cdp_hover`** — hover sobre elemento (`Input.dispatchMouseEvent mouseMoved`) para menús
|
|
desplegables que aparecen al pasar el ratón.
|
|
- [ ] 18. **`cdp_file_upload`** — adjuntar archivos a un `<input type=file>` (`DOM.setFileInputFiles`).
|
|
- [ ] 19. **`cdp_storage_get` / `set` / `clear`** — CRUD de localStorage y sessionStorage (vía evaluate
|
|
o `DOMStorage` domain). Útil si no se quiere todo el storage_state.
|
|
- [ ] 20. **`cdp_intercept_requests`** — interceptar/abortar/modificar/mockear peticiones inline vía
|
|
`Fetch.enable` (bloquear ads/trackers, mockear respuestas, inyectar headers). Complementa, no
|
|
sustituye, al `web_proxy` mitmproxy (este es inline sin proxy externo).
|
|
- [ ] 21. **`cdp_emulate_network`** — online/offline + throttle (`Network.emulateNetworkConditions`).
|
|
- [ ] 22. **`cdp_save_pdf`** — guardar la página como PDF (`Page.printToPDF`).
|
|
|
|
Prioridad BAJA (formularios compuestos, emulación device, performance, drag):
|
|
|
|
- [ ] 23. **`cdp_fill_form`** — rellenar varios campos de una (composición de find+click+type, candidato
|
|
a **pipeline** no a función — encaja con la doctrina de promover composiciones).
|
|
- [ ] 24. **`cdp_emulate_device`** — viewport/userAgent/touch móvil (`Emulation.setDeviceMetricsOverride`).
|
|
- [ ] 25. **`cdp_drag_drop`** — drag and drop entre elementos.
|
|
- [ ] 26. **Performance/Lighthouse** — `cdp_perf_trace` (Tracing domain) + audit Lighthouse. Nicho, solo
|
|
si aparece caso de análisis de rendimiento web.
|
|
|
|
## En curso
|
|
|
|
- [~] (ninguna ahora mismo — documento recién creado)
|
|
|
|
## Hecho (lo que YA tenemos)
|
|
|
|
- [x] **Tanda de deuda A+D+E+B — 4 fixes + 8/8 e2e** (06/06/2026)
|
|
- **A** aislamiento robusto: `chrome_launch` usa el binario real (salta el wrapper que pisaba flags).
|
|
- **D** `sessionStorage` añadido a `storage_state` (save+load). Validado por prueba e2e 6.
|
|
- **E** `cdp_find_by_text` devuelve error en no-encontrado (antes vacío silencioso). Validado por prueba 7.
|
|
- **B** fin del fire-and-forget: `cdp_click` verifica visibilidad, `cdp_type_text` verifica foco. Validado por prueba 8.
|
|
- La batería e2e pasó de 5 a 8 pruebas, todas verdes. Pendiente: C (Enter en widgets JS), `cdp_scroll`
|
|
con target (P1.5), puente percepción→acción por nodeId (P1.3).
|
|
- enlace: functions/browser/{chrome_launch,cdp_save_storage_state,cdp_load_storage_state,cdp_find_by_text,cdp_click,cdp_type_text}.go
|
|
- [x] **Fase C — validación e2e real: 5/5 PASS** (06/06/2026, headless + ventana visible)
|
|
- resultado: batería `projects/web_scraping/demo_e2e/` contra sitios sandbox (quotes/books.toscrape.com,
|
|
the-internet.herokuapp.com). 5 tareas simples→complejas: extracción estructurada, percepción AX,
|
|
teclado/form, **login persistente (storage_state)**, scraping paginado+dedup. Cliente MCP stdio
|
|
secuencial. Chrome aislado en 9333.
|
|
- **3 bugs reales encontrados y arreglados ejecutando** (lo que "compila" no detecta):
|
|
`page_perceive` (args posicionales a fn run), `cdp_save_storage_state` (filtrar cookies por dominio),
|
|
`cdp_load_storage_state` (añadir `url` por cookie para httpOnly). Login persistente ahora funciona.
|
|
- enlace: projects/web_scraping/demo_e2e/RESUMEN.md + results/
|
|
- [x] **`browser_mcp` v1 — servidor MCP de control de navegador** (Go, 06/06/2026)
|
|
- resultado: app en `projects/web_scraping/apps/browser_mcp/` (sub-repo Gitea, `git init` hecho).
|
|
**36 tools** (v0.2.0), pool de conexiones por puerto, stdio + `--http` + `--read-only`. **Build verde**
|
|
(smoke `tools/list`=36). Registrado en `projects/web_scraping/.mcp.json` como server `browser`.
|
|
- ✅ Fase B.5 (P0 LLM-readiness) cerrada: Chrome aislado 9333, `tab_select` determinista, `page_get_text`,
|
|
`page_perceive`. Pendiente Fase C (e2e real contra Chrome) + P1 (verificación post-acción).
|
|
- enlace: projects/web_scraping/apps/browser_mcp/ — patrón `apps/registry_mcp`.
|
|
- [x] **Fix bug `%v` en `cdp_evaluate` + `cdp_eval_in_frame`** (06/06/2026)
|
|
- resultado: objetos/arrays JS ahora se serializan con `json.Marshal` (antes repr de Go inservible).
|
|
Build+vet+test del paquete `browser` verdes. Reindexado.
|
|
- enlace: functions/browser/cdp_evaluate.go, cdp_eval_in_frame.go
|
|
- [x] **Tanda mínima viable del MCP — 15 funciones CDP nuevas** (Go, dominio `browser`, 06/06/2026)
|
|
- resultado: 5 `fn-constructor` en paralelo. Compila (`go build`/`vet`/`test` verdes), indexado (`fn index`),
|
|
15 entradas confirmadas. Tag de grupo `navegator` en todas.
|
|
- **Tabs/navegación**: `cdp_close_tab_go_browser`, `cdp_activate_tab_go_browser` (registradas — el código ya
|
|
vivía en `cdp_list_tabs.go`), `cdp_nav_back_go_browser`, `cdp_nav_forward_go_browser`.
|
|
- **Iframes**: `cdp_list_frames_go_browser`, `cdp_eval_in_frame_go_browser`, `cdp_get_frame_html_go_browser`.
|
|
- **Cookies**: `cdp_get_cookies_go_browser`, `cdp_delete_cookies_go_browser`, `cdp_clear_cookies_go_browser`.
|
|
- **Input/diálogos**: `cdp_press_key_go_browser`, `cdp_scroll_go_browser`, `cdp_handle_dialog_go_browser`.
|
|
- **Sesión**: `cdp_save_storage_state_go_browser`, `cdp_load_storage_state_go_browser` (login persistente).
|
|
- enlace: functions/browser/cdp_*.go — cierra gaps #2, #3, #6(cookies), #9 + tanda2 #10/#11/#13/#14/#15.
|
|
- [x] **Perfiles — CRUD completo** (Bash, dominio `browser`)
|
|
- resultado: Create `create_chrome_profile`, Read `list_chrome_profiles` (+ `list_chrome_profile_extensions`),
|
|
Delete `delete_chrome_profile`, Update `set_chrome_profile_appearance` (avatar + color de tema) /
|
|
`prepare_chrome_profile` (clonar limpio) / `reset_chrome_profiles` (pipeline reset destructivo con
|
|
preservación de bookmarks).
|
|
- enlace: bash/functions/browser/, bash/functions/pipelines/reset_chrome_profiles.md
|
|
- [x] **Lanzador personalizado** (Go + Bash)
|
|
- resultado: `chrome_launch` (remote debugging, multi-OS, mata árbol de proceso),
|
|
`launch_chromium_proxy` (perfil aislado apuntando a proxy mitm/Burp),
|
|
`chrome_load_extensions` (unpacked), `apply_chromium_cdp_flag` (CDP global del sistema),
|
|
`apply_chromium_extension_policy` + `install_chromium_proxy_extension` (distribuir extensiones),
|
|
y subcomando `launch` de `script_navegador`.
|
|
- enlace: functions/browser/chrome_launch.go, bash/functions/browser/
|
|
- [x] **Pestañas — crear / listar / navegar**
|
|
- resultado: `cdp_new_tab`, `cdp_open_url_and_wait` (crear+navegar+esperar load), `cdp_list_tabs`,
|
|
`cdp_navigate`. (Falta cerrar/activar una pestaña concreta → Pendiente #2.)
|
|
- enlace: functions/browser/cdp_new_tab.md, cdp_list_tabs.go, cdp_navigate.go
|
|
- [x] **Lectura de página — HTML + Accessibility tree**
|
|
- resultado: `cdp_get_html` (DOM vivo post-JS), `cdp_get_ax_tree` (pipeline Python, AX tree completo).
|
|
Texto plano hoy vía `cdp_evaluate` (ver Pendiente #7).
|
|
- enlace: functions/browser/cdp_get_html.go, python/functions/pipelines/cdp_get_ax_tree.md
|
|
- [x] **Selección de elementos del DOM**
|
|
- resultado: `cdp_find_by_text` (texto→selector CSS único), `cdp_wait_element` (polling existencia),
|
|
`cdp_pick_element_js` (picker interactivo: hover overlay + click captura selector/XPath/bbox),
|
|
querySelector arbitrario vía `cdp_evaluate`.
|
|
- enlace: functions/browser/cdp_find_by_text.go, cdp_pick_element_js.js, cdp_wait_element.go
|
|
- [x] **Lanzamiento de JS en la página**
|
|
- resultado: `cdp_evaluate` (expresión JS arbitraria, soporta await, reporta excepciones),
|
|
steps `js` de `cdp_extract_recipe` (recipe YAML).
|
|
- enlace: functions/browser/cdp_evaluate.go, python/functions/pipelines/cdp_extract_recipe.md
|
|
- [x] **Marcadores — backup / restore**
|
|
- resultado: `backup_chrome_bookmarks` + `restore_chrome_bookmarks` (copia byte a byte preservando
|
|
checksum, sin reserializar JSON). CRUD individual de bookmarks pendiente (#5).
|
|
- enlace: bash/functions/browser/backup_chrome_bookmarks.sh, restore_chrome_bookmarks.sh
|
|
- [x] **Cookies — set**
|
|
- resultado: `cdp_set_cookie` (incl. HttpOnly, para auth e2e). get/delete/clear pendientes (#3).
|
|
- enlace: functions/browser/cdp_set_cookie.go
|
|
- [x] **Extras (capital acumulado, fuera de la lista pedida)**
|
|
- resultado: captura de tráfico `cdp_har_record` (HAR 1.2) + app `web_proxy` (mitmproxy siempre activo);
|
|
interacción humanizada `cdp_click_human` / `cdp_move_mouse_human` / `cdp_type_text` (anti-detección);
|
|
esperas inteligentes `cdp_wait_idle` / `cdp_wait_load` / `cdp_wait_element`; `cdp_screenshot`;
|
|
pipeline `extract_hls_from_cdp_tab` (manifests HLS de tabs+iframes).
|
|
- enlace: functions/browser/, projects/web_scraping/apps/web_proxy
|
|
|
|
## MCP full-CDP (`browser_mcp`) — estado e iteración
|
|
|
|
**Meta**: un servidor MCP que da a cualquier agente Claude control total del navegador vía CDP.
|
|
Nombre **resuelto: `browser_mcp`** (genérico, para todo control de navegador, no solo CDP).
|
|
|
|
### Estado actual — v1 construido (06/06/2026)
|
|
|
|
- **App**: `projects/web_scraping/apps/browser_mcp/` (Go, sub-repo Gitea propio, `git init` hecho).
|
|
Patrón `registry_mcp`: `github.com/mark3labs/mcp-go` v0.52.0, archivos `tools_<grupo>.go`, registro en
|
|
`main.go`, stdio por defecto + `--http` opcional + flag `--read-only`. **Build verde, 33 tools.**
|
|
- **Pool de conexiones** (resuelto: pool por puerto, NO connect-per-tool). Ver explicación abajo.
|
|
- **Registro**: `projects/web_scraping/.mcp.json` con el server `browser`.
|
|
- **Omitido v1**: `cdp_har_record` (requiere callback), `cdp_get_ax_tree` (pipeline Python), perfiles Bash
|
|
(requieren Chrome cerrado → incompatible con un Chrome vivo).
|
|
|
|
### Pool de conexiones — por qué es requisito, no opción
|
|
|
|
`browser.CdpConnect(port)` hace un handshake WebSocket a una tab "page" de Chrome (~50-200 ms) y esa
|
|
conexión ES una sesión viva (Page.*/Runtime.*/Input.* + un `readLoop` en goroutine + event handlers).
|
|
Si reconectáramos en cada tool: (1) pagaríamos el handshake cada vez, (2) **perderíamos estado entre
|
|
tools** — los handlers de eventos (p.ej. el auto-handler de `handle_dialog`) mueren al cerrar la conexión.
|
|
Por eso `browser_mcp` mantiene `map[port]→*CDPConn`: la primera tool que toca el puerto abre la conexión,
|
|
las siguientes la reusan; se cierra al apagar el MCP o con `browser_disconnect`. **Sin pool, encadenar
|
|
navigate→wait→click→eval es imposible** (cada `fn run` suelto pierde la conexión al terminar el proceso).
|
|
|
|
### ⚠️ Deuda crítica (análisis LLM-readiness) — ver sección dedicada abajo
|
|
|
|
El v1 **envuelve las funciones tal cual**. Un LLM percibe-decide-actúa-verifica; las funciones están
|
|
hechas para un script que ya sabe qué hacer. Antes de declarar el MCP "usable por un agente" hay que
|
|
cerrar los P0 de la sección siguiente (percepción compacta + verificación + target determinista). El
|
|
valor del MCP está en lo que AÑADE encima, no en el wrapping.
|
|
|
|
### Fases
|
|
|
|
- **Fase 0** — inventario + catálogo + plan. ✅
|
|
- **Fase A** — 15 funciones gap de la tanda mínima viable. ✅ (5 fn-constructor paralelos)
|
|
- **Fase B** — ensamblar `browser_mcp` v1 (33 tools, pool, build verde). ✅
|
|
- **Fase B.5 — NUEVO, BLOQUEANTE** — cerrar los P0 del análisis (abajo) y elevar el MCP de "wrapper" a
|
|
"capa de agente": percepción compacta, verificación post-acción, target determinista.
|
|
- **Fase C** — registrar en Claude + e2e (un agente abre Chrome aislado, navega, lee, click, extrae) +
|
|
documentar en `LLM_BROWSER_GUIDE.md`.
|
|
|
|
### Capability group
|
|
|
|
Crear/actualizar `docs/capabilities/browser.md` con el cluster completo (39 funciones + 15 nuevas) +
|
|
ejemplo canónico end-to-end + las tools del MCP, para cargar el grupo en un solo read.
|
|
|
|
---
|
|
|
|
## Análisis LLM-readiness — deuda P0/P1/P2
|
|
|
|
Crítica recibida (06/06/2026): el núcleo CDP es sólido y la cobertura de verbos casi completa, pero el
|
|
sistema está hecho para un programa que ya sabe qué hacer, no para un LLM que percibe-decide-actúa-verifica.
|
|
Faltan las dos piezas que un agente necesita: **percibir sin colapsar el contexto** y **saber si la acción
|
|
funcionó**. Evaluado contra el bucle PERCIBIR → DECIDIR → ACTUAR → VERIFICAR (+ ESTADO transversal).
|
|
|
|
### Ya corregido este turno
|
|
|
|
- [x] **BUG GRAVE `cdp_evaluate` / `cdp_eval_in_frame`** — usaban `fmt.Sprintf("%v", value)`; objetos/arrays
|
|
JS llegaban como repr de Go (`map[a:1]`) en vez de JSON. Arreglado: `json.Marshal` para no-strings.
|
|
(Sin esto el scraping de datos estructurados era inservible.)
|
|
- [x] **Comentario mentiroso de `cdp_navigate`** — decía "espera Page.loadEventFired"; el código solo lanza
|
|
`Page.navigate`. Comentario corregido: NO espera carga, hay que encadenar `CdpWaitLoad`/`CdpWaitIdle`.
|
|
|
|
### P0 — CERRADOS (06/06/2026, Fase B.5) ✅
|
|
|
|
- [x] **P0.1 `render_ax_outline`** ✅ — `render_ax_outline_py_core` (puro: nodos AX → outline indentado con
|
|
`#ref`) + pipeline `cdp_perceive_outline_py_pipelines` (compone `cdp_get_ax_tree` + `trim_ax_tree` +
|
|
`render_ax_outline`). Expuesto como tool `page_perceive` del MCP (vía `fn run`). La pieza de PERCEPCIÓN.
|
|
- [x] **P0.2 Lecturas con límite** ✅ — `cdp_get_text_go_browser` (texto visible, selector opcional, `maxBytes`
|
|
con corte rune-safe). Tool `page_get_text` del MCP (default 20000 bytes). `get_html` se deja intacto
|
|
(sin romper firma); el truncado de HTML vive en la tool del MCP (200k). Decisión KISS documentada.
|
|
- [x] **P0.3 Target determinista + Chrome aislado** ✅ — `cdp_connect_target_go_browser` (elige target por id o
|
|
substring de URL) + refactor `cdp_connect.go` (helper `cdpConnectWS`). En el MCP: **default puerto 9333 =
|
|
Chrome aislado del MCP** (no el 9222 diario), `browser_launch` con `user_data_dir` dedicado, y tool
|
|
`tab_select` para fijar la pestaña determinísticamente. **Cierra el riesgo de operar sobre banca/correo.**
|
|
- [x] **Aislamiento robusto del binario** ✅ (06/06) — `chrome_launch` usa el binario real
|
|
`/usr/lib/chromium/chromium` (salta el wrapper `/etc/chromium.d/cdp` que inyectaba flags globales).
|
|
Antes el aislamiento dependía del orden de los flags.
|
|
|
|
### P1 — fiabilidad de acción
|
|
|
|
- [x] **P1.1 Verificación post-acción** ✅ (06/06) — `cdp_click` verifica visibilidad (oculto → error, no clic
|
|
en (0,0)); `cdp_type_text` verifica foco editable (sin foco → error). Validado por prueba e2e 8. PENDIENTE:
|
|
`cdp_scroll`/`press_key` con confirmación de efecto (menor).
|
|
- [x] **P1.2 `cdp_find_by_text` honesto** ✅ (06/06) — ahora devuelve error explícito en no-encontrado (antes
|
|
`("", nil)` silencioso). Validado por prueba e2e 7. (Aviso de múltiples matches: pendiente, menor.)
|
|
- [ ] **P1.3 Puente percepción→acción** — el LLM percibe en AX tree (role/name/nodeId) pero actúa por selector
|
|
CSS. Falta `click(nodeId)`/`act(#ref)` para actuar por ref del snapshot (como Playwright), sin adivinar CSS.
|
|
- [x] **P1.4 `cdp_type_text` verifica focus** ✅ (06/06) — error claro si no hay campo editable enfocado
|
|
(antes el texto iba a `document.body` en silencio). Validado por prueba e2e 8.
|
|
- [ ] **P1.5 `cdp_scroll` con target** — punto (100,100) hardcodeado; si ahí hay un navbar fixed no scrollea el
|
|
contenido. Permitir x,y o selector/elemento objetivo.
|
|
- [ ] **P1.6 `cdp_get_console`** (era tanda2 #12) — capturar consola + excepciones; el LLM detecta errores de página.
|
|
|
|
### P2 — paridad
|
|
|
|
- [ ] **P2.1** `cdp_select_option`, `cdp_file_upload`, `cdp_hover` (ver tanda 2 #16-18).
|
|
- [x] **P2.2 `storage_state`** ✅ (06/06) — cookies (filtradas por dominio) + localStorage + **sessionStorage**
|
|
+ login persistente, todo validado e2e (pruebas 4 y 6). `load` sigue exigiendo navigate-first al dominio (documentado).
|
|
|
|
> Implicación: el MCP NO es "envolver 39 funciones". Es la capa que arregla los 4 ejes (pool=estado,
|
|
> representaciones compactas=percepción, JSON real=ya hecho, verificación=acción fiable). Un MCP que solo
|
|
> expone las funciones hereda todos los defectos.
|
|
|
|
---
|
|
|
|
## Enlaces
|
|
|
|
- App orquestadora — projects/web_scraping/apps/script_navegador (CLI rápido: open/click/type/eval/html/shot/wait/tabs/launch/close/profiles + runner YAML)
|
|
- App captura — projects/web_scraping/apps/web_proxy (mitmproxy, service systemd-user puerto 8080)
|
|
- Guía LLM — projects/web_scraping/LLM_BROWSER_GUIDE.md
|
|
- Setup CDP global — projects/web_scraping/CHROMIUM_SYSTEM.md
|
|
- Dominio browser en registry — `mcp__registry__fn_search query="" domain="browser"`
|
|
|
|
## Issues / flows relacionados
|
|
|
|
- (ninguno aún — al priorizar los gaps, abrir issue por bloque o delegar a fn-constructor)
|
|
|
|
## Notas
|
|
|
|
- **Decisión de arquitectura**: el control de navegador es CDP crudo sobre Chrome/Chromium en Linux nativo,
|
|
no Playwright/Selenium. Toda capacidad reutilizable vive en `functions/browser/`; las apps solo orquestan.
|
|
- **Prioridad sugerida de gaps** (mayor impacto scraping/automatización primero):
|
|
1) Cerrar/activar pestaña individual (#2) y CRUD de iframes (#6) — bloquean automatización de SPAs reales.
|
|
2) Cookies completas (#3) — leer/borrar cookies es básico para sesiones y limpieza.
|
|
3) CRUD de ventanas (#1) — útil para multi-ventana y posicionamiento, menos urgente para scraping headless.
|
|
4) Historial (#4) y bookmarks CRUD (#5) — nicho, construir cuando haya caso concreto.
|
|
- **No inflar por inflar**: `cdp_get_text` (#7) y config genérica de prefs (#8) solo si aparece un caso real
|
|
repetido; el patrón vía `cdp_evaluate` puede ser suficiente (regla function_growth_and_self_docs).
|
|
- Cada función nueva del dominio `browser` debe declararse en `uses_functions` del `app.md` que la consuma
|
|
(el indexer no deduce deps en Go automáticamente para apps).
|