A Rust-native MCP server that gives AI agents full control over a real Chrome browser through the Chrome DevTools Protocol. No browser extensions, no Puppeteer, no Node.js ā just a single binary that speaks CDP.
Benchmarks
_See dev-browser-eval for methodology. The model used was sonnet 4.5
Why remix-browser?
Installation
Claude Code Plugin (recommended)
/plugin marketplace add hkd987/remix-browser
/plugin install remix-browser@hkd987-remix-browser
That's it ā the binary downloads automatically on first use. No Rust required.
Download pre-built binary
curl -fsSL https://raw.githubusercontent.com/hkd987/remix-browser/main/scripts/install.sh | sh
From source
git clone https://github.com/hkd987/remix-browser.git
cd remix-browser
cargo build --release
Requirements
- Google Chrome or Chromium installed (auto-detected)
- Rust 1.88+ only needed if building from source
Quick Start
Add to Claude Code
Add to your Claude Code MCP config (~/.claude/mcp.json):
{
"mcpServers": {
"remix-browser": {
"command": "/path/to/remix-browser"
}
}
}
That's it. Claude now has a browser.
Headed mode (see what's happening)
{
"mcpServers": {
"remix-browser": {
"command": "/path/to/remix-browser",
"args": ["--headed"]
}
}
}
Connect to your existing browser
Chrome 144+ has a built-in toggle that lets any agent connect to your running browser ā no extensions needed.
- Open
chrome://inspect/#remote-debuggingin Chrome - Enable the remote debugging toggle
- Connect remix-browser:
{
"mcpServers": {
"remix-browser": {
"command": "/path/to/remix-browser",
"args": ["--cdp-url", "ws://127.0.0.1:9222"]
}
}
}
Or use an environment variable:
{
"mcpServers": {
"remix-browser": {
"command": "/path/to/remix-browser",
"env": {
"CDP_URL": "ws://127.0.0.1:9222"
}
}
}
}
HTTP URLs also work ā the WebSocket URL is auto-discovered from /json/version:
CDP_URL=http://127.0.0.1:9222 remix-browser
When connected to an external browser, remix-browser uses your existing tabs and gracefully disconnects on exit without closing Chrome.
Best performance tip
For the best experience, add this line to your project's CLAUDE.md (or ~/.claude/CLAUDE.md for all projects):
When I ask to use Chrome or browser automation, use remix-browser MCP tools.
For 1-2 simple actions, granular tools are fine.
For workflows with 3+ actions, loops, or extraction, prefer `run_script`.
Use `fill` for setting any form control ā it auto-detects input type (text, select, checkbox, range slider).
Snapshots auto-append after every tool call, so [ref=eN] selectors are always fresh.
This tells Claude to automatically reach for remix-browser whenever you mention browser tasks ā no need to say "remix-browser" by name.
Performance Usage Pattern
- Use granular tools for short flows (
navigate->click->get_text). - Use
run_scriptfor multi-step workflows, loops, and repeated extraction. - Use
fillinstead oftype_text+select_optionā it auto-detects the control type (text, select, checkbox, range slider, ARIA slider). - Snapshots auto-append after every tool call, so
[ref=eN]selectors are always available without a separatesnapshotcall. - All interaction tools (
click,type_text,fill) include auto-wait ā they poll up to 5 seconds for the element to appear before acting, eliminating timing errors on dynamic pages.
Tools
remix-browser exposes a broad toolset organized by category.
Navigation
Finding Elements
Snapshot
Interaction
Screenshots
JavaScript & Console
Network Monitoring
Tab Management
Script Automation
Selector Types
All element-targeting tools support three selector strategies:
CSS (default): "button.submit", "#login-form", "div > p:first-child"
Text: "Sign In", "Submit Order", "Click here"
XPath: "//button[@type='submit']", "//div[contains(@class, 'menu')]"
Text selectors use a TreeWalker to find elements by their visible text content ā no need to inspect the DOM to find the right CSS class.
The Hybrid Click
Most browser automation tools fail on modern JS-heavy sites. Dropdown menus, overlays, dynamically positioned elements ā they all break simple element.click().
remix-browser uses a hybrid click strategy:
- Auto-wait up to 5 seconds for the element to appear in the DOM
- Scroll the element into view
- Check visibility and whether it's obscured by other elements
- Dispatch real mouse events (
mousedown->mouseup->click) at the element's coordinates - If the element is obscured (e.g., behind an overlay), automatically fall back to JavaScript
click() - Report which method was used so you know exactly what happened
This means clicks just work ā even on sites with complex overlays, sticky headers, dynamic menus, and late-loading elements.
Configuration
Chrome Detection
remix-browser automatically finds Chrome on your system:
- macOS:
/Applications/Google Chrome.app, Homebrew paths, Chrome Canary - Linux:
/usr/bin/google-chrome, snap packages, Chromium - Windows: Program Files, Local AppData
Falls back to which google-chrome / which chromium if standard paths don't exist.
Default Browser Settings
- Viewport: 1280x720
- Headless mode:
--headless=new(Chrome's latest headless implementation) - Extensions, sync, popups, and first-run prompts are all disabled for a clean automation environment
Architecture
src/
āāā main.rs # CLI entry point
āāā server.rs # MCP ServerHandler ā routes tool calls
āāā browser/
ā āāā session.rs # Browser lifecycle management
ā āāā pool.rs # Multi-tab tracking (TabPool)
ā āāā launcher.rs # Chrome binary detection & launch config
āāā tools/
ā āāā navigation.rs # navigate, go_back, go_forward, reload
ā āāā dom.rs # find_elements, get_text, get_html, wait_for
ā āāā interaction.rs # click, type_text, fill, hover, select_option, press_key, scroll
ā āāā screenshot.rs # screenshot capture
ā āāā snapshot.rs # compact interactive tree + ref generation
ā āāā javascript.rs # execute_js, console log capture
ā āāā network.rs # network monitoring
ā āāā page.rs # tab management
ā āāā script.rs # run_script JS engine and page API
āāā interaction/
ā āāā click.rs # Hybrid click strategy implementation
ā āāā keyboard.rs # Key press & text input
ā āāā scroll.rs # Scroll logic
ā āāā wait.rs # Auto-wait polling for element existence
āāā selectors/
āāā mod.rs # Selector normalization & :has-text() conversion
āāā css.rs # CSS selector resolution
āāā text.rs # Text content matching via TreeWalker
āāā xpath.rs # XPath evaluation
āāā ref.rs # [ref=eN] snapshot reference resolution
Built on:
- rmcp ā Rust MCP framework
- chromiumoxide ā CDP client for Rust
- tokio ā Async runtime
Testing
# Run all 50 integration tests (uses real headless Chrome)
cargo test -- --test-threads=4
# Run a specific test
cargo test test_navigate
Tests use fixture HTML files and spin up isolated Chrome instances with unique profiles ā no shared state, no flakiness.
License
MIT