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:
+43
-4
@@ -10,10 +10,12 @@ import (
|
||||
"fn-registry/functions/browser"
|
||||
)
|
||||
|
||||
// registerFrameTools wires frame_list + frame_get_html (read) and frame_eval (MUTA).
|
||||
// registerFrameTools wires frame_list + frame_get_html + frame_get_text (read)
|
||||
// and frame_eval (MUTA).
|
||||
func registerFrameTools(s *server.MCPServer, d *deps) {
|
||||
s.AddTool(frameListTool(), mcp.NewTypedToolHandler(d.handleFrameList))
|
||||
s.AddTool(frameGetHTMLTool(), mcp.NewTypedToolHandler(d.handleFrameGetHTML))
|
||||
s.AddTool(frameGetTextTool(), mcp.NewTypedToolHandler(d.handleFrameGetText))
|
||||
|
||||
if !d.readOnly {
|
||||
s.AddTool(frameEvalTool(), mcp.NewTypedToolHandler(d.handleFrameEval))
|
||||
@@ -29,7 +31,7 @@ type frameListArgs struct {
|
||||
func frameListTool() mcp.Tool {
|
||||
return mcp.NewTool("frame_list",
|
||||
mcp.WithDescription("List all frames (including iframes) of the current page via Page.getFrameTree. Returns JSON with frame IDs."),
|
||||
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.")),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,7 +60,7 @@ type frameEvalArgs struct {
|
||||
func frameEvalTool() mcp.Tool {
|
||||
return mcp.NewTool("frame_eval",
|
||||
mcp.WithDescription("Evaluate a JavaScript expression inside a specific frame's execution context. Returns the stringified result."),
|
||||
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("frame_id", mcp.Required(), mcp.Description("Frame ID (from frame_list).")),
|
||||
mcp.WithString("expression", mcp.Required(), mcp.Description("JavaScript expression to evaluate.")),
|
||||
)
|
||||
@@ -93,7 +95,7 @@ type frameGetHTMLArgs struct {
|
||||
func frameGetHTMLTool() mcp.Tool {
|
||||
return mcp.NewTool("frame_get_html",
|
||||
mcp.WithDescription("Return the serialized HTML of a specific frame. Truncated to 200000 chars."),
|
||||
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("frame_id", mcp.Required(), mcp.Description("Frame ID (from frame_list).")),
|
||||
)
|
||||
}
|
||||
@@ -113,3 +115,40 @@ func (d *deps) handleFrameGetHTML(_ context.Context, _ mcp.CallToolRequest, a fr
|
||||
}
|
||||
return mcp.NewToolResultText(truncate(html, htmlMax)), nil
|
||||
}
|
||||
|
||||
// ---- frame_get_text ----
|
||||
|
||||
type frameGetTextArgs struct {
|
||||
Port int `json:"port"`
|
||||
FrameID string `json:"frame_id"`
|
||||
MaxBytes int `json:"max_bytes"`
|
||||
}
|
||||
|
||||
func frameGetTextTool() mcp.Tool {
|
||||
return mcp.NewTool("frame_get_text",
|
||||
mcp.WithDescription("Return the visible text (innerText) of a specific iframe, truncated to max_bytes. Use this to read content trapped inside an iframe — page_get_text only covers the top-level document. Get the frame_id from frame_list."),
|
||||
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("frame_id", mcp.Required(), mcp.Description("Frame ID (from frame_list).")),
|
||||
mcp.WithNumber("max_bytes", mcp.Description("Máximo de bytes a devolver. Default 20000. 0 = sin límite.")),
|
||||
)
|
||||
}
|
||||
|
||||
func (d *deps) handleFrameGetText(_ context.Context, _ mcp.CallToolRequest, a frameGetTextArgs) (*mcp.CallToolResult, error) {
|
||||
if a.FrameID == "" {
|
||||
return mcp.NewToolResultError("frame_id is required"), nil
|
||||
}
|
||||
maxBytes := a.MaxBytes
|
||||
if maxBytes == 0 {
|
||||
maxBytes = 20000
|
||||
}
|
||||
var text string
|
||||
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
|
||||
var e error
|
||||
text, e = browser.CdpGetTextInFrame(c, a.FrameID, maxBytes)
|
||||
return e
|
||||
})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(err.Error()), nil
|
||||
}
|
||||
return mcp.NewToolResultText(text), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user