Skip to main content

nodriver-mcp-server — undetected browser automation MCP server

nodriver-mcp-server

An undetected, anti-bot-resistant browser automation MCP server — a drop-in, stealth alternative to chrome-devtools-mcp for AI agents like Claude, Claude Code, Cursor, Windsurf, and any Model Context Protocol client. Powered by nodriver so your agent can browse, scrape, and automate real Chrome without tripping Cloudflare, hCaptcha, or WebDriver fingerprint detection.

PyPI CI License: MIT Python 3.12+ MCP compatible Tools: 57 Stars

Keywords: MCP server · browser automation · undetected chromedriver · anti-bot · Cloudflare bypass · web scraping · Claude · Cursor · nodriver · chrome-devtools-mcp alternative · Playwright/Puppeteer alternative · AI agent tools.

Why?

chrome-devtools-mcp and most Playwright/Puppeteer-based servers drive Chrome through CDP/WebDriver in a way that leaves detectable fingerprints (navigator.webdriver, CDP artifacts). Anti-bot systems (Cloudflare, hCaptcha, DataDome, etc.) flag these instantly.

nodriver is the successor of undetected-chromedriver. It talks directly to the CDP protocol — no ChromeDriver binary, no Selenium/WebDriver markers — so automated sessions look like a real user. This server exposes that power through the same tool surface as chrome-devtools-mcp (57 tools), so your agent gets a familiar API with far better stealth.

Features

  • 🕵️ Undetected by designnavigator.webdriver is undefined, no CDP fingerprints.
  • ☁️ Built-in Cloudflare challenge solver (cf_verify).
  • 🧩 57 tools covering navigation, input, snapshots, screenshots, content/PDF export, network + console inspection, device emulation, cookies/storage, sessions, profiles, and performance tracing.
  • 🧠 Schemas written for the agent, not just the compiler — every parameter carries a description, fixed-value options are real enums, and each tool declares read-only/destructive hints. See why this matters.
  • 📄 Accessibility-tree snapshots (take_snapshot) — searchable, LLM-friendly page text that's far smaller and faster than screenshots.
  • 📱 Device emulation (Pixel 7, iPad) with correct UA / client hints.
  • 💾 Session save/restore — persist logins across runs.
  • 🧬 Ephemeral by default, run many at once — each session gets its own temp Chrome profile (auto-deleted), so Claude Desktop, Claude Code and VS Code can all drive nodriver simultaneously without colliding. Named persistent profiles are available on demand for reusable logins.
  • One-command setup for 15+ MCP clients.

Installation

# Recommended: isolated install, won't touch your global Python environment
uv tool install nodriver-mcp

# or with pip
pip install nodriver-mcp

# or run it without installing anything
uvx nodriver-mcp

Requires uv for the first and third form. To track the development branch instead of a release:

uv tool install "nodriver-mcp @ git+https://github.com/andresolbach/nodriver-mcp-server.git@main"

Uses upstream nodriver >=0.50.3, which contains the Chrome 146+ CDP fixes (sameParty removed from Cookie, privateNetworkRequestPolicylocalNetworkAccessRequestPolicy) — verified working against Chrome 150. pip install also works, but uv tool install keeps it isolated.

You'll also need a local installation of Google Chrome (auto-detected).

Upgrade

uv tool upgrade nodriver-mcp

Requirements & tested versions

Every tool in this server was tested end-to-end against Google Chrome 150 with nodriver 0.50.3 on Python 3.12.11 / Windows 11 (macOS and Linux are supported too). Because nodriver talks to Chrome directly over CDP and tracks upstream Chrome changes, it keeps working as Chrome auto-updates.

Component Requirement Verified version
Python 3.12+ 3.12.11
Google Chrome any recent stable 150.0.7871.101
Operating system Windows / macOS / Linux Windows 11
nodriver >= 0.50.3 0.50.3
mcp (MCP SDK) >= 1.26.0, < 2 1.26.0
pillow >= 12.1.1 12.1.1
tomli-w >= 1.0.0 1.2.0

