{"id":"87314431-e4f4-49c1-8c58-adc8fd8be5d7","slug":"browser-rs","kind":"mcp","name":"browser-rs","summary":"Browser automation for AI agents in Rust — Playwright MCP's tools on a bundled Chromium, a third of the memory, pages as Markdown, batching and an in-place human hand-off.","owner":"luka-loehr","repoUrl":"https://github.com/luka-loehr/browser-rs","license":"MIT","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-09-13T21:06:29.000Z","addedAt":"2026-10-21T05:08:04.339Z","logoUrl":"https://avatars.githubusercontent.com/u/172180589?u=bdd24b16c0d5d490bf955bc8c6e1987ab9172bf8&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["browser-web","files-documents","cloud-infra"],"tags":["agent-tools","ai-agents","browser-automation","chrome-devtools-protocol","chromium","claude","headless-browser","playwright"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-27T00:45:30.805Z","sample":false,"url":"https://conduits.dev/tools/browser-rs","language":"Rust","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"![browser-rs — the browser your agent would pick](docs/assets/banner.svg)\n\n# browser-rs\n\n[![Rust](https://img.shields.io/badge/Rust-2021-ce422b?style=flat&logo=rust)](https://www.rust-lang.org) [![MCP](https://img.shields.io/badge/MCP-server-7c5cff?style=flat)](https://modelcontextprotocol.io) [![Chromium](https://img.shields.io/badge/Chromium-153-4285f4?style=flat&logo=googlechrome&logoColor=white)](https://googlechromelabs.github.io/chrome-for-testing/) [![Platform](https://img.shields.io/badge/Platform-macOS-lightgrey?style=flat&logo=apple)](#8-limits) [![License](https://img.shields.io/badge/License-MIT-orange?style=flat)](LICENSE)\n\nI wanted a browser my agents actually like using. So I built one in Rust, on a bundled Chromium,\nwith the tools of Microsoft's Playwright MCP, and then let Opus agents do real, multi-step work with\nit and fixed everything they complained about, round after round.\n\nIt uses **535 MB where Playwright MCP needs 1,496 MB** over the same four real pages, navigates\n20–35% faster, and is ready in 3–6 ms. It reads pages as Markdown instead of giant snapshots, runs\nmany steps per call, and when a login needs a human, it hands them the exact live page without\nreloading anything.\n\nWebsite: [browser-rs.lukaloehr.com](https://browser-rs.lukaloehr.com)\n\n---\n\n## 1. What it does\n\n- **Playwright MCP's tools, same names and parameters.** 39 by default, 84 with `--caps all`:\n  navigation, snapshots with element refs, trusted clicks, typing, drag and drop, dialogs, uploads,\n  tabs, network mocking, cookies and storage, screenshots, PDF, tracing, video, test assertions.\n- **Reading without snapshots.** `browser_text` returns the page as Markdown (the article, one\n  section, or just the opening paragraph), `browser_links` lists links as one line each,\n  `browser_table`, `browser_extract` and `browser_read` pull exactly the values you need, and\n  `browser_frames` reads every frame, nested framesets and cross-origin iframes included.\n- **Fewer round trips.** `browser_batch` runs navigate, type, click, wait and read in one call, and\n  a `read` step with `equals` turns into an assertion. Every action can skip its snapshot\n  (`snapshot: \"none\"`); by default a reply shows only the lines that changed.\n- **The web beyond the page.** `browser_fetch` sends HTTP with the browser's cookies directly (no\n  CORS), trimming JSON to the `fields` you ask for or running a `transform`. `browser_scrape` reads\n  many URLs at once in background tabs.\n- **In-place human hand-off.** `browser_handoff` opens a live view of the running page in a tab of\n  your own browser. You click, type, paste and answer dialogs; nothing restarts or reloads. Measured\n  over the viewer's own connection: input to visible change p50 16 ms, 60 fps while animating.\n- **Measured, not guessed.** Every reply ends with `elapsed_ms`; `BROWSER_RS_TRACE=<file>` logs each\n  call with its duration and reply size.\n\n## 2. Built by its users\n\nSix Opus agents, restricted to browser-rs, each got a hard real-world task: researching GitHub pull\nrequests, a Wikipedia link race, Hacker News with the linked articles, a full e-commerce checkout\nwith its error cases, fourteen tricky widgets (dialogs, uploads, iframes, shadow DOM, drag and drop),\nand debugging on MDN. Each timed its steps with the server's own numbers and reported bugs, friction\nand the tools it wished it had. Their requests became the code between rounds.\n\n| Round | Scores (0–10) | Satisfied | Would pick over Playwright MCP | Cost |\n|---|---|---|---|---|\n| 1 | 5 · 8 · 7 · 7 · 5 · 5 | 0 of 6 | 3 of 6 | $4.57 |\n| 2 | 7 · 8 · 7 · 8 · 7 · 8 | 0 of 6 | 6 of 6 | $2.71 |\n| 3 | 8 · 8 · 7 · 8 · 8 · 7 | 4 of 6 | 6 of 6 | $2.67 |\n| 4 | 8 · 8 · 7 · 9 · 8 · 8 | 2 of 6 | 6 of 6 | $1.67 |\n| 5 | 8 · 8 · 7 · 8 · 8 · 6 | 2 of 6 | 6 of 6 | $2.67 |\n| 6 | 8 · 8 · 9 · 8 · 8 · 8 | 4 of 6 | 6 of 6 | $2.36 |\n\nThe Wikipedia race went from 75,000 characters of tool replies and 7.6 s of browser time to 3,400\ncharacters and 0.9 s. The harness lives outside this repo; the tasks and prompts are described in\nthe commit history.\n\n## 3. System\n\n```\n agent ──stdio/MCP──▶ browser-rs (Rust, ~9.5 MB)\n                         │  DevTools Protocol over two pipes (fd 3/4), no websocket, no port\n                         ▼\n               chrome-headless-shell 153 ─── headless work (default)\n               Chrome for Testing 153 ────── only for --headed / browser_set_mode\n                         │\n          hand-off:  Page.startScreencast ──▶ 127.0.0.1 viewer (random token) ──▶ your browser tab\n                     trusted Input.dispatch* ◀── mouse, keys, paste, dialogs ◀──┘\n```\n\nA `--watchdog` process next to each Chromium holds a pipe from the server and takes the whole\nbrowser process group down if the server dies, even by SIGKILL. No orphaned Chromium, ever.\n\n## 4. Architecture\n\n| Path | Role |\n|---|---|\n| `src/cdp.rs` | DevTools Protocol client on `--remote-debugging-pipe`: NUL-delimited JSON, one event handler, one-shot waiters |\n| `src/install.rs` | Downloads and locates the pinned Chrome for Testing builds |\n| `src/browser.rs` | Launch, tab state from CDP events, navigation with HTTP status, headless ↔ headed relaunch, watchdog |\n| `src/actions.rs` | Trusted input, evaluate, screenshots, PDF, storage, routes, server-side fetch, parallel scrape, tracing, video |\n| `src/injected.js` | Runs in an isolated world of every page: accessibility snapshot and refs, actionability checks, Markdown, links, tables, reads |\n| `src/response.rs` | Playwright-style replies, snapshot modes and diffs, compact console summary |\n| `src/tools.rs` | The MCP tools, per-call options, batch dispatch, timing and tracing |\n| `src/liveview.rs`, `src/liveview.html` | The hand-off viewer: token-protected local server, screencast relay, input forwarding |\n| `src/keys.rs` | Playwright key names to CDP key events |\n| `scripts/bench_browser.py` | The benchmark against `@playwright/mcp` |\n| `scripts/smoke.py` | Smoke tests over MCP against a local test server: `python3 scripts/smoke.py` after a build |\n\nBefore every click the page runtime waits until the element is attached, visible, stable, not\nanimating, enabled and actually hit at its center; if something covers it, the error names what.\n\n## 5. Quickstart\n\n```sh\ngit clone https://github.com/luka-loehr/browser-rs\ncd browser-rs && cargo build --release\nclaude mcp add browser-rs \"$PWD/target/release/browser-rs\"\n```\n\nChromium downloads itself on the first tool call (chrome-headless-shell, ~99 MB), or ahead of time\nwith `target/release/browser-rs install`, which also fetches the full browser for headed mode. The\ncache is `~/Library/Caches/browser-rs` (Linux: `~/.cache/browser-rs`).\n\nOr paste this into your agent and let it do the setup:\n\n```\nInstall browser-rs for me. Run `curl -fsSL https://browser-rs.lukaloehr.com/setup.txt`\nfirst, then follow it exactly.\n```\n\n## 6. Tools\n\n**Default (39):** `browser_navigate`, `browser_navigate_back`, `browser_snapshot`, `browser_find`,\n`browser_click`, `browser_hover`, `browser_type`, `browser_fill_form`, `browser_select_option`,\n`browser_press_key`, `browser_drag`, `browser_drop`, `browser_file_upload`, `browser_handle_dialog`,\n`browser_evaluate`, `browser_wait_for`, `browser_take_screenshot`, `browser_resize`, `browser_tabs`,\n`browser_console_messages`, `browser_network_requests`, `browser_network_request`, `browser_close`,\n`browser_install`, and the agent tools `browser_text`, `browser_frames`, `browser_links`, `browser_table`,\n`browser_extract`, `browser_read`, `browser_fetch`, `browser_scrape`, `browser_batch`,\n`browser_search`, `browser_check`, `browser_scroll_until`, `browser_run_snippet`,\n`browser_handoff`, `browser_set_mode`.\n\n**With `--caps`:** `vision` (coordinate mouse), `pdf`, `network` (route, offline), `storage`\n(cookies, localStorage, sessionStorage, storage state), `devtools` (tracing, video, highlights,\nrecording), `testing` (locators, verify), `config`. `--caps all` enables everything.\n\n**Targets** accept a snapshot ref (`e12`), a CSS selector (the first visible match wins;\n`iframe#x >> body` enters an iframe or shadow root), `text=…`, `role=button[name=\"…\"]`,\n`link=<regex>` or `href=/wiki/C++`.\n\n**The fast way to drive it:**\n\n1. Read with `browser_text`, `browser_links`, `browser_table`, `browser_extract`, `browser_read` or\n   `browser_fetch`, not snapshots.\n2. Act with refs or selectors, several steps per `browser_batch`, `snapshot: \"none\"` when you don't\n   need to see the page.\n3. Read many pages with `browser_scrape`; trim APIs with `browser_fetch` `fields` or `transform`.\n4. Hand the page to a person with `browser_handoff` when they have to log in.\n\n## 7. Options\n\nSame names as `@playwright/mcp` where they exist:\n\n```\n--headless / --headed           start headless (default) or with a visible window\n--caps <list>                   vision,pdf,network,storage,devtools,testing,config or all\n--executable-path <path>        your own Chromium or Chrome (also BROWSER_RS_EXECUTABLE)\n--user-data-dir <path>          --isolated                    --storage-state <path>\n--viewport-size <WxH>           --user-agent <ua>             --ignore-https-errors\n--proxy-server <url>            --proxy-bypass <domains>      --block-service-workers\n--allowed-origins <a;b>         --blocked-origins <a;b>       --init-script <path>\n--output-dir <path>             --test-id-attribute <name>\n--snapshot-mode <mode>          incremental (default), full or none\n--snapshot-max-chars <n>        default 50000; action replies inline at most 8000\n--timeout-action <ms> (5000)    --timeout-navigation <ms> (60000)    --timeout-settle <ms> (500)\n```\n\nEnvironment: `BROWSER_RS_CACHE`, `BROWSER_RS_TRACE`, `BROWSER_RS_NO_VIEWER` (tests), `BROWSER_RS_DEBUG`.\n\n## 8. Limits\n\n- Tested on macOS. Linux uses the same code but is untested; Windows is not supported (the pipe\n  transport and the watchdog are Unix-only).\n- In the hand-off view, headless Chromium does not draw native `<select>` dropdowns, date pickers or\n  file choosers, and passkeys do not work; use a one-time code or a password.\n- `browser_set_mode` relaunches Chromium, so pages reload; use `browser_handoff` when state matters.\n- Not implemented: `browser_run_code_unsafe` (needs Playwright's Node runtime), `browser_annotate`,\n  `browser_resume`, and the `--device`, `--mobile`, `--init-page` and `--config` options.\n- Cross-origin iframes are not part of snapshots and selectors cannot enter them; `browser_frames`\n  reads their text. Same-origin iframes, framesets and open shadow roots are part of snapshots.\n\n## 9. Benchmarks\n\nAgainst `@playwright/mcp@0.0.80`, both on chrome-headless-shell, same four pages (example.com, a\nWikipedia article, Hacker News, a GitHub repository), three rounds, medians, memory summed over the\nwhole process tree. Reproduce with `python3 scripts/bench_browser.py`.\n\n| | browser-rs | Playwright MCP |\n|---|---|---|\n| Memory after 4 pages | **535 MB · 5 processes** | 1,496 MB · 9 processes |\n| Navigate Wikipedia / Hacker News / GitHub | **214 / 185 / 274 ms** | 315 / 229 / 330 ms |\n| `browser_snapshot` on GitHub | **26 ms** | 36 ms |\n| Server ready | **3–6 ms** | 574 ms |\n| Tool schemas in the agent's context | **15.9k chars** | 19.5k chars |\n\n## 10. License\n\nMIT, see [LICENSE](LICENSE). Tool names follow Microsoft's `@playwright/mcp`; no Playwright code is\nincluded. Chromium for Testing is downloaded from Google under its own licences.\n\nMade by [Luka Löhr](https://lukaloehr.com). This repository grew out of\n`rust-mcp-servers`, whose browser server it was; its history is kept here.\n"}