nekoro-browser-mcp
The nekoro-browser-mcp server provides comprehensive browser automation via a Chrome extension, allowing control of your everyday Chrome browser with persistent login state. It offers over 50 tools for navigation, interaction, content extraction, JavaScript/CDP execution, screenshot capture, and more.
Navigation & Tabs: Navigate to URLs, manage tabs (open, close, switch, list, sweep duplicates), and ensure a real tab is active.
Content Extraction: Get page metadata (title, URL), full HTML, visible text, Markdown conversion, and an indexed interactive element tree for easy model interaction. Find text and element bounding boxes.
Interaction: Click elements by selector, index, coordinates, or text; type text; fill form inputs; press keys; hover; upload files; scroll pages via various methods.
Scripting & Protocol Access: Execute arbitrary JavaScript in the page (
js), send raw Chrome DevTools Protocol commands (cdp), and run multi-step Python code in the daemon namespace (exec_python).Screenshots: Capture the current view as PNG or JPEG with quality options.
Waiting & Synchronization: Wait for page load, specific selectors, network idle, or fixed delays.
Network & Cookies: Enable network monitoring, retrieve response bodies, get/set cookies, and perform HTTP GET requests.
Dialogs: Automatically suppress JavaScript dialogs and retrieve information about the last intercepted dialog.
Site Knowledge & Self-Healing: Dynamically load site-specific scripts and notes, reload agent helpers and extension at runtime without restarting the daemon.
Advanced Utilities: Work with iframes (
iframe_target), drain CDP events, and ensure automation reliability.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nekoro-browser-mcpOpen my Gmail inbox and show me the latest unread emails."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nekoro-browser drives the Chrome you already use — logins, cookies, sessions, all intact.
Other automation tools spawn a fresh browser: no logins, nothing works. nekoro-browser just adds a small extension: same profile, no second instance, no "controlled by automated software" banner.
Install is oneuv tool install — Python stdlib only, no bundled engine, no 200MB download.
Every helper is reflected into an MCP tool (53 of them), so Claude Code, Cursor, Cline, opencode, Codex and VS Code/Copilot can drive the browser directly. Bring any model — no subscription, no lock-in. MIT, extension source included.
Quick Start
Install nekoro-browser for me:
1. `uv tool install nekoro-browser` (no uv → `pipx install nekoro-browser`).
2. Run `nekoro-browser setup` and show me the extension path it prints. This step is mine:
I open chrome://extensions, turn on Developer mode, click Load unpacked, paste that
path. Wait until I say it's loaded — you cannot click this for me.
3. Then start the daemon in a separate terminal that stays open: `nekoro-browser`.
4. Last step depends on how I'll use it — ask me which:
- from my AI editor → register the MCP server (`claude mcp add nekoro-browser --
nekoro-browser-mcp`, or the equivalent config for my client), then I restart it;
- from the terminal only → nothing to do, `echo "page_info()" | nekoro-browser` works.
5. Finish with `nekoro-browser --doctor` and tell me if daemon / extension / service
worker are all green.1 — Install (Python 3.12+, zero third-party dependencies)
uv tool install nekoro-browserNo uv? pipx install nekoro-browser works too.
From source: git clone https://github.com/zeshuochen/nekoro-browser && cd nekoro-browser && uv pip install -e .
Upgrading? uv tool upgrade nekoro-browser only updates the Python side — reload
the extension afterwards: nekoro-browser --reload-ext (or Reload on the card in
chrome://extensions).
2 — Load the extension
nekoro-browser setupCopies the extension directory to your clipboard and waits until it connects. Meanwhile:
chrome://extensions/ → Developer mode → Load unpacked → paste.
3 — Start the daemon — open a second terminal and leave it running (it's the background process that holds the Chrome connection; close it and everything stops)
nekoro-browser4 — Drive the browser. Pick the way you actually work:
From your AI coding tool (MCP) — one command for Claude Code, other clients in MCP:
claude mcp add nekoro-browser -- nekoro-browser-mcpRestart the client and ask it to open a page. That's it — 53 browser tools show up.
From the terminal — pipe a snippet to the running daemon:
echo "page_info()" | nekoro-browser
# → {"ok": true, "result": {"title": "...", "url": "..."}}Something down? nekoro-browser --doctor checks daemon / extension / service worker
and tells you which one.
Related MCP server: Browser Automation MCP
Examples
Send a multi-step flow in one shot. Every helper is already await-able at top level — no
asyncio boilerplate, no imports:
nekoro-browser <<'PY'
await new_tab("https://example.com")
print((await page_info())["title"]) # Example Domain
print((await get_markdown(max_chars=200))["result"])
print((await state(max_items=3))["result"]) # indexed interactive elements, model-ready
await close_tab()
PY@'
await new_tab("https://example.com")
print((await page_info())["title"])
'@ | nekoro-browserThe closing '@ must sit at the start of its own line. One-liners:
nekoro-browser -c "await navigate('https://example.com')".
Not cmd.exe — its echo keeps the quotes, so the snippet arrives as a string and comes back
{"ok": true, "result": "page_info()"} with the browser untouched.
state() numbers the elements and click_index(n) clicks by number — the model never has to guess a CSS selector:
nekoro-browser <<'PY'
await navigate("https://github.com/search?q=browser+automation&type=repositories")
await wait_for_load()
print((await state(max_items=40))["result"])
await click_index(12)
PYAll helpers are documented in SKILL.md.
How It Compares
CDP WebSocket | playwright-cli | opencli | nekoro-browser | |
Approach |
| Playwright ext. | OpenCLI ext. | Custom ext. + WS |
Install | one flag |
| npm / desktop |
|
Login state | ❌ fresh instance | ✅ | ✅ | ✅ |
Modify ext. | — | edit source | edit source | ✅ this repo |
Self-healing | ❌ | ❌ | ❌ | ✅ agent edits at runtime |
MCP | ❌ | ✅ separate pkg | ❌ | ✅ built-in, 53 tools |
Site knowledge | ❌ | ❌ | ❌ | ✅ notes auto-attached |
Why row 3 is ❌: since Chrome 136, --remote-debugging-port refuses the default profile — a raw CDP connection means a fresh instance with none of your logins. An extension's chrome.debugger is exempt.
MCP (any MCP client)
MCP is how Claude Code, Cursor and friends call outside tools. Hook it up once and the
model gets navigate, click_index, get_markdown… as first-class tools.
Prerequisite: the daemon is running (nekoro-browser, its own terminal) — the MCP server
is a thin forwarder, the daemon owns the Chrome connection.
The command to register is always nekoro-browser-mcp. Only the config shape differs:
Claude Code
claude mcp add nekoro-browser -- nekoro-browser-mcpClaude Desktop (Settings → Developer → Edit Config) · Cursor (~/.cursor/mcp.json,
or .cursor/mcp.json for one project) · Cline (MCP Servers → Configure MCP Servers)
{ "mcpServers": { "nekoro-browser": { "command": "nekoro-browser-mcp" } } }Claude Desktop config file: macOS ~/Library/Application Support/Claude/claude_desktop_config.json · Windows %APPDATA%\Claude\claude_desktop_config.json
opencode (opencode.json) — note command is an array, and the key is mcp
{ "mcp": { "nekoro-browser": { "type": "local", "command": ["nekoro-browser-mcp"], "enabled": true } } }Codex (~/.codex/config.toml, or codex mcp add nekoro-browser -- nekoro-browser-mcp)
[mcp_servers.nekoro-browser]
command = "nekoro-browser-mcp"VS Code / Copilot (.vscode/mcp.json, or MCP: Open User Configuration) — the key is
servers, not mcpServers
{ "servers": { "nekoro-browser": { "command": "nekoro-browser-mcp" } } }Prefer not to install anything up front? Replace the command with uvx, which fetches and
runs on demand the way npx -y does — e.g. "command": "uvx", "args": ["--from", "nekoro-browser", "nekoro-browser-mcp"]. That only removes the install step for the MCP
server; the daemon still has to be installed and running.
Restart the client afterwards. If the tools don't show up, run nekoro-browser --doctor
first — a dead daemon looks exactly like a broken MCP config — then check the client's MCP
log (Claude Desktop keeps them in ~/Library/Logs/Claude on macOS, %APPDATA%\Claude\logs
on Windows).
Beyond the tool list:
cdp— raw CDP command, andexec_python— arbitrary Python in the daemon namespace, so a whole multi-step flow costs one round trip.Screenshots return as image content; clients render them inline.
A helper failure (
{"ok": false}) surfaces asisError, never dressed up as success.Navigating to a site you have notes or scripts for ships them in the tool result — see Site Knowledge.
API
Category | Commands |
Navigation |
|
Page info |
|
JavaScript |
|
Interaction |
|
Dialogs |
|
Waiting |
|
Downloads |
|
Screenshots |
|
All page-level helpers take an optional tab= (default: the active tab). capture_screenshot defaults to scale="css" — pixel size equals the CSS viewport, so coordinates can be fed straight to click_at_xy; scale="device" keeps physical pixels.
Architecture
flowchart TD
A["Chrome tab — your profile, your logins"]
B["Extension background.js<br/>chrome.debugger / CDP"]
C["Python daemon<br/>127.0.0.1:28417"]
D["CLI<br/>nekoro-browser"]
E["MCP server<br/>nekoro-browser-mcp"]
A <-->|CDP| B
B <-->|persistent WebSocket| C
D -->|"HTTP /exec · token auth"| C
E -->|"HTTP /exec · token auth"| CChrome extension (background.js) —— chrome.debugger / CDP
↕ persistent WebSocket
Python daemon (127.0.0.1:28417)
↕ HTTP /exec (token auth)
CLI (nekoro-browser) · MCP server (nekoro-browser-mcp)helpers.py— 54 helpers (53 exposed as MCP tools), none aware of any particular website.lifecycle.py— pid file + process fingerprint (never kills a reused pid), stale-daemon self-heal (CDP probe fails → cleanup and restart), localhost bypasses the system proxy.Extension, against MV3 service worker eviction —
content_scriptsheartbeat (wake vector living in the page, revives a killed SW) +onStartup(reconnects on Chrome cold start) + reattaches the last-driven tab instead of drifting to a blank one.
Self-Healing and Site Knowledge
When an agent hits a gap it writes the missing piece and uses it immediately — nothing is recompiled, no daemon restart, no extension reload.
src/nekoro_browser/agent_helpers.pyis scratch paper: reloaded on every/exec, good for a quick experiment. It lives inside the installed package, so an upgrade overwrites it.Anything worth keeping goes in your own skills directory (
NEKORO_DOMAIN_SKILLS, falling back todomain-skills/in the repo), one folder per site holding both kinds of material:<site>/*.mdfor knowledge and<site>/*.pyfor workflows. Scripts are loaded into the/execnamespace on every call and can use the built-in helpers directly.
The point is that this material finds the agent instead of waiting to be discovered.
navigate() and new_tab() return two extra fields when the site has any:
{'ok': True, 'loaded': True,
'notes': ['example/search.md — Example — search results'],
'actions': ['open_first_result(query) — search and open the top hit']}notes lists titles only; actions lists functions that are already callable, so the agent
runs one instead of rebuilding the flow. list_site_actions() shows everything loaded,
failed files included. What to record — and what not to — is in
domain-skills/README.md.
Tabs work the same way: a tab left over from last time still holds its login and page state,
so new_tab() adds an existing field when the managed group already has tabs for that site:
{'ok': True, 'tabId': 42, 'loaded': True,
'existing': {'hint': 'switch_tab(id) reuses an open tab, or new_tab(url, reuse=True)',
'tabs': [{'tabId': 17, 'title': 'Example Domain'}]}}The tab still opens — the field only makes reuse visible at the moment a duplicate is about
to appear; reuse=True navigates the existing one instead. Nothing is ever closed
automatically: sweep_tabs() only reports candidates (same-site duplicates, stray
about:blank), sweep_tabs(dry_run=False) / close_tabs([...]) act on them, and the active
tab is never a candidate.
Platform Support
Platform | Status |
Windows | Primary development platform, exercised end to end |
Linux / macOS | Platform branches + CI, full Chrome loop untested — reports welcome |
Linux/macOS have the platform branches (~/.config / ~/Library/Application Support
data dirs, chmod 600 token, /proc + ps liveness probes) and CI runs unit tests on all
three — but the full "Chrome + extension" loop has never run on a real macOS/Linux box.
Known Limitations
Unpacked extensions get disabled by Chrome. An extension installed via "Load unpacked" may be switched off automatically after a Chrome update or restart, or hidden behind the "Disable developer mode extensions" prompt. When
--doctorreports Extension/SW not responding, re-enable it inchrome://extensions/first. This project is not published to the Chrome Web Store, so the limitation is not going away soon.Service worker keepalive is not 100%. MV3 eviction timing is Chrome's call. The heartbeat +
onStartup+ reattach cover the vast majority of cases, but unattended long-running cron jobs should still health-check with--doctorand retry.Everything is anchored to one active tab. 16 helpers (
click,click_selector,state,wait_selector,fill_input, …) take an explicittab=idto target another already attached tab — naming a tab that is not attached is an error, never a silent fallback to the active one. The other 37 always follow the active tab, and there are still no parallel sessions: one daemon drives one Chrome, requests are serialised.Downloads land wherever Chrome is configured to put them; the path cannot be changed from here.
wait_for_download()returns{url, filename, bytes}— a filename, not a full path. Set the directory in Chrome's own settings. BothBrowser.setDownloadBehavior(-32601) and the deprecatedPage.setDownloadBehavior(-32000 "Cannot not access browser-level commands") are browser-level and get rejected underchrome.debugger, which only ever hands out a tab target.The MCP server handles requests serially. During a
wait_selector(timeout=90)every other request on that connection (includingping) queues behind it. Open separate client connections if you need concurrency.
Reference
CLI
Command | What it does |
| Start the daemon (foreground) |
| Guided install: copies the extension path, then waits until the extension actually connects |
| Self-healing readiness check: launches Chrome if it isn't running, starts the daemon in the background if it isn't up, reloads the service worker if it isn't answering. Exit 0 only when all green — run this before a task instead of doing the steps by hand. It never starts a second daemon on a port that is already held; it reports the pid and stops |
| End-to-end diagnostic (daemon + extension + SW all alive?) — reports only, repairs nothing |
| Stop the daemon |
| Stop and restart (foreground) |
| Reload the extension's service worker — required after upgrading, also useful before a batch job for a clean state |
| Print the extension directory (for "Load unpacked") |
| Print the installed version (check it against the extension you loaded) |
| Run the daemon on port N (default 28417) |
| Run one snippet, print the result |
| Seconds to allow a snippet (default 120 — page loads are slow) |
| Only allow these domains (comma-separated); unset = unrestricted |
| Pipe mode (daemon must already be running) |
Configuration
The daemon listens on 28417 by default. To change it:
Side | How |
Python (daemon + CLI + MCP) |
|
Extension | Extension details → Extension options → set the port → Save (reconnects immediately, no reload) |
Both sides must agree. Clients don't need the flag repeated: the daemon records its
actual port in <data dir>/port, so a plain echo ... | nekoro-browser finds a daemon
running on a non-default port. Precedence is --port > NEKORO_PORT > that file > default.
The data dir holding token / pid / port is %LOCALAPPDATA%\nekoro-browser on Windows,
~/Library/Application Support/nekoro-browser on macOS, $XDG_CONFIG_HOME/nekoro-browser
or ~/.config/nekoro-browser elsewhere. NEKORO_DATA_DIR overrides it on any platform.
The same limit can be set via NEKORO_ALLOW_DOMAINS (comma-separated, same syntax).
Rule syntax: example.com matches exactly; *.example.com matches subdomains and the
bare domain; * allows everything. See Security below.
Troubleshooting
Symptom | Cause | Fix |
| Daemon not started | Run |
CDP timeout | Extension not connected / service worker asleep |
|
Extension disabled by Chrome | Unpacked extension + Chrome update | Re-enable it in |
Page unchanged | Extension not attached to tab | Open a regular (non-chrome://) page, restart daemon |
Port in use | Stale process | Kill the process on port 28417, or just run |
Red Errors badge on the extension card in | Daemon isn't running; the extension keeps retrying | The extension is not broken. Start the daemon ( |
Nearly everyone hits the last one: between loading the extension and starting the daemon, every reconnect logs WebSocket connection to 'ws://127.0.0.1:28417/ws' failed: ERR_CONNECTION_REFUSED. Chrome's network stack emits that message below the JS layer — the extension's try/catch and ws.onerror cannot suppress it, and probing with fetch first logs the same thing. It can be explained, not silenced.
Security
The daemon listens on 127.0.0.1 and /exec runs arbitrary Python, so the transport is guarded:
CLI / MCP → daemon (
/exec,/raw): a per-session token is written to a user-private file —%LOCALAPPDATA%\nekoro-browser\tokenon Windows,~/Library/Application Support/nekoro-browser/tokenon macOS,$XDG_CONFIG_HOMEor~/.config/nekoro-browser/tokenelsewhere,chmod 600on POSIX. Clients read it and sendX-Nekoro-Token; missing/wrong token →403. Web pages and remote hosts can't read local files, so they can't obtain it./pingstays open.Extension → daemon (
/ws): the handshakeOriginmust bechrome-extension://…; a web page'sWebSocketto localhost carries its own origin and is rejected.
Same-user local processes can read the token file — that boundary matches the OS user account, as with browser-harness's chmod 600.
Optional domain allowlist:
--allow-domains "jd.com,*.taobao.com"(orNEKORO_ALLOW_DOMAINS) gatesnavigate/new_tabto listed hosts — anything else is refused before reaching CDP. Unset = unrestricted (fail-open): this tool drives your personal Chrome, so the default stays permissive.
Feedback
Hit a problem, or missing a helper you need? Open an
issue.
For bugs, include the output of nekoro-browser --doctor, your Chrome version and OS — saves a round trip.
PRs welcome. Run the tests first: for f in tests/test_*.py; do uv run python "$f"; done (CI runs them on all three platforms too).
Acknowledgments
Core architecture derived from:
browser-harness — thin-wrapper philosophy (each function is a CDP alias, ≤10 lines), pipe mode, self-healing
agent_helpers.py, domain-skills directory structure,cdp()raw accessbrowser-act —
state()indexed element tree,*[N]change markers,waitSelector()state polling,getMarkdown()page extractionPlaywright — CDP
Input.dispatchMouseEventreal mouse events (isTrusted:true), extension + daemon dual-path architecture
Ideas drawn from:
ego-lite — "code base, not CLI base" (agent writes a script, not a command loop), unified locator syntax (
css:/text:/xpath=…) withtransient/permanentelement-resolution errors as a retry/abandon signal (→click()), "name says the intent"openOrReuseTabergonomics (→ensure_tab()), and experience-accumulation as a first-class design goal (nekoro's domain-skills already chase this)
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceAn MCP server that provides AI assistants with full control over a real browser session via a Chrome extension, supporting 36 tools for navigation, data extraction, and DOM manipulation. It bypasses bot detection by utilizing the user's active browser session, including cookies, authentication tokens, and installed extensions.133MIT
- AlicenseAqualityDmaintenanceMCP server for AI browser automation with tools for navigation, actions, data extraction, and scripting, supporting local and cloud execution.14742Apache 2.0

Browser MCPofficial
AlicenseNot gradedqualityFmaintenanceAutomate your browser with AI using a Chrome extension and MCP server, enabling logged-in sessions and stealth automation.8,9676,965Apache 2.0- FlicenseCqualityDmaintenanceEnables browser automation, including navigation, form filling, login with CAPTCHA handling, and element manipulation, using a Chrome-based MCP server.364
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Automate cloud browsers to navigate websites, interact with elements, and extract structured data.…
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/zeshuochen/nekoro-browser'
If you have feedback or need assistance with the MCP directory API, please join our Discord server