feat: browser_mcp — servidor MCP de control de navegador CDP (33 tools + pool de conexiones)
This commit is contained in:
+110
@@ -0,0 +1,110 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
|
||||
"fn-registry/functions/browser"
|
||||
)
|
||||
|
||||
// registerInputTools wires press_key, scroll, handle_dialog. All MUTA.
|
||||
func registerInputTools(s *server.MCPServer, d *deps) {
|
||||
if d.readOnly {
|
||||
return
|
||||
}
|
||||
s.AddTool(pressKeyTool(), mcp.NewTypedToolHandler(d.handlePressKey))
|
||||
s.AddTool(scrollTool(), mcp.NewTypedToolHandler(d.handleScroll))
|
||||
s.AddTool(handleDialogTool(), mcp.NewTypedToolHandler(d.handleHandleDialog))
|
||||
}
|
||||
|
||||
// ---- press_key (MUTA) ----
|
||||
|
||||
type pressKeyArgs struct {
|
||||
Port int `json:"port"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
func pressKeyTool() mcp.Tool {
|
||||
return mcp.NewTool("press_key",
|
||||
mcp.WithDescription("Press a named key (Enter, Tab, Escape, ArrowDown, Backspace, ...) on the focused element."),
|
||||
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
|
||||
mcp.WithString("key", mcp.Required(), mcp.Description("Key name, e.g. Enter, Tab, Escape, ArrowDown.")),
|
||||
)
|
||||
}
|
||||
|
||||
func (d *deps) handlePressKey(_ context.Context, _ mcp.CallToolRequest, a pressKeyArgs) (*mcp.CallToolResult, error) {
|
||||
if a.Key == "" {
|
||||
return mcp.NewToolResultError("key is required"), nil
|
||||
}
|
||||
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
|
||||
return browser.CdpPressKey(c, a.Key)
|
||||
})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(err.Error()), nil
|
||||
}
|
||||
return mcp.NewToolResultText("pressed " + a.Key), nil
|
||||
}
|
||||
|
||||
// ---- scroll (MUTA) ----
|
||||
|
||||
type scrollArgs struct {
|
||||
Port int `json:"port"`
|
||||
DeltaX float64 `json:"delta_x"`
|
||||
DeltaY float64 `json:"delta_y"`
|
||||
}
|
||||
|
||||
func scrollTool() mcp.Tool {
|
||||
return mcp.NewTool("scroll",
|
||||
mcp.WithDescription("Scroll the page by (delta_x, delta_y) pixels via a synthetic mouse wheel event."),
|
||||
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
|
||||
mcp.WithNumber("delta_x", mcp.Description("Horizontal scroll delta in pixels. Default 0.")),
|
||||
mcp.WithNumber("delta_y", mcp.Description("Vertical scroll delta in pixels. Default 300.")),
|
||||
)
|
||||
}
|
||||
|
||||
func (d *deps) handleScroll(_ context.Context, _ mcp.CallToolRequest, a scrollArgs) (*mcp.CallToolResult, error) {
|
||||
deltaY := a.DeltaY
|
||||
if deltaY == 0 && a.DeltaX == 0 {
|
||||
deltaY = 300
|
||||
}
|
||||
err := d.withConn(portOr(a.Port), func(c *browser.CDPConn) error {
|
||||
return browser.CdpScroll(c, a.DeltaX, deltaY)
|
||||
})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(err.Error()), nil
|
||||
}
|
||||
return mcp.NewToolResultText("scrolled"), nil
|
||||
}
|
||||
|
||||
// ---- handle_dialog (MUTA) ----
|
||||
|
||||
type handleDialogArgs struct {
|
||||
Port int `json:"port"`
|
||||
Accept bool `json:"accept"`
|
||||
PromptText string `json:"prompt_text"`
|
||||
}
|
||||
|
||||
func handleDialogTool() mcp.Tool {
|
||||
return mcp.NewTool("handle_dialog",
|
||||
mcp.WithDescription("Arm an auto-handler that responds to every JS dialog (alert/confirm/prompt/beforeunload) on the tab until disconnect. The handler lives in the pooled connection."),
|
||||
mcp.WithNumber("port", mcp.Description("CDP port. Default 9222.")),
|
||||
mcp.WithBoolean("accept", mcp.DefaultBool(true), mcp.Description("Whether to accept (true) or dismiss (false) dialogs. Default true.")),
|
||||
mcp.WithString("prompt_text", mcp.Description("Text to enter for prompt() dialogs.")),
|
||||
)
|
||||
}
|
||||
|
||||
func (d *deps) handleHandleDialog(_ context.Context, _ mcp.CallToolRequest, a handleDialogArgs) (*mcp.CallToolResult, error) {
|
||||
port := portOr(a.Port)
|
||||
c, err := d.pool.get(port)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(err.Error()), nil
|
||||
}
|
||||
cancel, err := browser.CdpHandleDialog(c, a.Accept, a.PromptText)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(err.Error()), nil
|
||||
}
|
||||
d.pool.setCancel(port, cancel)
|
||||
return mcp.NewToolResultText("dialog auto-handler armed"), nil
|
||||
}
|
||||
Reference in New Issue
Block a user