The pip packages/versions are also listed in requirements.txt (pip install -r requirements.txt), though uv tool install is recommended for a fully pinned, reproducible install.

One-command MCP client setup

# Interactive client selector (terminal TUI)
nodriver-mcp install

# Install to specific clients
nodriver-mcp install claude,cursor,kiro

# Uninstall
nodriver-mcp uninstall claude

# List all supported clients
nodriver-mcp --list-clients

# Print MCP config JSON (for manual setup)
nodriver-mcp --config

# Project-level config (writes to .cursor/mcp.json, .mcp.json, etc.)
nodriver-mcp install --scope project

Supported clients: Claude Desktop, Claude Code, Cursor, Windsurf, Codex, Gemini CLI, Copilot CLI, Kiro, VS Code, Cline, Roo Code, Amazon Q, Warp, Opencode, Trae.

The Claude Code VS Code extension shares Claude Code's config (~/.claude.json), so installing to claude-code covers both the CLI and the extension.

Manual config

If you'd rather paste it yourself, this works in any MCP client (claude_desktop_config.json, ~/.claude.json, .cursor/mcp.json, .mcp.json, …):

{
  "mcpServers": {
    "nodriver": {
      "command": "uvx",
      "args": ["nodriver-mcp"]
    }
  }
}

Environment Variables

Variable Description Default
NODRIVER_HEADLESS Headless mode (true/false) false
NODRIVER_USER_DATA_DIR Explicit persistent Chrome profile dir (overrides the default) Ephemeral temp profile, auto-deleted per session
NODRIVER_BROWSER_PATH Chrome executable path Auto-detected
NODRIVER_PROXY Proxy server address None
NODRIVER_ENABLE_TRANSLATE Set true to re-enable Chrome's Google Translate popup Disabled
NODRIVER_ENABLE_EXTENSIONS Set true to allow externally-installed Chrome extensions (and their prompts) Disabled

