GitHub - vernikr-personal/browser-agent-mcp: MCP server giving LLM agents a real desktop Chrome (X11 mouse, Cloudflare Turnstile auto-solve) via the browser-agent-server daemon. Companion project: vernikr/browser-agent · GitHub
Skip to content

About

MCP server giving LLM agents a real desktop Chrome (X11 mouse, Cloudflare Turnstile auto-solve) via the browser-agent-server daemon. Companion project: vernikr/browser-agent

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

@vernikr/browser-agent-mcp

npm CI Release Node 20+ License: MIT

🔗 Companion project: vernikr/browser-agent (PyPI browser-agent-server) — the real-Chrome-on-Xvfb daemon this MCP server talks to over its HTTP API. The two projects are developed together; the contract is docs/api.md.

MCP server that gives LLM agents a real desktop browser. Your agent gets browser_status, browser_fetch, browser_action and browser_agent — backed by headful Chrome on Xvfb with a hardware-level X11 mouse (Bézier curves, isTrusted: true clicks), Cloudflare Turnstile auto-solve and zero-leak CDP. The heavy lifting happens on a Linux host running browser-agent-server; this bridge is a thin, well-behaved MCP citizen in front of it.

Quickstart

# 0. Have a daemon reachable: install browser-agent-server on a Linux host (see the companion repo),
#    then open a route to it (default: SSH local forward to 127.0.0.1:8766)
pnpm dlx @vernikr/browser-agent-mcp tunnel start --ssh user@host

# 1. Point your agent at the bridge — writes the MCP config for you
pnpm dlx @vernikr/browser-agent-mcp init --client claude-code     # also: claude-desktop | cursor | freebuff | gemini-cli

# 2. Verify end-to-end
pnpm dlx @vernikr/browser-agent-mcp doctor

pnpm not available? Every command also works as npx -y @vernikr/browser-agent-mcp …. init --runner npx writes configs as npx -y @vernikr/browser-agent-mcp@latest — the quickest route to fresh releases (see below).

The MCP config written by init (manual form):

{
  "mcpServers": {
    "browser-agent": {
      "command": "pnpm",
      "args": ["dlx", "@vernikr/browser-agent-mcp"],
      "env": { "BROWSER_AGENT_URL": "http://127.0.0.1:8766" }
    }
  }
}

Getting bridge updates

Config Fresh release arrives Why
pnpm dlx @vernikr/browser-agent-mcp (also @latest) pnpm's cache decides pnpm caches the resolved dlx environment for as long as dlxCacheMaxAge says — even for @latest. Check the setting rather than a number restated here
npx -y @vernikr/browser-agent-mcp@latest whatever the registry cache says npx always resolves the latest dist-tag over the network; the only thing standing between you and a release is the registry packument's own Cache-Control, which npm owns and can change
pinned, e.g. …@1.0.2 never (by design) reproducible

So: want prompt auto-updates (recommended for MCP clients, which restart the bridge on launch) → use the npx runner: pnpm dlx @vernikr/browser-agent-mcp init --client <you> --runner npx, or hand-write the config above with @latest. Sticking to a pnpm-only toolbox? pnpm dlx still updates — on its own schedule; to force it now: pnpm --config.dlx-cache-max-age=0 dlx @vernikr/browser-agent-mcp@latest doctor.

Tools

Tool What it does Notes
browser_status Daemon/X11/Chrome/mouse/WARP status read-only; call before a fetch wave
browser_fetch Open URL in real Chrome, Turnstile auto-solve, return text/HTML/cookies save_text_to / save_html_to keep megabytes out of context; include_screenshot returns MCP image content
browser_action Single-step driving: goto/move/click/type/key/scroll/screenshot/stop hardware X11 events (no CDP input); return_screenshot saves a round trip; use_proxy routes one session through WARP
browser_agent Vision-guided computer use: URL + goal → it clicks/types to the answer slow & costly on small hosts — only for pages plain fetch can't handle

Errors come back structured: browser-agent-mcp error (<kind>): <message> + a hint: line (network_error, rate_limit, auth_or_bot_wall, timeout, server_error), so agents can branch without parsing prose.

Configuration (env)

Var Default Meaning
BROWSER_AGENT_URL http://127.0.0.1:8766 where the daemon's HTTP API is reachable (tunnel/VPN/localhost)
BROWSER_AGENT_TIMEOUT_MS 180000 per-attempt timeout; cold start after hibernation takes seconds — keep ≥60s
BROWSER_AGENT_ATTEMPTS 3 retries on transient failures (network/408/429/5xx) with backoff + Retry-After
BROWSER_AGENT_LOG_FILE $XDG_STATE_HOME/browser-agent-mcp/logs/mcp-browser-agent.jsonl rotating-friendly jsonl with secret redaction
MCP_LOG_LEVEL info debug for HTTP-level traces
MCP_LOG_FILE unset fallback log path when BROWSER_AGENT_LOG_FILE is not set

init copies every one of these that is set in your shell into the generated config, so the client behaves the way your shell does — the alternative is the client silently running on defaults. A variable that is set but unusable (a non-numeric timeout, an unknown log level) is copied and reported on stderr, because loadConfig() would otherwise discard it without a word. BROWSER_AGENT_URL is always written, default included. XDG_STATE_HOME is read but not copied — set BROWSER_AGENT_LOG_FILE if you need the log somewhere specific.

CLI

browser-agent-mcp                        # MCP server on stdio (what agents run)
browser-agent-mcp --http [--port 8767]   # Streamable HTTP at 127.0.0.1:8767/mcp
browser-agent-mcp --selfcheck [--offline]
browser-agent-mcp doctor [--json] [--url …]
browser-agent-mcp init --client <agent> [--user] [--runner pnpm|npx] [--print]
browser-agent-mcp tunnel {start|ensure|check|stop|vnc} --ssh user@host

--selfcheck needs no network with --offline (in-process handshake); without it, also probes the daemon and checks api_version compatibility.

For AI agents

  • Install one-liner: pnpm dlx @vernikr/browser-agent-mcp init --client <your-host> then … doctor.
  • Daemon side (the thing BROWSER_AGENT_URL points at): browser-agent-server on PyPI — install & provisioning guide in the companion repo.
  • Before a fetch wave: browser_status. If a fetch returns blocked: true, retry once with use_proxy: true; a persistent login wall is a human item, not a retry loop.
  • use_proxy on browser_fetch and browser_action means different things. On a fetch, omitting it lets the daemon auto-retry a blocked page through WARP. On an action there is no auto-retry — you are already driving a live session — so omitting it means "leave the session on the route it is on", and false actively forbids WARP. Switching the route restarts Chrome and drops cookies and page state, so set it once per session rather than per click, and check warp_proxy_ready in browser_status first: a forced route with WARP down degrades to direct silently, and goto reports success either way.

Docs

License

MIT — see LICENSE.

About

MCP server giving LLM agents a real desktop Chrome (X11 mouse, Cloudflare Turnstile auto-solve) via the browser-agent-server daemon. Companion project: vernikr/browser-agent

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages