Mare Browser
Lean, LLM-first browser automation MCP server. Gives Claude, OpenCode, or any MCP client a real browser to navigate, interact with, and debug web apps.
Ask AI about Mare Browser
Powered by Claude Β· Grounded in docs
I know everything about Mare Browser. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
mare-browser-mcp
A lean, LLM-first browser automation MCP server. Gives Claude (or any MCP client) a real Chromium browser to navigate, interact with, and debug web apps β without the overhead of raw Playwright APIs.
Built with Playwright + MCP SDK. One server = one browser session = one LLM.
Free to use. If it saves you time, buy me a coffee β
Install (recommended)
Prerequisites: Node.js 18+, pnpm
git clone https://github.com/emadklenka/mare_browser_mcp
cd mare_browser_mcp
pnpm install
npx playwright install chromium
This is the fastest way to run the server β starts instantly with no registry lookups.
Alternative installs
Global install β no cloning, still fast:
pnpm add -g mare-browser-mcp
npx playwright install chromium
Register with Claude Code
If you cloned the repo, the setup script does it for you:
pnpm run setup
That's it. The script detects the correct path automatically and registers the MCP with Claude Code. Restart Claude Code and the browser tools are ready.
Manual config β add to ~/.claude.json under mcpServers:
{
"mcpServers": {
"mare-browser": {
"command": "node",
"args": ["/absolute/path/to/mare_browser_mcp/src/index.js"],
"env": { "HEADLESS": "false" }
}
}
}
If installed globally:
{
"mcpServers": {
"mare-browser": {
"command": "mare-browser-mcp",
"env": { "HEADLESS": "false" }
}
}
}
Register with OpenCode
Add this to ~/.config/opencode/opencode.json (global) or opencode.json (project root):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mare_browser_mcp": {
"type": "local",
"command": [
"node",
"/absolute/path/to/mare_browser_mcp/src/index.js"
]
}
}
}
If installed globally:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mare_browser_mcp": {
"type": "local",
"command": ["mare-browser-mcp"]
}
}
}
Tools
browser_navigate(url, clear_logs?)
Navigate to a URL. Pass clear_logs: true when starting a new task to wipe stale console/network/dialog history.
browser_act(commands[])
Run a sequence of actions in one call. Supported actions:
| action | required params | optional params | what it does |
|---|---|---|---|
click | selector | button (left/right/middle) | Click an element. Use button: "right" for context menus |
hover | selector | Hover over an element β triggers tooltips, dropdown menus, hover states | |
drag | selector | target or offsetX/offsetY | Drag an element to another element (target) or by pixel offset (for resizing, sliders) |
clicklink | text | Click a link/button by its visible text | |
fill | selector, value | Type into an input (clears first) | |
select | selector, value | Select a dropdown option | |
keypress | key | Press a key (e.g. Enter, Tab, Escape) | |
waitfor | selector | timeout | Wait until element appears |
scrollto | selector | Scroll element into view | |
wait | ms | Pause for N milliseconds | |
clearconsole | β | Clear console log buffer |
browser_debug()
Start here when something goes wrong. Returns in one call:
- Current URL and page title
- Console logs (filterable by type:
error,warning,log,pageerror) - Network requests with: method, URL, query params, request body, request headers (auth masked), status code, response body (JSON), and
duration_mstiming - Dialog history (alert/confirm/prompt β auto-accepted, text captured)
Filter with url_filter, method_filter, console_types, or last_n.
browser_query(selector, all?, fields?, visible_only?, limit?, count_only?)
Read the DOM without a screenshot. Query any element by CSS selector.
| param | what it does |
|---|---|
all | Return all matching elements (default: first only) |
fields | Pick fields: text, value, visible, disabled, className, href, innerHTML |
visible_only | Filter to visible elements only β recommended for broad selectors |
limit | Cap the number of results (e.g. 10) to prevent huge payloads |
count_only | Just return the count β fast way to check "how many rows?" without fetching data |
browser_eval(code)
Escape hatch for anything the other tools don't cover:
- Read computed styles:
getComputedStyle(el).backgroundColor - Append text to inputs without clearing
- Type character-by-character for autocomplete
- Drag-and-drop via manual DOM events
- Call
fetch()to hit APIs directly - Read JS app state (
window.__store__, etc.) - Check CSS visibility (
display,opacity,visibility)
browser_scroll(direction?, pixels?, selector?, container?)
Three modes:
- Page scroll:
direction: "down", pixels: 500 - Scroll into view:
selector: ".my-element" - Scroll within a container:
container: ".ag-body-viewport", direction: "down", pixels: 300β for scrollable divs, grid viewports, chat panels
browser_wait_for_network(url_pattern?, method?, timeout?)
Wait for a specific network response after triggering an action β smarter than guessing with wait.
browser_screenshot()
Returns a PNG screenshot. Use as a last resort β prefer browser_debug and browser_query first.
browser_upload(selector, files[])
Upload files to a file input element.
browser_restart(url?)
Kill the browser and start fresh. Clears all logs. Optionally navigate to a URL after restart.
browser_emulate_device(device, orientation?, custom?)
Switch the browser into a device profile for responsive QA. Emulation persists across navigations until you swap devices or call browser_restart.
Presets (natural portrait viewport):
iphone-15-pro-max(430Γ932),iphone-15-pro(393Γ852),iphone-15(393Γ852),iphone-se(375Γ667)galaxy-s24(360Γ800)ipad-pro-13(1024Γ1366),ipad-pro-11(834Γ1194),ipad-mini(768Γ1024)galaxy-tab-s9(800Γ1280)desktop-chrome(1280Γ800) β resets to desktopcustomβ requirescustom.userAgent+custom.viewport.{width, height}
Swapping devices recreates the browser context, so cookies and localStorage are lost and auth'd pages may land on login. innerWidth: 980 on a mobile emulation viewing a page without <meta name="viewport"> is Chrome's legacy fallback, not a bug β pointer_coarse, hasTouch, and userAgent are the authoritative signals. browser_debug surfaces the active emulation under an emulation field.
Example workflow
1. browser_navigate("https://myapp.com", clear_logs: true)
2. browser_act([
{ action: "fill", selector: "#email", value: "user@example.com" },
{ action: "fill", selector: "#password", value: "secret" },
{ action: "click", selector: "button[type=submit]" }
])
3. browser_wait_for_network({ url_pattern: "/api/session", method: "POST" })
4. browser_debug({ console_types: ["error"] }) <- check for login errors
5. browser_query(".dashboard-title") <- confirm we're logged in
Hover + tooltip example
1. browser_act([{ action: "hover", selector: ".info-icon" }])
2. browser_query(".tooltip", { fields: ["text", "visible"] })
Drag-and-drop example
// Reorder columns
browser_act([{ action: "drag", selector: ".col-name", target: ".col-age" }])
// Resize a column by 100px
browser_act([{ action: "drag", selector: ".resize-handle", offsetX: 100, offsetY: 0 }])
Right-click context menu
1. browser_act([{ action: "click", selector: ".grid-row", button: "right" }])
2. browser_query(".context-menu-item", { all: true, fields: ["text"] })
Scroll inside a container
browser_scroll({ container: ".ag-body-viewport", direction: "down", pixels: 500 })
Count elements quickly
browser_query({ selector: ".ag-row", count_only: true })
// -> { selector: ".ag-row", count: 47 }
Emulate a mobile device
1. browser_emulate_device({ device: "iphone-15-pro-max" })
2. browser_navigate({ url: "https://www.youtube.com" })
// redirects to m.youtube.com because of the iPhone UA
3. browser_screenshot() // mobile layout
4. browser_emulate_device({ device: "ipad-pro-13", orientation: "landscape" })
5. browser_emulate_device({ device: "desktop-chrome" }) // reset
Environment
| Variable | Default | Description |
|---|---|---|
HEADLESS | false | Run browser headless (true) or visible (false) |
REAL_CHROME | false | Use your installed Chrome instead of Playwright's Chromium |
CHROME_PROFILE | Default | Chrome profile name (when REAL_CHROME=true) |
The browser launches lazily β it won't open until the first tool call.
License
MIT β free to use, modify, and distribute.
If this project helps you, buy me a coffee β
