A side-by-side look at the Noncelogic MCP and Nodriver — undetected browser automation MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Noncelogic MCP for browser automation work as a local process; choose Nodriver — undetected browser automation for browser automation as a local process. You can also run both side by side in one mcpServers block.
ROVE_API_KEY.navigate, interact, extract_schema.list_frames, bypass_insecure_warning, cf_verify.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Browser Automation | Browser Automation |
| Quality signal | 59/100 · Good | 61/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | Not declared |
| Pricing | Not declared | Not declared |
| Required env vars | ROVE_API_KEY | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 0 | 6 |
| Directory activity | 1 views 0 installs 0 upvotes | 4 views 0 installs 0 upvotes |
| Tools | 31 | 70 |
| Full listing | View Noncelogic MCP | View Nodriver — undetected browser automation |
navigateNavigate to a URL in a hosted Chromium browser and return the page title and final URL after redirects. Creates a new browser session automatically if no session_id is provided. Use this as the first step in any browser automation workflow. Use stealth mode when visiting public websites that may block automated browsers. Use action_delay_ms to add human-like jitter between subsequent actions in the same session.interactPerform a click or fill action on a page element in an active browser session. Use "click" to click buttons, links, checkboxes, or any clickable element by CSS selector. Use "fill" to type text into input fields, textareas, or contenteditable elements — pass the target CSS selector and the text value in params. Requires an active session created by the navigate tool. Do not use this for navigation — use the navigate tool instead.extract_schemaExtract structured data from a web page according to a JSON schema. Navigates to the given URL, renders the page in a headless browser, and returns data matching your schema definition. Use this instead of navigate + get_a11y_tree when you need specific structured fields (e.g. product name, price, rating) rather than a full page snapshot. Each key in the schema should map to a CSS selector or description of the data to extract.screenshotCapture a PNG screenshot of a web page. Operates in two modes: (1) Pass a session_id to screenshot the current page in an active session — use this when you need to see the visual state after interactions. (2) Pass a url without session_id to take a one-shot screenshot without creating a persistent session — use this for quick visual checks. Returns a URL to the screenshot image. Prefer get_a11y_tree over screenshot when you need to read page content, as it uses 77% fewer tokens.get_a11y_treeGet a structured accessibility tree snapshot of the current page in an active browser session. Returns a hierarchical representation of all visible elements including their roles, names, values, and states — similar to what a screen reader sees. Use this as the primary way to read page content, as it uses 77% fewer tokens than a screenshot. Use this after navigate to understand page structure, find interactive elements by their accessible names, or verify that an action changed the page state. Requires a session created by navigate.close_sessionClose a browser session and release its server-side resources (browser context, memory, and any open pages). Call this when you are done with a browser session to free resources and avoid hitting session limits. If the closed session is the currently active session, the internal session reference is cleared — subsequent tool calls will need a new navigate to create a fresh session.clickClick an element on the page by CSS selector, accessible label or a11y tree ref. Use this to interact with buttons, links, checkboxes, and other clickable elements. In a supervised session, a click that opens a popup makes the popup the current page.fillType text into a form field. By default clears any existing value first, then types the new value.get_textExtract the text content of an element on the page. Useful for reading specific values like prices, headings, or status messages.scrollScroll the page in a given direction, or scroll a specific scrollable element. Useful for loading lazy content or reaching elements below the fold.evaluateExecute arbitrary JavaScript in the browser page context. Returns the result of the expression. Use for advanced DOM queries, custom extraction logic, or page manipulation.list_pagesList the pages open in this session's browser, with page ids, opener, URL, title and status, and which page is current. Popups the page opened appear here. Does not count as a fresh observation.list_framesList the page's frames, so their content can be reached at all.
Chrome's accessibility tree stops at a frame boundary, and
document.querySelector never crosses one, so anything inside an iframe —
payment fields, consent walls, embedded editors, CAPTCHA widgets — is
invisible to take_snapshot and query_selector by default.
Index 0 is always the main frame. Pass an index or frame id as the `frame`
argument of get_page_content, query_selector, evaluate_script,
wait_for_selector or take_snapshot to work inside one.bypass_insecure_warningClick through Chrome's "Your connection is not private" interstitial.
Use this when a navigation lands on an SSL/certificate warning page (expired
or self-signed certificate, hostname mismatch) instead of the site itself —
the snapshot then shows a warning page rather than the expected content.
This performs the Advanced -> Proceed click for you.
Has no effect on any other kind of page.cf_verifyAttempt to solve a Cloudflare "Verify you are human" challenge.
Use when a page is stuck on a Cloudflare interstitial — a checkbox widget,
or "Checking your browser before accessing". Drives nodriver's built-in
verification bypass, which locates the checkbox visually and clicks it.
Needs opencv, which is an optional extra: install nodriver-mcp[cf], or this
reports that it is missing rather than failing obscurely. Many challenges
clear by themselves after a few seconds, so wait_for(["some text from the
real page"]) is worth trying first — and this says so when it looks at the
page and finds no challenge at all.clickClick an element addressed by its snapshot uid.
This is the click you want in almost all cases — prefer it over click_at,
because a uid survives layout shifts and raw coordinates do not. The element
is scrolled into view first, so it need not be visible beforehand.
Sends real CDP input events, so the page sees `isTrusted=true`, which is the
entire point of an undetected driver. Because those are delivered by
coordinate, the click is aimed at a point that actually hits the element:
several points inside it are hit-tested first, since a sticky header or a
banner can cover the centre. If none of them reach it, `if_covered` decides
what happens, and the response always says which path was taken.
Two other situations force the scripted fallback regardless: a touch-emulated
target, where CDP mouse input can crash the renderer, and a CDP click that
times out or errors. Every step is bounded at 10s, so a wedged page cannot
hang the call.
On "unknown uid", take a fresh take_snapshot and retry with the new uid.click_atClick a raw viewport coordinate instead of an element.
Use `click` with a uid whenever the target shows up in a snapshot — it is
robust against layout shifts, this is not. Coordinates are for surfaces with
no addressable element: canvases, maps, video players, image hotspots.
The point must lie inside the current viewport; scroll_page or
scroll_to_selector first if it does not. Coordinates are in CSS pixels and
ignore the device pixel ratio, so they match what emulate/resize_page report.
Same input path as `click`: real CDP events, the same reported scripted
fallback, the same 10s bound per step.close_pageClose a single browser tab.
The last remaining tab cannot be closed — use close_browser to shut Chrome
down entirely. Closing the selected page clears the selection, so subsequent
tools fall back to the most recently opened tab.
Returns the remaining open pages, because indices shift when a tab closes:
re-read them from this response rather than reusing older ones.close_browserQuit Chrome entirely, closing every tab.
Unlike close_page (which always keeps one tab alive), this tears down the
whole browser. Chrome relaunches automatically on the next tool call with
the currently selected profile, so this is also how you apply pending launch
flags without switching profiles.
On an ephemeral temp profile (the default) this discards cookies, logins and
localStorage — save_session first if you need them back. Persistent profiles
created with create_profile keep everything.
It keeps the browser itself. The name, its selected profile and its launch
flags survive for the next relaunch, so the name goes on counting against the
12-browser limit. When an agent is finished for good, call shutdown_browser
instead: it also releases the name.dragDrag one element onto another (press, move, release).
Both uids must come from the same take_snapshot. Both ends are scrolled into
view and hit-tested first, and the pointer is moved in steps with the button
held, which is what mouse-driven sortables and sliders listen for.
This drives the mouse. Native HTML5 drag-and-drop — the dataTransfer kind —
is a separate protocol that a synthetic mouse does not trigger in Chrome; if
nothing happens on a page that uses it, that is why.emulateEmulate network, CPU, geolocation, user agent, color scheme or viewport.
Applies to the selected page and persists across navigations until
reset_emulation. Every parameter is independent — pass only what you want to
change, leave the rest at their defaults. Settings already in force are
carried over rather than dropped: a user_agent on its own keeps the client
hints a device preset established, and a viewport on its own keeps its touch
and mobile flags unless you clear them with `notouch` / `nomobile`.
To emulate a real phone or tablet, use emulate_device instead: it sets user
agent, client hints, viewport, DPR and touch as one coherent set, which
hand-assembled overrides here get wrong in ways anti-bot systems detect.
Turning `touch` on in the viewport also changes how `click` behaves — CDP
mouse input can crash a touch-emulated renderer, so clicks fall back to the
scripted path (`isTrusted=false`) for as long as touch is enabled.reset_emulationClear every emulation override on the selected page.
Resets in one call: network throttling, CPU throttling, geolocation, user
agent and client hints, color scheme, viewport, device pixel ratio, page
scale and touch emulation — back to the real browser defaults.
Use after emulate or emulate_device to make the page behave like ordinary
desktop Chrome again. Turning touch emulation off here also restores trusted
CDP clicks.emulate_deviceEmulate a phone or tablet with one internally consistent set of signals.
Preferred over assembling `emulate` parameters by hand for mobile work: user
agent, UA client hints (Sec-CH-UA-*), viewport, device pixel ratio, touch
support and Accept-Language are set together so they cannot contradict each
other — a mismatch between them is a classic automation tell.
Presets are pixel_7, pixel_7_landscape and ipad_air (aliases are listed in
the `device` parameter). ipad_air deliberately reports desktop-class Safari
with touch and sends no client hints, which is what a real iPad does.
Applies to the selected page and survives navigation. To have mobile signals
present on a page's very first request, pass `device` to new_page or
navigate_page instead of calling this afterwards. Undo with reset_emulation.evaluate_scriptRun JavaScript inside the page and get the result back as JSON.
The escape hatch for anything the other tools do not cover: reading computed
styles, calling a page's own JS API, or extracting structured data in a
single round trip instead of a dozen snapshot-and-click cycles.
Without `args` the function runs at page level in the main world. With
`args`, the given uids become real element references, which is how you
operate on one specific element from a snapshot.
Return values must be JSON-serialisable — DOM nodes, functions and circular
structures are not, so map them to plain values inside the function. A script
that throws comes back as a failed call carrying the JavaScript error, not as
a result you have to inspect for the word "Error".
Pass script_path to run a function kept in a .js file, and file_path to write
the result to disk instead of into the conversation.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"mcp-313": {
"command": "npx",
"args": [
"-y",
"@roveapi/mcp"
],
"env": {
"ROVE_API_KEY": "YOUR_ROVE_API_KEY_HERE"
}
}
}
}
{
"mcpServers": {
"nodriver-undetected-browser-automation": {
"command": "uvx",
"args": [
"nodriver-mcp"
]
}
}
}