AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Nekoro Browser

mcp-zeshuochen-nekoro-browser · by zeshuochen

Lightweight browser automation CLI + MCP server driving your everyday Chrome via the extension chrome.debugger API — keeps your login state, no --remote-debugging-port

No reviews yet
0 installs
18 views
0.0% view→install

Install

$ agentstack add mcp-zeshuochen-nekoro-browser

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-zeshuochen-nekoro-browser)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Nekoro Browser? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Let your AI coding tool drive your own Chrome — open pages, click, read them back, still logged in everywhere.

Quick Start · Examples · Compare · MCP · API · Architecture · Site Knowledge · Limitations · Reference


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.

> [!NOTE] > Install is one uv 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

Rather have your AI do it? Paste this into Claude Code / Cursor / opencode

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-browser

No uv? pipx install nekoro-browser works too.

From source: git clone https://github.com/zeshuochen/nekoro-browser && cd nekoro-browser && uv pip install -e .

> [!WARNING] > 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 setup

Copies the extension directory to your clipboard and waits until it connects. Meanwhile: chrome://extensions/Developer modeLoad 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-browser

4 — Drive the browser. Pick the way you actually work:

From your AI coding tool (MCP) — one command for Claude Code, other clients in [MCP](#mcp-any-mcp-client):

claude mcp add nekoro-browser -- nekoro-browser-mcp

Restart 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.


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 
On Windows? <<'PY' is bash-only — PowerShell equivalent

```powershell
@'
await new_tab("https://example.com")
print((await page_info())["title"])
'@ | nekoro-browser

The 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 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**

```bash
claude mcp add nekoro-browser -- nekoro-browser-mcp

Claude 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/claudedesktopconfig.json · Windows %APPDATA%\Claude\claudedesktopconfig.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, and exec_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 as isError, never dressed up as success.
  • Navigating to a site you have notes or scripts for ships them in the tool result — see

[Site Knowledge](#self-healing-and-site-knowledge).

API

| Category | Commands | |----------|----------| | Navigation | navigate(url), new_tab(url), ensure_tab(url), new_tab(url, reuse=True), list_tabs(), switch_tab(id), close_tab(id), close_tabs(ids), sweep_tabs() | | Page info | page_info(), page_html(), page_text(), get_markdown(), state(), refs(), find_text(t), iframe_target(url_substr) | | JavaScript | js(code), cdp(method, **p), cdp_batch(*cmds) | | Interaction | click(loc), click(loc, tab=id), click_selector(sel), click_ref(ref), click_index(n), click_at_xy(x,y), type_text(t), fill_input(sel,t), press_key(k), upload_file(sel,path) | | Dialogs | dialog_off(), get_last_dialog() | | Waiting | wait_for_load(), wait_selector(sel), wait_for_network_idle(), sleep(s) | | Downloads | wait_for_download() | | Screenshots | capture_screenshot(), capture_screenshot(scale="device"), capture_screenshot("jpeg", 90) |

All page-level helpers take an optional tab= (default: the active tab). capturescreenshot defaults to scale="css" — pixel size equals the CSS viewport, so coordinates can be fed straight to clickat_xy; scale="device" keeps physical pixels.


Architecture

flowchart TD
    A["Chrome tab — your profile, your logins"]
    B["Extension background.jschrome.debugger / CDP"]
    C["Python daemon127.0.0.1:28417"]
    D["CLInekoro-browser"]
    E["MCP servernekoro-browser-mcp"]

    A |CDP| B
    B |persistent WebSocket| C
    D -->|"HTTP /exec · token auth"| C
    E -->|"HTTP /exec · token auth"| C

Same diagram as plain text (for renderers without Mermaid, e.g. PyPI)

Chrome 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_scripts heartbeat (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.py is 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 to domain-skills/ in the repo), one folder per site holding both kinds of material: /*.md for knowledge and /*.py for workflows. Scripts are loaded into the /exec namespace 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 --doctor reports Extension/SW not responding, re-enable it in chrome://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 --doctor and retry.
  • Everything is anchored to one active tab. 16 helpers (click, click_selector, state, wait_selector, fill_input, …) take an explicit tab=id to 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. Both Browser.setDownloadBehavior (-32601) and the deprecated Page.setDownloadBehavior (-32000 "Cannot not access browser-level commands") are browser-level and get rejected under chrome.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 (including ping) queues behind it. Open separate client connections if you need concurrency.

Reference

CLI flags, configuration, troubleshooting, security — click to expand

CLI

| Command | What it does | |---------|---------------| | nekoro-browser | Start the daemon (foreground) | | nekoro-browser setup | Guided install: copies the extension path, then waits until the extension actually connects | | nekoro-browser --doctor | End-to-end diagnostic (daemon + extension + SW all alive?) | | nekoro-browser --stop | Stop the daemon | | nekoro-browser --restart | Stop and restart (foreground) | | nekoro-browser --reload-ext | Reload the extension's service worker — required after upgrading, also useful before a batch job for a clean state | | nekoro-browser --extension-path | Print the extension directory (for "Load unpacked") | | nekoro-browser --version | Print the installed version (check it against the extension you loaded) | | nekoro-browser --port N | Run the daemon on port N (default 28417) | | nekoro-browser -c "code" | Run one snippet, print the result | | nekoro-browser --timeout N | Seconds to allow a snippet (default 120 — page loads are slow) | | nekoro-browser --allow-domains "jd.com,*.taobao.com" | Only allow these domains (comma-separated); unset = unrestricted | | echo "code" \| nekoro-browser | Pipe mode (daemon must already be running) |

Configuration

The daemon listens on 28417 by default. To change it:

| Side | How | |------|-----| | Python (daemon + CLI + MCP) | nekoro-browser --port 30500, or set NEKORO_PORT=30500 | | 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 /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. `NEKORODATADI

Source & license

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

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

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.