# Browser Rs Mcp

> Lightweight stealth-oriented browser MCP server in Rust — one shared Chrome for many AI agents, 64+ Playwright-style tools, no Node.js runtime.

- **Type:** MCP server
- **Install:** `agentstack add mcp-maestrojeong-browser-rs-mcp`
- **Verified:** Pending review
- **Seller:** [maestrojeong](https://agentstack.voostack.com/s/maestrojeong)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [maestrojeong](https://github.com/maestrojeong)
- **Source:** https://github.com/maestrojeong/browser-rs-mcp

## Install

```sh
agentstack add mcp-maestrojeong-browser-rs-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# browser-rs

**One real browser. Many agents. A ~5.5 MB Rust server.**

[](https://github.com/maestrojeong/browser-rs-mcp/actions/workflows/ci.yml)
[](https://github.com/maestrojeong/browser-rs-mcp/releases)
[](LICENSE)

browser-rs is a lightweight, stealth-oriented browser MCP server. It lets
multiple AI agents share one logged-in Chrome — each agent controls only its
own tabs — for parallel scraping, web automation, and QA without every agent
spinning up its own browser. 67 Playwright-style tools, one Rust binary, no
Node.js runtime.

```mermaid
flowchart LR
    A["Agent Aowner=research"] --> M["browser-rs MCPone Rust process"]
    B["Agent Bowner=operations"] --> M
    C["Agent Cowner=qa"] --> M
    M --> H["one shared Chromepersistent profile"]
    H --> T1["research tabs"]
    H --> T2["operations tabs"]
    H --> T3["qa tabs"]
```

## Why browser-rs?

| | browser-rs | Playwright/Puppeteer-based MCP (Node) |
|---|:--|:--|
| Server runtime | Single Rust binary | Node.js + npm dependency tree |
| Release artifact | ~5.5 MB | Runtime and packages installed separately |
| Server memory¹ | ~6 MB RSS | ~180 MB RSS |
| Multi-agent control | One Chrome, owner-isolated tab groups | Separate coordination required |
| Browser control | Raw CDP over one multiplexed WebSocket | Playwright |

The default mode uses a locally installed, headful Chrome with a persistent
profile and does not inject page patches, minimizing common automation signals.
It does **not** guarantee bot-detection bypass — no automation stack can — but
ships reproducible detector runners under [`bench/`](./bench) so changes can be
tested against current browsers and detectors. See [DESIGN.md](DESIGN.md) for
how the stealth defaults work.

¹ Historical maintainer measurements excluding Chrome, taken from idle local
servers. Exact memory varies by OS, build, runtime, and workload; treat these
figures as an order-of-magnitude comparison, not a benchmark guarantee. The
release binary size can be checked with `du -h target/release/browser-rs`.

## Quick start

**1. Install** — on macOS arm64 and Linux x64 the installer downloads a
prebuilt binary. A locally installed Google Chrome or Chromium is also required.

```bash
curl -fsSL https://raw.githubusercontent.com/maestrojeong/browser-rs-mcp/main/install.sh | sh
browser-rs --help
```

To pin this release instead of following `latest`:

```bash
curl -fsSL https://raw.githubusercontent.com/maestrojeong/browser-rs-mcp/main/install.sh | AB_VERSION=v0.1.22 sh
```

**2. Run** — use stdio for a client that launches the server:

```bash
browser-rs
```

**3. Verify** — point an MCP client at it and drive the browser:

```text
browser_navigate  → https://example.com   # a headful Chrome window opens
browser_snapshot                          # returns the accessibility tree
```

The common workflow is: `browser_snapshot`, act, then inspect the returned
accessibility diff. Most interaction tools accept a snapshot `ref` or a CSS
selector.

Other install options — direct downloads, SHA-256 files, source builds, and
updating a running binary — are in **[INSTALL.md](INSTALL.md)**. To build from
source:

```bash
cargo install --git https://github.com/maestrojeong/browser-rs-mcp ab-mcp
```

Set `AB_CHROME` if Chrome is not in a standard location.

What's new (v0.1.14 - v0.1.22)

**v0.1.22 — exact pointer capabilities.** Snapshot refs are unique capabilities
bound to the page target and main-document loader, so navigation and later
snapshots make old refs fail instead of silently retargeting them. The new
`browser_pointer` tool adds right-click, double-click, horizontal/vertical
scroll, and drag through either trusted CDP input or an explicit untrusted
`dom_event` route. Trusted input retains humanized motion; DOM scrolling only
succeeds when the selected scroll container actually moves.

**v0.1.21 — opt-in background typing.** `browser_type` waits for completion
and returns its settle diff by default, preserving the existing action contract.
Pass `wait: false` only when a later `browser_cancel_typing` call is needed.

**v0.1.20 — cancellable typing.** `browser_type` can run keyboard dispatch in
the background so a later `browser_cancel_typing` call can stop it mid-flight.
Long text (≥ 30 chars) uses a single CDP `Input.insertText` instead of
char‑by‑char key events for speed. Call `browser_cancel_typing({page})` to
stop a running typing task mid-flight; already-typed characters remain.

**v0.1.19 — out-of-process iframe support.** Cross-origin iframe actions now
route CDP commands through the iframe target's own flatten-mode session when
Chrome Site Isolation places it in a separate renderer process (OOPIF). The
session is carried together with the execution context through nested frame
chains and reused across calls. This fixes structures such as a same-origin
wrapper containing a cross-origin Kakao postcode iframe. The public iframe
tool interfaces remain unchanged.

**v0.1.18 — nested cross-origin iframe resolution.** The iframe tools now
resolve the selected iframe element directly to its CDP frame ID before
falling back to URL/name matching, keep paths relative to the current frame
context across repeated origin boundaries, and handle frames without `src` or
`name` attributes. OOPIF target-session routing is completed in v0.1.19.

**v0.1.17 — cross-origin iframe support.** `browser_iframe_click` and
`browser_iframe_fill` now work on cross-origin iframes, not just same-origin
ones — same-origin frames still resolve with a single JS round trip, but a
cross-origin boundary automatically falls back to CDP
(`Page.getFrameTree` + `Page.createIsolatedWorld`), which isn't subject to
the Same-Origin Policy. Both tools also support nested iframes via a
Playwright-style `" >> "` chain in `frame_selector` (e.g.
`"iframe.wrapper >> iframe.popup"`). A new `browser_iframe_read` tool reads
`outerHTML`/`innerText` from inside an iframe with the same resolution
logic — use it where `browser_get_visible_html`/`_text`/`browser_snapshot`
can't see past a cross-origin boundary. `browser_snapshot`'s accessibility
tree also now always surfaces iframe nodes (even nameless ones) with a hint
to use these tools, instead of silently pruning them. Frame resolution that
would otherwise be ambiguous (e.g. two sibling iframes sharing a `name` or
overlapping `src`) is rejected with an error rather than silently guessing
and risking action in the wrong origin — see the `frame_selector` docs on
`browser_iframe_click`/`_fill`/`_read` for the current known limitations
(no CSS-aware `>>` escaping, no redirect tracking).

**v0.1.14 — managed hosting security.** browser-rs can now run behind a
trusted host (like an agent platform) that spins up one server for many
tenants. New in this mode: per-owner HMAC capability tokens, a fail-closed
`AB_ALLOWED_TOOLS` allowlist, an optional secret broker that keeps the
credential database and lookup logic out of browser-rs, and graceful shutdown
that cleans up Chrome. See [Managed mode](#managed-mode-secure-multi-tenant-hosting)
below.

**v0.1.15 — safer output limits.** Output truncation (snapshots, HTML, visible
text, API responses) now respects UTF-8 character boundaries instead of
cutting mid-character, and managed hosts can raise the internal limit so the
secret broker redacts *before* truncation, not after.

**v0.1.16 — output-limit hardening.** A caller-supplied `maxLength`/`maxBytes`
(e.g. `usize::MAX`) is now clamped to an absolute ceiling (`AB_MAX_OUTPUT_LIMIT`,
default 5 MB — well above the 100k/200k tool defaults) so one managed tenant
can no longer force an oversized response and degrade the shared process for
everyone else.

Standalone stdio and unauthenticated loopback HTTP — the default for most
users — are unaffected and keep their existing behavior.

## Connect an MCP client

Example stdio configuration:

```jsonc
{
  "mcpServers": {
    "browser-rs": {
      "command": "browser-rs"
    }
  }
}
```

Use HTTP when several agents should share one browser process and profile:

```bash
browser-rs --port 9321
# streamable HTTP: http://127.0.0.1:9321/mcp
# legacy SSE:      http://127.0.0.1:9321/sse
```

Configure the client with `http://127.0.0.1:9321/mcp` for streamable HTTP, or
`/sse` for clients that still use legacy SSE. Keep HTTP on loopback unless it is
behind a trusted, authenticated proxy; non-loopback binds and the
`X-Browser-Capability` header are covered in **[INSTALL.md](INSTALL.md)**.

## Multi-agent tabs

Each HTTP request identifies its topic, worker, or job with a stable owner (via
an `?owner=` query param or the `X-Browser-Owner` header). New tabs are assigned
to the request owner, and each agent lists, switches, and controls only its own
tabs — even though every agent shares the same Chrome process, login state, and
persistent profile. An owner-scoped `browser_close` closes only that agent's
tabs without stopping the browser.

Owner isolation is an in-process scope, not an authentication boundary.
Connections without an owner are administrative and can access all tabs, so do
not expose an ownerless HTTP endpoint publicly. Owner setup, per-owner cleanup,
and capability-header details are in **[INSTALL.md](INSTALL.md)**.

## Managed mode (secure multi-tenant hosting)

Plain multi-agent tabs (above) trust every caller. **Managed mode** is for a
host that runs one browser-rs process for many untrusted tenants — it adds
per-owner authentication and keeps the credential *database* out of
browser-rs. (Secret *values* still pass through browser-rs' memory on their
way to Chrome — the broker keeps the lookup/storage logic and long-term
credentials out, not the in-flight value itself.)

```mermaid
sequenceDiagram
    participant T as Tenant A (owner=A)
    participant H as Host platform
    participant B as browser-rs
    participant S as Secret broker
    participant C as Chrome

    H->>H: derive capability = HMAC(root, "A")
    H-->>T: hand tenant its capability for owner "A"
    T->>B: request + X-Browser-Capability + X-Browser-Owner: A
    B->>B: verify capability, pin session to owner "A"
    B->>S: transform tool input (e.g. inject a saved password)
    S-->>B: real value, never logged to the client
    B->>C: drive the browser (CDP)
    C-->>B: page output (may contain secrets)
    B->>S: redact output
    S-->>B: safe-to-return text
    B-->>T: result (owner A's tabs only)
```

- **Capability tokens** — the host holds one root secret and hands each tenant
  an `HMAC-SHA256(root, owner)` token, so a leaked tenant token can't be used
  to impersonate another owner or reach `/owners` (root-only).
- **Secret broker** — a Unix-socket side-process the host controls. browser-rs
  sends tool *input* through it before acting (e.g. to fill in a real
  credential the tenant never sees) and tool *output* through it again before
  replying (to redact secrets from page text). browser-rs itself never touches
  the credential database, and any broker error or timeout fails closed. Tools
  that return page content (`browser_snapshot`, `browser_get_visible_html`,
  `browser_get_visible_text`, `browser_api_request`) accept an optional
  `maxLength`/`maxBytes` so the host can raise the internal limit and let the
  broker redact *before* the caller-visible truncation is applied.
- **Tool allowlist** — `AB_ALLOWED_TOOLS` restricts which `browser_*` tools a
  managed tenant can even see or call.

**Required** to turn this on: `AB_MANAGED=1` plus both `AB_HTTP_CAPABILITY` and
`AB_SPAWN_NONCE` (the server refuses to start managed without all three).
**Optional**: `AB_SECRET_BROKER_SOCKET` and `AB_SECRET_BROKER_TOKEN` together,
if you want the redaction broker (see [CLI and environment](#cli-and-environment)
for full per-variable semantics). Standalone/self-hosted users can ignore this
whole section — it only activates when a host explicitly configures it.

## Tools

MCP exposes 67 `browser_*` tools:

**Navigation and inspection:** `browser_navigate` · `browser_new_page` · `browser_snapshot` · `browser_activate_page` · `browser_read` · `browser_get_visible_html` · `browser_get_visible_text` · `browser_find` · `browser_take_screenshot` · `browser_save_pdf` · `browser_pages` · `browser_tabs` · `browser_switch_page` · `browser_profile` · `browser_status`

**Interaction:** `browser_click` · `browser_pointer` · `browser_wheel` · `browser_type` · `browser_cancel_typing` · `browser_press_key` · `browser_hover` · `browser_select_option` · `browser_fill_form` · `browser_drag` · `browser_file_upload` · `browser_navigate_back` · `browser_wait_for` · `browser_resize` · `browser_evaluate` · `browser_run_code_unsafe` · `browser_iframe_click` · `browser_iframe_fill` · `browser_iframe_read` · `browser_close_page` · `browser_close`

Use `browser_activate_page({ "page": "p5" })` before automating a background
tab whose site throttles lazy loading. It calls CDP `Target.activateTarget`,
retries visibility/focus verification, and uses a process-specific macOS
foreground fallback when browser-rs launched Chrome itself. Use
`browser_wheel({ "page": "p5", "delta_y": 700, "x": 650, "y": 500 })` for a
real CDP `mouseWheel` event instead of DOM `window.scrollBy()`.
Use `browser_pointer` for right-click/double-click and for an explicit
`input_route: "dom_event"` background compatibility path. Constructed DOM
events have `isTrusted == false`; they are never an automatic fallback from
trusted CDP input.
`browser_type` keeps human-like key events for text under 30 characters and
uses one atomic CDP `Input.insertText` command for longer input.
`browser_cancel_typing` stops active per-character typing started with
`wait: false`; text already entered remains, while atomic long-text insertion
normally finishes before cancellation.

**Network and requests:** `browser_network_requests` · `browser_route_block` · `browser_route_mock` · `browser_route_clear` · `browser_network_state_set` · `browser_api_request`

**Cookies and storage:** `browser_cookie_list` · `browser_cookie_get` · `browser_cookie_set` · `browser_cookie_delete` · `browser_cookie_clear` · `browser_localstorage_list` · `browser_localstorage_get` · `browser_localstorage_set` · `browser_localstorage_delete` · `browser_localstorage_clear` · `browser_sessionstorage_list` · `browser_sessionstorage_get` · `browser_sessionstorage_set` · `browser_sessionstorage_delete` · `browser_sessionstorage_clear` · `browser_storage_save` · `browser_storage_load`

**Diagnostics and page utilities:** `browser_console_messages` · `browser_fingerprint_check` · `browser_handle_dialog` · `browser_highlight` · `browser_hide_highlight` · `browser_webauthn` · `browser_claim_page` · `browser_release_page`

## CLI and environment

```text
browser-rs                          # stdio MCP transport
browser-rs --port 9321 [options]    # HTTP MCP transport
  --host             HTTP bind host (default 127.0.0.1)
  --user-data-dir    Persistent browser profile directory
  --profile          Alias for --user-data-dir
  --headless               Run headless
  --headed                 Run headful (default)
  --connect      Attach to an existing Chrome
  --stealth                Enable the JS fallback layer
```

`--port` enables HTTP mode; without it, the server uses stdio. The equivalent
environment variables are `AB_HTTP`, `AB_PROFILE`, `AB_HEADLESS`, `AB_CONNECT`,
`AB_STEALTH`, and `AB_CHROME`. `AB_HTTP_CAPABILITY` protects HTTP/SSE requests
with `X-Browser-Capability` and is required for non-loopback binds.

Managed hosts (see [Managed mode](#managed-mode-secure-multi-tenant-hosting)
above) can set:

| Variable | Purpose |
|---|---|
| `AB_MANAGED=1` | Turns on per-owner capability auth for `/sse` and `/mcp` |
| `AB_HTTP_CAPABILITY=` | Root secret the host derives per-owner tokens from |
| `AB_SPAWN_NONCE=` | Reported on `/health` so the host can confirm which process instance is live |
| `AB_ALLOWED_TOOLS=` | Fail-closed allowlist of callable/visible `browser_*` tool names |
| `AB_SECRET_BROKER_SOCKET`, `AB

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [maestrojeong](https://github.com/maestrojeong)
- **Source:** [maestrojeong/browser-rs-mcp](https://github.com/maestrojeong/browser-rs-mcp)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-maestrojeong-browser-rs-mcp
- Seller: https://agentstack.voostack.com/s/maestrojeong
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
