browser_mcp
MCP server (Go) that exposes the registry's CDP browser-control functions
(fn-registry/functions/browser) as MCP tools. Drive a live Chrome/Chromium over the
Chrome DevTools Protocol: navigate, read the DOM, click, manage cookies, evaluate
JavaScript, operate iframes, and persist/restore session state.
33 tools total, grouped by domain. See app.md for the full per-tool reference and the
"Omitido en v1" section.
Build
cd projects/web_scraping/apps/browser_mcp
go mod tidy # first time only
go build -o browser_mcp .
browser_mcp only imports fn-registry/functions/browser (no sqlite/cgo), so a plain
go build works. If transitive deps ever require it, fall back to
CGO_ENABLED=1 go build -tags fts5 -o browser_mcp ..
Architecture: live CDP connection pool
Unlike registry_mcp (one DB handle), browser_mcp keeps a pool of live CDP
connections keyed by port. A CDP connection is a live WebSocket session to a "page"
tab; reusing it avoids paying the ~50-200ms handshake on every tool and preserves state
between tools (e.g. the persistent dialog auto-handler armed by handle_dialog). The
pool retries once on a dead-connection error (Chrome may have closed the tab between
tools). See pool.go and deps.withConn in main.go.
Register in Claude Code
Add to a .mcp.json (the project's projects/web_scraping/.mcp.json already has it):
{
"mcpServers": {
"browser": {
"command": "/home/enmanuel/fn_registry/projects/web_scraping/apps/browser_mcp/browser_mcp",
"args": []
}
}
}
For an inspection-only session that cannot mutate browser state, pass "args": ["--read-only"].
Transports
- stdio (default) — for MCP clients.
- HTTP —
./browser_mcp --http :7740(Streamable HTTP).--bind 0.0.0.0requiresREGISTRY_API_TOKEN(bearer auth).
Example session
Assuming a Chrome already running with --remote-debugging-port=9222 (or call
browser_launch first), a typical agent flow:
browser_launch { "port": 9222, "url": "https://example.com" } # -> "launched pid=... port=9222"
browser_connect { "port": 9222 } # -> "connected port=9222"
tab_navigate { "port": 9222, "url": "https://example.com" }
page_wait_load { "port": 9222, "timeout_ms": 10000 }
page_get_html { "port": 9222 } # -> serialized HTML (truncated 200k)
dom_find_by_text { "port": 9222, "text": "More information" } # -> "a" / "#id" selector
dom_click { "port": 9222, "selector": "a" }
page_eval_js { "port": 9222, "expression": "document.title" } # -> page title
page_screenshot { "port": 9222, "path": "/tmp/example.png", "full_page": true }
browser_disconnect{ "port": 9222 }
Cookies, iframes (frame_list -> frame_eval/frame_get_html), keyboard/scroll
(press_key, scroll), JS dialogs (handle_dialog), and session persistence
(storage_save / storage_load) follow the same per-port pattern.