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
+42 -9
View File
@@ -25,6 +25,7 @@ func registerDomTools(s *server.MCPServer, d *deps) {
s.AddTool(domClickRefTool(), mcp.NewTypedToolHandler(d.handleDomClickRef))
s.AddTool(domTypeRefTool(), mcp.NewTypedToolHandler(d.handleDomTypeRef))
s.AddTool(domHoverRefTool(), mcp.NewTypedToolHandler(d.handleDomHoverRef))
s.AddTool(domClickXYTool(), mcp.NewTypedToolHandler(d.handleDomClickXY))
}
}
@@ -58,7 +59,7 @@ func (d *deps) handleDomClickRef(_ context.Context, _ mcp.CallToolRequest, a dom
return mcp.NewToolResultError(err.Error()), nil
}
time.Sleep(settleDelay)
outline, _ := d.perceiveOutline(port, 4000)
outline, _ := d.perceiveOutline(port, 8000)
return mcp.NewToolResultText("clicked ref " + fmt.Sprint(a.Ref) + "\n\n" + outline), nil
}
@@ -92,7 +93,7 @@ func (d *deps) handleDomTypeRef(_ context.Context, _ mcp.CallToolRequest, a domT
return mcp.NewToolResultError(err.Error()), nil
}
time.Sleep(settleDelay)
outline, _ := d.perceiveOutline(port, 4000)
outline, _ := d.perceiveOutline(port, 8000)
return mcp.NewToolResultText("typed into ref " + fmt.Sprint(a.Ref) + "\n\n" + outline), nil
}
@@ -122,10 +123,42 @@ func (d *deps) handleDomHoverRef(_ context.Context, _ mcp.CallToolRequest, a dom
return mcp.NewToolResultError(err.Error()), nil
}
time.Sleep(settleDelay)
outline, _ := d.perceiveOutline(port, 4000)
outline, _ := d.perceiveOutline(port, 8000)
return mcp.NewToolResultText("hovered ref " + fmt.Sprint(a.Ref) + "\n\n" + outline), nil
}
// ---- dom_click_xy (MUTA) — click humanizado por coordenadas absolutas ----
type domClickXYArgs struct {
Port int `json:"port"`
X float64 `json:"x"`
Y float64 `json:"y"`
Mode string `json:"mode"`
}
func domClickXYTool() mcp.Tool {
return mcp.NewTool("dom_click_xy",
mcp.WithDescription("Fallback de click por coordenadas absolutas (x, y) en CSS pixels del viewport, con movimiento de ratón humanizado por defecto. Pensado para usarse sobre lo que el agente VE en page_screenshot cuando el outline de page_perceive no basta (canvas, mapas, layouts visuales). Prefiere dom_click_ref cuando el elemento aparece en el outline. Devuelve el outline actualizado tras la acción (auto-observe)."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithNumber("x", mcp.Required(), mcp.Description("Coordenada X absoluta en CSS pixels del viewport.")),
mcp.WithNumber("y", mcp.Required(), mcp.Description("Coordenada Y absoluta en CSS pixels del viewport.")),
mcp.WithString("mode", mcp.Description("Velocidad: 'human' (default, Bézier+jitter anti-bot), 'fast' (movimiento reducido, scraping masivo), 'instant' (sin movimiento de ratón).")),
)
}
func (d *deps) handleDomClickXY(_ context.Context, _ mcp.CallToolRequest, a domClickXYArgs) (*mcp.CallToolResult, error) {
port := portOr(a.Port)
err := d.withConn(port, func(c *browser.CDPConn) error {
return browser.CdpClickXYHuman(c, a.X, a.Y, browser.MouseProfileForMode(a.Mode))
})
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
time.Sleep(settleDelay)
outline, _ := d.perceiveOutline(port, 8000)
return mcp.NewToolResultText(fmt.Sprintf("clicked at (%g, %g)\n\n%s", a.X, a.Y, outline)), nil
}
// ---- dom_click (MUTA) ----
type domClickArgs struct {
@@ -136,7 +169,7 @@ type domClickArgs struct {
func domClickTool() mcp.Tool {
return mcp.NewTool("dom_click",
mcp.WithDescription("Click the element matching the CSS selector (synthetic CDP click)."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithString("selector", mcp.Required(), mcp.Description("CSS selector of the element to click.")),
)
}
@@ -164,7 +197,7 @@ type domClickHumanArgs struct {
func domClickHumanTool() mcp.Tool {
return mcp.NewTool("dom_click_human",
mcp.WithDescription("Click the element matching the CSS selector with human-like mouse movement (Bézier path + jitter + press/release pause)."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithString("selector", mcp.Required(), mcp.Description("CSS selector of the element to click.")),
)
}
@@ -192,7 +225,7 @@ type domClickTextArgs struct {
func domClickTextTool() mcp.Tool {
return mcp.NewTool("dom_click_text",
mcp.WithDescription("Find the first element whose visible text matches and click it."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithString("text", mcp.Required(), mcp.Description("Visible text to match (substring).")),
)
}
@@ -220,7 +253,7 @@ type domTypeArgs struct {
func domTypeTool() mcp.Tool {
return mcp.NewTool("dom_type",
mcp.WithDescription("Type text into the currently focused element (dispatches key events char by char)."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithString("text", mcp.Required(), mcp.Description("Text to type.")),
)
}
@@ -248,7 +281,7 @@ type domFindByTextArgs struct {
func domFindByTextTool() mcp.Tool {
return mcp.NewTool("dom_find_by_text",
mcp.WithDescription("Find the first element whose visible text matches and return a unique CSS selector for it (empty string if none)."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithString("text", mcp.Required(), mcp.Description("Visible text to match (substring).")),
)
}
@@ -315,7 +348,7 @@ type domWaitElementArgs struct {
func domWaitElementTool() mcp.Tool {
return mcp.NewTool("dom_wait_element",
mcp.WithDescription("Block until an element matching the CSS selector appears in the DOM (or timeout)."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
mcp.WithString("selector", mcp.Required(), mcp.Description("CSS selector to wait for.")),
mcp.WithNumber("timeout_ms", mcp.Description("Max wait in ms. Default 10000.")),
)