By default the browser starts clean for automation: the Google Translate popup is suppressed and externally-installed Chrome extensions are blocked (so you don't get "an extension requires your attention" prompts). Re-enable either via the env vars above or at runtime with the set_browser_flags tool — which can also set any other Chrome launch flags (e.g. --lang=de-DE, --window-size=1280,800) via its extra_args parameter. The browser also auto-recovers if Chrome is closed or crashes between calls — tools relaunch it instead of failing.

Chrome also starts on about:blank rather than the New Tab page, so the NTP's own Google requests never show up in list_network_requests, and new_page reuses that empty startup tab instead of leaving a stray blank page behind.

Extensions

manage_extensions handles extensions at runtime:

  • manage_extensions("list") — extensions installed in the active profile (name, version, id) plus the current state
  • manage_extensions("on") / ("off") — the master switch (--disable-extensions); restarts Chrome. It covers unpacked extensions too, so "off" really means off
  • manage_extensions("load", path) / ("unload", path) — unpacked extensions from a folder

To use an extension permanently, switch to a persistent profile, install it once from the Chrome Web Store in that browser, and turn extensions on — it then loads on every launch.

Unpacked extensions need Chromium or Chrome for Testing. Official Chrome builds dropped --load-extension in v137, and as of Chrome 151 neither --enable-unsafe-extension-debugging nor disabling DisableLoadExtensionCommandLineSwitch brings it back — the flag is accepted and the extension is silently never registered. manage_extensions detects a branded build and says so instead of pretending it worked. Point NODRIVER_BROWSER_PATH at Chromium / Chrome for Testing if you need unpacked loading.

Profiles & running multiple instances at once

By default every server instance launches Chrome with a fresh temporary profile that nodriver creates and deletes automatically. That means you can run nodriver from Claude Desktop, Claude Code and the VS Code extension at the same time — each gets its own isolated Chrome, and they never fight over a shared profile. No configuration, no detection logic, nothing to clean up.

When you want to reuse a login across sessions, create a named persistent profile and switch to it:

  • list_profiles — list persistent profiles and show the active one
  • create_profile(name, activate=false) — create a reusable profile
  • use_profile(name) — switch to a persistent profile (""/"temp" returns to ephemeral)
  • use_temp_profile — switch back to a fresh ephemeral profile
  • delete_profile(name) — remove a persistent profile

Persistent profiles live under ~/.nodriver-mcp/profiles/<name>. You can still force a fixed profile globally with the NODRIVER_USER_DATA_DIR env var.

Tools (57)

Network collection is enabled automatically on each tab. Console collection is opt-in: call enable_console_collection when you want list_console_messages / get_console_message to start collecting events. This keeps Runtime.enable() disabled by default for sites that detect attached debuggers.

For mobile-only sites, pass device directly to new_page(...) or navigate_page(...) so the first real request already carries mobile signals.

click and click_at send real CDP input events, so the page sees isTrusted=true. A scripted click (element.click() plus synthetic events, isTrusted=false) is used only where real input cannot be delivered — on a touch-emulated target, or after the CDP click times out — and the response says when that happened, so a detectable click is never silent.

Category Tools
Input automation (10) click · click_at · hover · fill · fill_form · type_text · press_key · drag · upload_file · handle_dialog
Navigation (10) navigate_page · new_page · close_page · close_browser · list_pages · select_page · wait_for · wait_for_selector · scroll_page · scroll_to_selector
Snapshots & debugging (10) take_screenshot · take_snapshot · get_page_content · query_selector · evaluate_script · save_pdf · enable_console_collection · disable_console_collection · list_console_messages · get_console_message
Network monitoring (3) list_network_requests · get_network_request · block_resources
Device emulation (4) emulate · emulate_device · reset_emulation · resize_page
Performance (3) performance_start_trace · performance_stop_trace · take_memory_snapshot
Cookies & storage (5) get_cookies · set_cookie · clear_cookies · get_local_storage · set_local_storage
Session management (3) save_session · load_session · list_sessions
Profiles & browser (7) list_profiles · create_profile · use_profile · use_temp_profile · delete_profile · set_browser_flags · manage_extensions
Anti-detection helpers (2) cf_verify · bypass_insecure_warning

📖 Full tool reference → — every tool with its exact parameters, types, defaults and enum values, generated straight from the live schemas.

Built for the agent that calls it

An MCP tool is only as good as what the model can see of it. Most servers hand over a name, a sentence, and untyped parameters — leaving the agent to guess whether it's type="url" or type="goto", and burning a failed call to find out.

Here, the schema does that work:

  • Every parameter has a description in the schema itself — not buried in a prose blob the client may never show. All 57 tools, all parameters, no exceptions (there's a test for it).
  • Fixed-value parameters are real enums. navigate_page(type=…) advertises exactly url, back, forward, reload. A wrong value is rejected by validation before it ever reaches Chrome, instead of returning an error the agent has to interpret.
  • Numeric and array bounds are declaredquality is 0–100, wait_for(text=…) requires at least one entry.
  • Structured parameters are typed. fill_form publishes {uid, value} rather than an opaque list[dict].
  • Every tool declares behaviour hints (readOnlyHint, destructiveHint, idempotentHint). Clients use these to group permissions — so a client can auto-approve take_snapshot while still prompting for delete_profile.
  • Descriptions say when not to use a tool, and point at the better one. take_screenshot tells the model to prefer take_snapshot; click_at points back to click.

Comparison with chrome-devtools-mcp

Feature chrome-devtools-mcp nodriver-mcp-server
Browser backend Puppeteer (ChromeDriver) nodriver (direct CDP)
WebDriver fingerprint ❌ Exposed ✅ None
navigator.webdriver true undefined
Cloudflare bypass ✅ Built-in cf_verify
Install method npx uvx / pip
Language TypeScript / Node.js Python
Tool coverage 29 tools 57 tools
Per-parameter schema docs partial ✅ all 57 tools
Tool behaviour hints ✅ read-only / destructive

Tools not implemented: performance_analyze_insight (needs the DevTools frontend trace parser), lighthouse_audit (needs the Lighthouse Node API), screencast_start/stop (needs ffmpeg + Puppeteer), extension management (experimental).

Use cases

  • Scrape sites behind Cloudflare / anti-bot (DataDome, PerimeterX, hCaptcha challenges) without being fingerprinted or blocked.
  • Let an AI agent browse the real web — Claude, Cursor, Windsurf and other LLM agents can log in, fill forms, click, read pages, and screenshot.
  • Automate authenticated workflows and reuse the login across sessions with persistent profiles.
  • LLM-driven web research & data extraction using compact accessibility-tree snapshots instead of brittle screenshots.
  • End-to-end / QA testing with device emulation, network + console inspection, and performance traces.
  • An undetected alternative to Playwright, Puppeteer and Selenium for agentic browsing.

FAQ

Is this an undetected alternative to chrome-devtools-mcp? Yes. It exposes the same tool surface but drives Chrome through nodriver (direct CDP), so navigator.webdriver is undefined and there are no WebDriver/CDP fingerprints for anti-bot systems to detect.

Can it bypass Cloudflare? It ships a cf_verify tool that solves the Cloudflare "verify you are human" challenge, and its undetected profile avoids most bot checks. (No tool can guarantee bypassing every protection.)

Which clients are supported? One command installs it into 15+ MCP clients: Claude Desktop, Claude Code, Cursor, Windsurf, Codex, Gemini CLI, Copilot CLI, Kiro, VS Code, Cline, Roo Code, Amazon Q, Warp, Opencode, Trae.

Can I run it in several clients at the same time? Yes. Each instance uses its own ephemeral Chrome profile by default, so Claude Desktop, Claude Code and the VS Code extension can all use nodriver simultaneously without colliding.

Headless or visible browser? A real Chrome window by default; set NODRIVER_HEADLESS=true for headless.

How do I keep a login between sessions? Create a persistent profile with create_profile and switch to it with use_profile, or use save_session / load_session.

Does it work on Windows / macOS / Linux? Yes, all three. Tested on Windows 11 with Chrome 150 and Python 3.12.

Changelog

See CHANGES.md. Highlights: ephemeral-by-default Chrome profiles so multiple instances run at once (+ named persistent profiles), migrated to upstream nodriver 0.50.3 (Chrome 150 verified), and fixed several previously-broken tools — fill/fill_form, evaluate_script with element args, select_page tab switching, press_key modifier chords (Ctrl+A/C/V), network/console lookup indexing, and Windows installer crashes.

Credits

Based on nodriver-mcp by Saber-CC (MIT). Browser backend by nodriver (ultrafunkamsterdam). Tool surface mirrors chrome-devtools-mcp.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nodriver_mcp-1.7.0.tar.gz (71.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nodriver_mcp-1.7.0-py3-none-any.whl (63.3 kB view details)

Uploaded Python 3

File details

Details for the file nodriver_mcp-1.7.0.tar.gz.

File metadata

  • Download URL: nodriver_mcp-1.7.0.tar.gz
  • Upload date:
  • Size: 71.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.11

File hashes

Hashes for nodriver_mcp-1.7.0.tar.gz
Algorithm Hash digest
SHA256 9c6de1d05d5828bd91be597b6a6d5c2a261acbb5053f698da1d6dfcf1b0613ef
MD5 59ed3c5b2242fe40947d3ebbf15efecc
BLAKE2b-256 4f06a2cb85f2fac094ea116f4cd600ff903f9f5fccec2940b981d71242c8af5d

See more details on using hashes here.

File details

Details for the file nodriver_mcp-1.7.0-py3-none-any.whl.

File metadata

  • Download URL: nodriver_mcp-1.7.0-py3-none-any.whl
  • Upload date:
  • Size: 63.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.11

File hashes

Hashes for nodriver_mcp-1.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 179580a890946222e227bce52a1bd50e39e403ce4b177ddb5fa644385697268b
MD5 3e6750b7dfa441b6cb7d3a5620ceb44f
BLAKE2b-256 6aa790eab510c00b4bb3e49e3dc0a2422c0b991f81839e500baf55d46f23c876

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page