nekoro-browser-mcp

nekoro-browser-mcp

MCP server for browser automation that drives Chrome via an extension, preserving login state and offering 45 tools for navigation, interaction, scraping, and screenshots.

Category
访问服务器

README

<p align="center"> <img src="https://raw.githubusercontent.com/zeshuochen/nekoro-browser/master/docs/banner.svg" width="820" alt="nekoro-browser — browser automation CLI + MCP server"> </p>

<p align="center"> <a href="https://github.com/zeshuochen/nekoro-browser/actions/workflows/tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/zeshuochen/nekoro-browser/tests.yml?branch=master&style=flat-square&label=tests" alt="tests"></a> <a href="https://pypi.org/project/nekoro-browser/"><img src="https://img.shields.io/pypi/v/nekoro-browser?style=flat-square" alt="PyPI"></a> <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/pypi/pyversions/nekoro-browser?style=flat-square" alt="Python versions"></a> <a href="https://github.com/zeshuochen/nekoro-browser/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="MIT License"></a> <a href="#mcp-any-mcp-client"><img src="https://img.shields.io/badge/MCP-supported-8A2BE2?style=flat-square" alt="MCP supported"></a> </p>

<p align="center"> Lightweight browser automation CLI + MCP server. Drives your everyday Chrome through an extension — <b>keeps your login state</b>, <b>no debug port</b>, <b>no banners</b>.<br> <sub><a href="https://github.com/zeshuochen/nekoro-browser/blob/master/README.zh-CN.md">中文</a></sub> </p>

<p align="center"> <a href="#quick-start">Quick Start</a> · <a href="#examples">Examples</a> · <a href="#mcp-any-mcp-client">MCP</a> · <a href="#api">API</a> · <a href="#architecture">Architecture</a> · <a href="#self-healing-and-site-knowledge">Site Knowledge</a> · <a href="#known-limitations">Limitations</a> · <a href="#reference">Reference</a> </p>

uv tool install nekoro-browser && nekoro-browser setup   # install, then load the extension
nekoro-browser                                           # daemon — its own terminal, leave open
echo "page_info()" | nekoro-browser                      # drive the Chrome you're logged into

Why Not --remote-debugging-port?

Since Chrome 136, --remote-debugging-port / --remote-debugging-pipe refuse the default profile — you must point Chrome at a non-default --user-data-dir, i.e. a clean instance with none of your logins. An extension's chrome.debugger is not subject to that restriction, which is why nekoro goes through an extension.

CDP WebSocket playwright-cli opencli nekoro-browser
Approach --remote-debugging-port Playwright extension OpenCLI extension Custom extension + persistent WebSocket
Install one flag npm i -g (~200MB) npm / desktop app uv tool install (stdlib only, zero deps)
Login state ❌ fresh instance ✅ ✅ ✅
Modify the extension — Edit Playwright source Edit OpenCLI source ✅ right in this repo
Self-healing ❌ ❌ ❌ ✅ Agent edits helpers at runtime
MCP ❌ ✅ (separate @playwright/mcp) ❌ ✅ built in, 46 tools via nekoro-browser-mcp
Site knowledge ❌ ❌ ❌ ✅ your notes and scripts are handed to the agent on navigate

Quick Start

1 — Install (Python 3.12+, zero third-party dependencies)

uv tool install nekoro-browser

Installs both commands (nekoro-browser, nekoro-browser-mcp) into their own environment, so nothing lands in your system Python. No uv? pipx install nekoro-browser works the same way.

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

2 — Load the extension

nekoro-browser setup

setup copies the extension directory to your clipboard and then waits — up to three minutes — until the extension actually connects, so you find out it worked instead of guessing. Meanwhile you do the part Chrome reserves for humans: open chrome://extensions/, turn on Developer mode, click Load unpacked, paste the directory.

3 — Start the daemon — give it its own terminal and leave it open; it runs in the foreground and closing that window stops it

nekoro-browser

4 — Drive the browser from anywhere else

echo "page_info()" | nekoro-browser
# → {"ok": true, "result": {"title": "...", "url": "..."}}

That's it. If step 4 says the daemon isn't running, or a command times out, run nekoro-browser --doctor — it checks the daemon, the extension and the service worker separately and tells you which one is down.

Examples

