Files
browser_mcp/tools_cookies.go
T
egutierrez fed245a738 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>
2026-06-06 17:35:33 +02:00

146 lines
4.8 KiB
Go

package main
import (
"context"
"encoding/json"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"fn-registry/functions/browser"
)
// registerCookieTools wires cookie_get (read) + set/delete/clear (MUTA).
func registerCookieTools(s *server.MCPServer, d *deps) {
s.AddTool(cookieGetTool(), mcp.NewTypedToolHandler(d.handleCookieGet))
if !d.readOnly {
s.AddTool(cookieSetTool(), mcp.NewTypedToolHandler(d.handleCookieSet))
s.AddTool(cookieDeleteTool(), mcp.NewTypedToolHandler(d.handleCookieDelete))
s.AddTool(cookieClearTool(), mcp.NewTypedToolHandler(d.handleCookieClear))
}
}
// ---- cookie_get ----
type cookieGetArgs struct {
Port int `json:"port"`
}
func cookieGetTool() mcp.Tool {
return mcp.NewTool("cookie_get",
mcp.WithDescription("Return all browser cookies (Network.getAllCookies) as JSON."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
)
}
func (d *deps) handleCookieGet(_ context.Context, _ mcp.CallToolRequest, a cookieGetArgs) (*mcp.CallToolResult, error) {
var cookies []browser.CdpCookie
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
var e error
cookies, e = browser.CdpGetCookies(c)
return e
})
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
b, _ := json.MarshalIndent(cookies, "", " ")
return mcp.NewToolResultText(string(b)), nil
}
// ---- cookie_set (MUTA) ----
type cookieSetArgs struct {
Port int `json:"port"`
Name string `json:"name"`
Value string `json:"value"`
Domain string `json:"domain"`
Path string `json:"path"`
HTTPOnly bool `json:"http_only"`
}
func cookieSetTool() mcp.Tool {
return mcp.NewTool("cookie_set",
mcp.WithDescription("Set a cookie via Network.setCookie."),
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("name", mcp.Required(), mcp.Description("Cookie name.")),
mcp.WithString("value", mcp.Description("Cookie value.")),
mcp.WithString("domain", mcp.Required(), mcp.Description("Cookie domain.")),
mcp.WithString("path", mcp.Description("Cookie path. Default /.")),
mcp.WithBoolean("http_only", mcp.Description("Mark the cookie HttpOnly.")),
)
}
func (d *deps) handleCookieSet(_ context.Context, _ mcp.CallToolRequest, a cookieSetArgs) (*mcp.CallToolResult, error) {
if a.Name == "" {
return mcp.NewToolResultError("name is required"), nil
}
if a.Domain == "" {
return mcp.NewToolResultError("domain is required"), nil
}
path := a.Path
if path == "" {
path = "/"
}
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
return browser.CdpSetCookie(c, a.Name, a.Value, a.Domain, path, a.HTTPOnly)
})
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
return mcp.NewToolResultText("cookie set: " + a.Name), nil
}
// ---- cookie_delete (MUTA) ----
type cookieDeleteArgs struct {
Port int `json:"port"`
Name string `json:"name"`
Domain string `json:"domain"`
}
func cookieDeleteTool() mcp.Tool {
return mcp.NewTool("cookie_delete",
mcp.WithDescription("Delete cookies by name (optionally scoped to a domain) via Network.deleteCookies."),
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("name", mcp.Required(), mcp.Description("Cookie name to delete.")),
mcp.WithString("domain", mcp.Description("Optional domain scope.")),
)
}
func (d *deps) handleCookieDelete(_ context.Context, _ mcp.CallToolRequest, a cookieDeleteArgs) (*mcp.CallToolResult, error) {
if a.Name == "" {
return mcp.NewToolResultError("name is required"), nil
}
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
return browser.CdpDeleteCookies(c, a.Name, a.Domain)
})
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
return mcp.NewToolResultText("cookie deleted: " + a.Name), nil
}
// ---- cookie_clear (MUTA) ----
type cookieClearArgs struct {
Port int `json:"port"`
}
func cookieClearTool() mcp.Tool {
return mcp.NewTool("cookie_clear",
mcp.WithDescription("Clear all browser cookies via Network.clearBrowserCookies."),
mcp.WithNumber("port", mcp.Description("CDP port. Default 9333 (Chrome isolated del MCP); usa 9222 explícito solo para adjuntarte al navegador diario.")),
)
}
func (d *deps) handleCookieClear(_ context.Context, _ mcp.CallToolRequest, a cookieClearArgs) (*mcp.CallToolResult, error) {
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
return browser.CdpClearCookies(c)
})
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
return mcp.NewToolResultText("cookies cleared"), nil
}