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 "Deploy 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-mcp Open 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.
An MV3 extension connects your existing Chrome profile to a local Python daemon. Use CLI snippets or built-in MCP tools from your AI client. Python 3.12+, standard library only, no bundled browser engine. MIT; extension source included.
Quick Start
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 .
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 in the background and check readiness
Open a regular webpage in Chrome, then run:
nekoro-browser --ensure
nekoro-browser --doctor--ensure starts the daemon in the background and checks a real page response.
You can close this terminal. --doctor diagnoses; --stop stops the daemon.
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:
claude mcp add nekoro-browser -- nekoro-browser-mcpRestart the client and ask it to open a page.
From the terminal — run a snippet:
nekoro-browser -c "await page_info()"
# → {"ok": true, "result": {"title": "...", "url": "..."}}Upgrading
uv tool upgrade nekoro-browser
nekoro-browser --ensure
nekoro-browser --doctorFor pipx, use pipx upgrade nekoro-browser. --ensure restarts an outdated daemon,
preserves its port and domain allowlist, and reloads the extension. Doctor must show
matching CLI, daemon and extension versions. If Chrome disabled the extension or its
path changed, re-enable/load the directory printed by nekoro-browser --extension-path
in chrome://extensions, then run --ensure again.
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"]) # every interactive element carries an index
PYThen click the one you saw — indices shift with page content, so don't copy a fixed number:
nekoro-browser -c "await click_index(7)"All helpers are documented in SKILL.md.
Where It Fits
Use it for personal browser workflows that need existing logins, a small Python runtime, CLI/MCP access, and editable site scripts. Installation requires loading an unpacked extension; screenshots can change the visible tab.
It does not provide isolated parallel browser contexts or multi-browser testing. The full Chrome loop is validated on Windows; long-running stability remains unverified.
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: run nekoro-browser --ensure first. The daemon owns the Chrome
connection; the MCP server forwards calls to it.
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 |
|
Helpers exposing tab= can target an already attached 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.
Screenshots bring the requested tab to the foreground before capture, so the visible
Chrome tab may change. A window that still has a zero-size viewport reports not_rendered.
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— general browser helpers, reflected into MCP tools.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.MV3 recovery depends on Chrome. Heartbeats,
onStartupand reattachment help restore connections. Short browser regressions cover reload and restart recovery; multi-hour unattended stability remains unverified. Run--ensurebefore a workflow and handle failures.One shared Chrome session. Helpers with
tab=idcan target another already attached tab; an unattached target is an error. Other helpers follow the active tab. Concurrent clients share this tab state and must coordinate their actions; separate browser contexts are not provided.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 |
| Launch Chrome / daemon, restart an outdated daemon, and reload an unresponsive or outdated extension. Exit 0 requires a real page response and matching component versions. It never starts a second daemon while the port is still held |
| End-to-end diagnostic, including matching CLI / running daemon / extension versions — reports only, repairs nothing |
| Stop the daemon |
| Stop and restart (foreground) |
| Reload the extension and wait for a new connection plus a real page response; disconnected or failed reloads exit nonzero. Use |
| 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 — it replaces the parent of that path; a nekoro-browser/ directory is still created inside it. So with NEKORO_DATA_DIR=/my/dir the token lives at /my/dir/nekoro-browser/token, not /my/dir/token.
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 |
| Another debugging extension owns that tab | Only one debugger per tab. Use a different tab, or disable the other extension in |
Port in use | Another process or busy daemon | Let a busy daemon finish; use |
Red Errors badge on the extension card in | Daemon isn't running; the extension keeps retrying | Run |
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).
For the real browser regression, use Chrome for Testing and Node.js (the test bridge uses Node's standard library; runtime dependencies are unchanged):
uv run --with build python tests/browser_regression.py --chrome /path/to/chromeAdd --headful to exercise an ordinary browser window. The test builds and installs
the wheel into temporary venvs, enables Developer mode in an isolated profile, and
checks cold startup, 30 navigation/input/click cycles, background-tab screenshots,
three extension reloads, MCP success/error results, browser/daemon restarts, and an
in-place upgrade from PyPI 0.3.5 that retains the domain allowlist. Port 28417 must be
free; the test fails before launching if it is occupied. Temporary browser profiles
and runtime data are removed on exit. Windows CI runs this gate before publication;
this is a short regression, not a long-running stability test.
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)
Available Tools
53 toolsbox_ofC
box_of(".btn") → {x, y, w, h, visible, tag, text}
Inputs: sel (required), tab.
capture_screenshotD
capture_screenshot() → base64 图 + 尺寸元信息。
Inputs: tab, scale, format, quality.
cdpB
Raw CDP command, e.g. method='Page.navigate', params={'url': 'https://example.com'}.
Inputs: method (required), params.
clickA
click("css:.btn") / click("text:登录") / click("index:3") /
click("xpath://button[contains(.,'登录')]") / click("placeholder:关键词")
— 统一定位点击(借鉴 ego-lite 的 locator 语法)。一个入口覆盖所有定位形式,
不用在 click_selector / click_text / click_index 之间挑;nth:N; 前缀取第 N 个
匹配(nth:2;css:.btn),只对 css/xpath/placeholder 有效——text/index 走的是
扩展 op,扩展只回第一个匹配,给了 nth 会直接报 permanent 而不是悄悄点第一个。
Inputs: loc (required), tab.
click_at_xyB
click_at_xy(100, 200) — CDP 完整鼠标点击序列 (isTrusted:true)。
tab 指定目标标签(须已 attach);不传打当前活动标签。
Inputs: x (required), y (required), tab.
click_indexC
click_index(3) — CDP 真实坐标点击 (isTrusted:true)
Inputs: index (required), tab.
click_refA
click_ref(123) — 用 backendNodeId 点击(跨轮次稳定句柄)。
Inputs: ref (required).
click_selectorC
click_selector(".btn") — CDP 真实坐标点击 (isTrusted:true)
Inputs: sel (required), tab.
click_textC
click_text("喜欢") — CDP 真实坐标点击 (isTrusted:true)
Inputs: text (required), tab.
close_tabA
close_tab(123) — 关掉指定标签;tab 省略则关当前 attached tab。 走扩展 close_tab action(chrome.tabs.remove)。关掉活动标签后扩展会自动重连到 另一可用标签、经 attach 回调同步 active_tab_id。无标签可关 → ok:false。
Inputs: tab.
close_tabsA
close_tabs([1,2,3]) — 批量关标签,逐个走 close_tab,返回 {closed, failed}。 一张关不掉不影响其余(标签可能已被用户手动关掉)。
Inputs: tabs (required).
dialog_offB
dialog_off() — JS 层覆盖 window.alert/confirm/prompt 为自动关闭(需在触发前注入, 盖不住 beforeunload / 已开的原生对话框)。想要兜底防挂用扩展的 CDP 层处置(见 get_last_dialog)——那个 attach 后一直生效、覆盖 beforeunload。
Inputs: tab.
drain_eventsA
drain_events() → list — 拉取自上次 drain 后所有缓存的 CDP 事件。 每个事件为 {method, params, sessionId, tabId}(tabId 用于按标签过滤)。
Inputs: no named parameters.
ensure_real_tabA
ensure_real_tab() — 当前 tab 是 chrome:// 等内部页时自动导航到 about:blank。 返回 {url, title}。
Inputs: no named parameters.
ensure_tabC
ensure_tab("https://example.com") — 复用优先的导航(借鉴 ego-lite 的
browser.openOrReuseTab:名字即语义,不用调用方记得传 reuse=True)。
Inputs: url (required), timeout.
exec_pythonA
Escape hatch: run arbitrary Python in the daemon namespace (all helpers pre-bound, top-level await allowed). Use when no single tool fits — e.g. multi-step flows in one round trip.
Inputs: code (required).
fill_inputA
fill_input("#email", "a@b.com") — 框架感知填值。 走 scripting:原生 value setter 写值 + 派发 input/change,React/Vue 受控组件 能收到 onChange(type_text 的 Input.insertText 常绕不过框架 setter)。 非 input/textarea/contenteditable 或找不到元素 → ok:false,不伪造成功。 自定义组件要真实键入用 click_selector + type_text。
Inputs: sel (required), text (required), tab.
find_textC
find_text("喜欢") → [{text, tag, match, w, h}]
Inputs: text (required), tab, exact, limit.
get_cookiesD
get_cookies("https://example.com")
Inputs: url.
get_last_dialogA
get_last_dialog() → {dialog} — 取最近一次被扩展自动处置的原生对话框,读后清。 attach 后扩展 Page.enable + 拦 Page.javascriptDialogOpening 立即处置(beforeunload 放行、alert/confirm/prompt 取消),防原生对话框冻结页面 JS 线程导致 evaluate 系 helper 挂死。dialog = {kind, message, url, defaultPrompt} 或 None(期间无对话框)。 仅事后观测,不能代答:需 confirm()===true 或特定 prompt 字符串的流程会被无条件取消。
Inputs: no named parameters.
get_markdownC
get_markdown() → 页面内容转 Markdown
Inputs: sel, tab, max_chars.
get_response_bodyD
get_response_body("1234.5") → CDP 网络响应体
Inputs: request_id (required).
hoverD
hover(".menu") — CSS 选择器悬停
Inputs: sel (required), tab.
hover_indexC
hover_index(3) — 悬停 state() 列表的第 N 个元素
Inputs: index (required), tab.
http_getC
http_get("https://example.com") → 纯 HTTP GET 返回 HTML 字符串。用于静态页/API。
Inputs: url (required), timeout.
iframe_targetA
iframe_target("player") → 第一个 URL 含 url_substr 的 iframe 的 frameId。
Inputs: url_substr (required).
jsA
js("document.title") — 在页面执行 JS,返回完成值。tab 不传打当前活动标签。
先按裸表达式/脚本 eval(document.title 直接返回标题);顶层 return 触发
"Illegal return statement" 时自动包进函数重试(return x 也能用)。
不可 JSON 序列化的值(Infinity/NaN/-0/BigInt)解码回 Python 值,不再吐原始 dict。
页内异常 / eval 出错 → ok:false,不伪造成功。
Inputs: code (required), tab.
list_site_actionsC
list_site_actions() → 已固化的站点函数 + 载入失败的文件。
Inputs: no named parameters.
list_tabsA
list_tabs() → nekoro 托管组里的标签 [{tabId,url,title,active,attached}]。
grouped: False 表示托管组还没建起来,这份清单其实是「所有非 chrome:// 标签」,
里面混着用户自己的标签——别拿它当「nekoro 开的标签」用(老版本扩展不带该字段)。
Inputs: no named parameters.
network_enableC
network_enable() — 启用 CDP 网络请求捕获
Inputs: no named parameters.
new_tabA
new_tab("https://example.com") — 开新标签,加入 nekoro 托管组,切过去并 attach;
reuse=True 则优先复用托管组里已有的同站标签(登录态和页面状态都还在),不新开。
Inputs: url, reuse, timeout.
page_htmlA
page_html() → 完整 HTML。tab 不传打当前活动标签。
Inputs: tab.
page_infoB
page_info() → {title, url}。tab 不传打当前活动标签。
Inputs: tab.
page_textB
page_text() → 可见文本。tab 不传打当前活动标签。
Inputs: tab.
press_keyA
press_key("Enter") / press_key("a") — 修饰键位: Alt=1 Ctrl=2 Meta=4 Shift=8。
tab 不传打当前活动标签。
特殊键(Enter/Tab/Arrow*/Backspace…)带 virtual key code,监听 e.keyCode/e.which/e.key
的页面(表单、老站)都能触发;单字符可打印键补发 char 事件(直接进 input,不走 insertText);
Alt/Ctrl/Meta 修饰时不发 char(让 Ctrl+A 走快捷键而非打出字符 'a')。
移植 browser-harness press_key。
Inputs: key (required), tab, modifiers.
refsB
refs() → [{ref, tag, text}] — 可交互元素 + 稳定句柄(CDP backendNodeId)。
tab 不传取当前活动标签。
Inputs: tab, selector, max_items.
reload_agent_helpersC
reload_agent_helpers() — 重新加载 agent_helpers.py,无需重启 daemon。
Inputs: no named parameters.
reload_extensionC
reload_extension() — 强制重载扩展(自愈用)
Inputs: no named parameters.
scroll_into_viewC
scroll_into_view("#target") — 滚动到可见
Inputs: sel, tab.
scroll_toC
scroll_to(0, 500) — 滚动页面视口到坐标 (window.scrollTo)
Inputs: x, y.
scroll_wheelC
scroll_wheel(0, 500) — CDP compositor 级 mouseWheel(能穿透 iframe/shadow DOM)。 fire-and-forget 修复后 CDP Input.dispatchMouseEvent 不再超时。
Inputs: x, y, dx, dy.
set_cookieD
set_cookie("token", "abc", domain=".example.com")
Inputs: name (required), value (required), url, path, +1 more.
sleepC
sleep(2)
Inputs: seconds (required).
stateD
state() → [{index, changed, tag, text, box}] — 索引元素树
Inputs: sel, tab, max_items.
sweep_tabsA
sweep_tabs() — 列出可清理的标签候选,默认只报不关。
Inputs: dry_run.
switch_tabC
switch_tab(123) — 把后续命令切到该标签(未 attach 则先 attach)。
Inputs: tab_id (required).
type_textC
type_text("hello") — CDP Input.insertText。tab 不传打当前活动标签。
Inputs: text (required), tab.
upload_fileA
upload_file("input[type=file]", r"C:\a.png") — 给文件输入框设文件。 走 CDP DOM.setFileInputFiles(触发 change 事件,框架能收到);path 为绝对路径, 单个 str 或多个 list。文件不存在 / 找不到元素 / 元素非 file input → ok:false,不伪造成功。 注意:作用于当前 attached tab,nodeId 经 daemon.query_selector 解析。
Inputs: sel (required), path (required).
wait_for_downloadA
wait_for_download(30) — 点击下载后等待完成(Page.downloadWillBegin/Progress 事件)。
Inputs: timeout.
wait_for_loadB
wait_for_load(15) — poll document.readyState (无 listener 泄漏)。
tab 不传等当前活动标签。
Inputs: tab, timeout.
wait_for_network_idleA
wait_for_network_idle(0.5, 15) — 等待【当前活动标签】的 Network 请求静默 idle_time 秒。 只算 active_tab_id 的事件:其它 attached 标签(后台轮询/SSE 页)的 Network 事件也进全局 缓冲,不过滤会一直把 idle 窗口顶开、永不静默。
Inputs: timeout, idle_time.
wait_selectorC
wait_selector(".modal", "visible", 15) — 等待元素状态
Inputs: sel (required), tab, state, timeout.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 53 tools
The older locator-specific click tools (`click_selector`, `click_text`, `click_index`) are largely redundant with the unified `click` tool, which explicitly says callers should not need to choose between them. Additional overlap exists among `state`/`refs`/`box_of`, the different scroll tools, and the universal escape hatches `cdp` and `exec_python`, making automatic tool selection fuzzy.
Most tools follow a clear snake_case verb_noun pattern (`close_tab`, `wait_for_load`, `scroll_into_view`, `switch_tab`, `upload_file`), and the specialized interaction variants are named predictably. The main deviations are bare nouns like `state`, `refs`, `cdp`, and `box_of`, plus the generic `click` tool sitting beside its named variants, but these are minor relative to the overall set.
With 53 tools, the server is well past the 50+ extreme threshold, and much of that size comes from redundant click variants, low-level CDP plumbing, and maintenance/debug helpers (`reload_extension`, `reload_agent_helpers`, `exec_python`, `drain_events`). A coherent public-facing browser MCP could provide the same capability in roughly 15–20 well-chosen tools by having `click` absorb the outdated variants and exposing CDP/`exec_python` through a single escape hatch.
The tool set covers the full lifecycle of browser automation: navigation, tab management, page interpretation, DOM interaction, wait conditions, screenshots, iframes, cookies, network interception, uploads/downloads, scrolling/reads, and even frame details. Minor gaps like explicit browser history back/forward and full cookie deletion are workaround-able via `navigate` and `cdp`, but they are noticeable in an otherwise richly featured set.
Maintenance
Related MCP Connectors
Web scraping, browser automation, screenshots and AI data extraction for AI agents via MCP.
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.
Web scraping MCP server — scrape, extract structured data, screenshot any site with anti-bot bypass.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn 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.19 npm4MIT
- AlicenseAqualityBmaintenanceMCP server for AI browser automation with tools for navigation, actions, data extraction, and scripting, supporting local and cloud execution.14496 npm3Apache 2.0

Browser MCPofficial
AlicenseNot gradedqualityBmaintenanceAutomate your browser with AI using a Chrome extension and MCP server, enabling logged-in sessions and stealth automation.25,734 npm7,346Apache 2.0- FlicenseCqualityDmaintenanceEnables browser automation, including navigation, form filling, login with CAPTCHA handling, and element manipulation, using a Chrome-based MCP server.364-