Send a multi-step flow in one shot with a heredoc. Every helper is a top-level await:

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

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)
PY

All helpers are documented in SKILL.md.


MCP (any MCP client)

Every function in helpers.py is reflected into an MCP tool (46 today) — no glue code.

Prerequisite: the daemon must be running (nekoro-browser, its own terminal). The MCP server is a thin forwarder — it talks to that daemon over the same authenticated path as echo ... | nekoro-browser, and the daemon is what 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-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" } } }

<sub>Claude Desktop config file: macOS <code>~/Library/Application Support/Claude/claude_desktop_config.json</code> · Windows <code>%APPDATA%\Claude\claude_desktop_config.json</code></sub>

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

What you get beyond the tool list: two escape hatches ship as tools — cdp (raw CDP command) and exec_python (arbitrary Python in the daemon namespace, so a whole multi-step flow costs one round trip). Screenshots come back as image content so clients render them inline. A helper's own failure ({"ok": false}) is surfaced as isError instead of being dressed up as success. And when you navigate to a site you have notes or scripts for, they ride along in the tool result — see Self-Healing and Site Knowledge.

API

Category Commands
Navigation navigate(url), new_tab(url), list_tabs(), switch_tab(id), close_tab(id)
Page info page_info(), page_html(), page_text(), get_markdown(), state()
JavaScript js(code), cdp(method, **p), cdp_batch(*cmds)
Interaction click_selector(sel), 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)
Screenshots capture_screenshot(), capture_screenshot("jpeg", 90)

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"| C

<details> <summary>Same diagram as plain text (for renderers without Mermaid, e.g. PyPI)</summary>

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)

</details>

helpers.py (47 thin wrappers) → CDP commands, each ≤10 lines, none of them aware of any particular website.

lifecycle.py manages the daemon: pid file + process fingerprint (avoids killing a reused pid), self-heal on stale daemon (CDP probe fails → auto cleanup and restart), localhost requests bypass the system proxy.

The extension is hardened against MV3 service worker eviction: a content_scripts heartbeat (an independent wake vector living in the page, reconnects and wakes the SW even after it's killed) + onStartup (reconnects instantly on Chrome cold start) + reattaches the last-driven tab after a restart instead of drifting to a blank tab.

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: <site>/*.md for knowledge and <site>/*.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 — full text on every navigation would turn a one-time write into a permanent read cost. actions lists functions that are already callable, so the agent runs one instead of rebuilding the flow. list_site_actions() shows everything loaded, including files that failed to load. Conventions for what to record — and what not to — are in domain-skills/README.md.


Platform Support

Platform Status
Windows Primary development platform, exercised end to end
Linux / macOS The code has the branches (XDG dirs, chmod 600 token, /proc and ps liveness probes) and CI runs the unit tests on all three, but the full "Chrome + extension" loop has never been run on a real macOS/Linux box — reports welcome

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.
  • One active tab at a time. Tabs can be listed and switched (list_tabs / switch_tab), but commands always go to the current active tab — there are no parallel sessions.
  • 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

<details> <summary><b>CLI flags, configuration, troubleshooting, security</b> — click to expand</summary>

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 — run before a batch job for a clean state
nekoro-browser --extension-path Print the extension directory (for "Load unpacked")
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)
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 <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.

Troubleshooting

Symptom Cause Fix
Daemon not running Daemon not started Run nekoro-browser in terminal 1
CDP timeout Extension not connected / service worker asleep nekoro-browser --doctor to diagnose; try --reload-ext or manually reload in chrome://extensions
Extension disabled by Chrome Unpacked extension + Chrome update Re-enable it in chrome://extensions/, then re-run --doctor
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 nekoro-browser --stop

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\token, chmod 600 on POSIX). Clients read it and send X-Nekoro-Token; missing/wrong token → 403. Web pages and remote hosts can't read local files, so they can't obtain it. /ping stays open.
  • Extension → daemon (/ws): the handshake Origin must be chrome-extension://…; a web page's WebSocket to 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.

</details>


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 access
  • browser-act — state() indexed element tree, *[N] change markers, waitSelector() state polling, getMarkdown() page extraction
  • Playwright — CDP Input.dispatchMouseEvent real mouse events (isTrusted:true), extension + daemon dual-path architecture

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选