nodriver — undetected browser automation
PYPI · NODRIVER-MCP · SCANNED SEP 21
Undetected anti-bot Chrome automation. 65 tools, parallel isolated browsers.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security50
- Malware scan not yet available for this package.Unverified
- No known CVEs affecting this package version or its production dependencies.Pass
- Runs setuptools.build_meta at install time, a recognised native-build step with no shell scripting around it. View diagnostics → Pass
- 1 of 37 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency32
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Provenance check failed: no build-provenance attestation is published. See how to fix → View diagnostics → Fail
- License check failed: no license is declared. See how to fix → Fail
- Actively maintained (last published 26 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability67
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 17117 tokens (~263/item across 65 items; 65 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management97
- Stability observed for 29 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage100
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 100% of tool parameters carry a description.Pass
- Structured output schemas are declared (97% of tools); any adoption earns full credit.Pass
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 5 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- An AI judge read all 66 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
How do I install the nodriver — undetected browser automation MCP server?
nodriver — undetected browser automation runs locally as a PyPI package, launched with uvx nodriver-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
pypi · nodriver-mcp
claude mcp add andresolbach-nodriver-mcp-server -- uvx nodriver-mcp
{
"mcpServers": {
"andresolbach-nodriver-mcp-server": {
"command": "uvx",
"args": [
"nodriver-mcp"
]
}
}
} {
"servers": {
"andresolbach-nodriver-mcp-server": {
"command": "uvx",
"args": [
"nodriver-mcp"
]
}
}
} codex mcp add andresolbach-nodriver-mcp-server -- uvx nodriver-mcp
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"andresolbach-nodriver-mcp-server": {
"type": "local",
"command": [
"uvx",
"nodriver-mcp"
],
"enabled": true
}
}
} openclaw mcp add andresolbach-nodriver-mcp-server --command uvx --arg nodriver-mcp
mcp_servers:
andresolbach-nodriver-mcp-server:
command: "uvx"
args: ["nodriver-mcp"] {
"McpServers": {
"andresolbach-nodriver-mcp-server": {
"Transport": "stdio",
"Command": "uvx",
"Arguments": [
"nodriver-mcp"
]
}
}
} assistant mcp add andresolbach-nodriver-mcp-server -t stdio -c uvx -a nodriver-mcp
{
"mcpServers": {
"andresolbach-nodriver-mcp-server": {
"command": "uvx",
"args": [
"nodriver-mcp"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 21 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 93 to 97. That category is still filling its 30-day observation window: 28 days of observed history at the previous scan, 29 at this one. The score rises as the window fills, whether or not the server changes.
- 18 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 83 to 87. That category is still filling its 30-day observation window: 25 days of observed history at the previous scan, 26 at this one. The score rises as the window fills, whether or not the server changes.
- 16 Sept 26 −2
- Stability: pass → 0.80 functional
- 15 Sept 26 0
- Stability: 0.97 → pass security
- 14 Sept 26 −14
- Malware scan: pass → unverified ▼ security
- 13 Sept 26 +15
- Malware scan: unverified → pass ▲ security
- 11 Sept 26 −14
- Malware scan: pass → unverified ▼ security
- 9 Sept 26 +1
- Security disclosure: unverified → fail ▼ functional
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 21 Sept 2026 · Analysed pypi/nodriver-mcp@2.4.0
Provenance No attestation
The registry publishes no build provenance for this version, so there is nothing to verify.
| Result | No attestation |
|---|---|
| Ecosystem | pypi |
Background: How many MCP packages publish verified provenance →
Install scripts 1 script
| Hook | Tier | Command |
|---|---|---|
| build_backend | allowlisted | setuptools.build_meta |
Background: Why install scripts are a supply-chain risk →
Dependencies 37 packages
| Packages resolved | 37 |
|---|---|
| Stale | 1 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
block_resources Block resource types ~260
Block images, fonts, stylesheets or media to speed up page loads. Removes most of the bytes on a media-heavy page, which makes scraping several times faster and far cheaper over a metered or proxied connection. Blocking stylesheets breaks layout, so anything that depends on element geometry becomes unreliable — click_at, element screenshots, and the `visible` check in wait_for_selector. Text extraction is unaffected. Requests are matched on the resource type Chrome reports for them, so an asset served from an extension-less URL is still blocked, and blocking one type never catches another. Applies to ONE TAB — the selected page — and stays in effect there across navigations until called again with no types. A tab opened afterwards with new_page starts unblocked and needs its own call.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| types | – | – | Resource types to block. ["image", "font", "media"] is the usual choice for scraping — it keeps stylesheets, so layout-dependent behaviour still works. Pass an empty list or omit to unblock everythin… |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
bypass_insecure_warning Bypass insecure-connection warning ~127
Click 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
cf_verify Solve Cloudflare challenge ~184
Attempt 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
clear_cookies Clear cookies ~129
Delete every cookie in the browser. Browser-wide, not per site and not per tab — this logs you out of everything at once, with no undo. Restore a previous state with load_session if you have one saved. Useful for testing a first-time-visitor flow or resetting a consent banner decision. localStorage is left untouched, so sites that keep state there may still recognise you.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
click Click element ~547
Click 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| dbl_click | boolean | – | Send a double-click instead of a single click. |
| if_covered | string | – | What to do when something else sits on top of the element, so a real mouse click at its position would hit that instead. "report" (the default) does not click at all: it returns an error naming what… |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| uid | string | yes | Element uid from the most recent take_snapshot, e.g. "4_12". uids are invalidated whenever the page changes — if you get "unknown uid", take a fresh snapshot and retry with the new uid. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
click_at Click at coordinates ~289
Click 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| dbl_click | boolean | – | Send a double-click instead of a single click. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| x | integer | yes | X coordinate in CSS pixels, relative to the viewport's top-left corner. |
| y | integer | yes | Y coordinate in CSS pixels, relative to the viewport's top-left corner. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
close_browser Close browser ~203
Quit 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
close_page Close page ~151
Close 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| page_id | integer | – | Index of the page to close, as listed by list_pages. -1 (the default) closes the currently selected page. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
create_profile Create profile ~243
Create a named persistent Chrome profile — a reusable user-data dir. A persistent profile keeps cookies, logins and Web Store extensions between runs, so a site you log into once stays logged in. It is the sturdier alternative to save_session / load_session. Creating one is harmless by itself: nothing changes until you activate it, here or via use_profile. An existing profile of the same name is left untouched rather than overwritten. Profiles live under ~/.nodriver-mcp/profiles/<name>. Only one browser instance can use a given profile at a time, so give concurrent setups different names.
| Name | Type | Req | Description |
|---|---|---|---|
| activate | boolean | – | Switch to the new profile straight away. That restarts the browser and closes all open pages. Default false, which only creates the directory. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| name | string | yes | Profile name, e.g. "google-login". Only letters, digits, "-" and "_" are kept; any other character is stripped out. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
delete_profile Delete profile ~118
Permanently delete a persistent Chrome profile directory. Irreversible: the profile's cookies, logins, history and installed extensions are removed from disk, with no undo. The active profile cannot be deleted — switch away with use_temp_profile first.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| name | string | yes | Name of the persistent profile to delete, as shown by list_profiles. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
disable_console_collection Disable console collection ~111
Stop capturing console output on the current page. Turns the CDP Runtime domain back off, which restores the quieter, harder-to-detect default — worth doing once you are finished debugging and the page still has anti-bot checks ahead of it. Messages already collected stay readable; only new ones stop arriving.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
drag Drag and drop ~246
Drag 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| from_uid | string | yes | uid of the element to pick up, from the most recent take_snapshot. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| to_uid | string | yes | uid of the element to drop onto, from the most recent take_snapshot. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
emulate Emulate page conditions ~618
Emulate 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| color_scheme | string | – | Emulate the prefers-color-scheme media feature. "auto" clears a previous override; empty string leaves it unchanged. |
| cpu_throttling_rate | number | – | Slow the CPU by this factor to emulate a low-end device (4 = 4x slower; 1-20 is the useful range). 0 or 1 means no throttling. |
| geolocation | – | – | Override geolocation, as "latitude,longitude" (e.g. "37.7749,-122.4194"). Omit to leave unchanged; pass an empty string to clear a previous override. |
| network_conditions | string | – | Throttle the network to a preset profile. "Offline" cuts the connection entirely. Empty string leaves throttling unchanged. |
| user_agent | – | – | Override the User-Agent header and navigator.userAgent. Omit to leave unchanged; pass an empty string to restore Chrome's real one. This does NOT touch UA client hints (Sec-CH-UA-*), which then contr… |
| viewport | string | – | Viewport override as "WIDTHxHEIGHTxDPR[,mobile][,touch][,landscape]", e.g. "375x812x3,mobile,touch" or "1920x1080x1". The trailing flags are optional: `mobile` turns on mobile viewport behaviour, `to… |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
emulate_device Emulate device preset ~471
Emulate 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| color_scheme | string | – | Emulate the prefers-color-scheme media feature. "auto" clears a previous override; empty string leaves it unchanged. |
| cpu_throttling_rate | number | – | Slow the CPU by this factor to emulate a low-end device (4 = 4x slower; 1-20 is the useful range). 0 or 1 means no throttling. |
| device | string | yes | Device to emulate — sets user agent, UA client hints, viewport, device pixel ratio and touch together. Presets: "pixel_7" (aliases: pixel7, android, android_phone), "pixel_7_landscape" (pixel7_landsc… |
| geolocation | – | – | Override geolocation, as "latitude,longitude" (e.g. "37.7749,-122.4194"). Omit to leave unchanged; pass an empty string to clear a previous override. |
| network_conditions | string | – | Throttle the network to a preset profile. "Offline" cuts the connection entirely. Empty string leaves throttling unchanged. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
enable_console_collection Enable console collection ~158
Start capturing console output on the current page. Call this before list_console_messages or get_console_message — neither returns anything until collection is on. Network capture, by contrast, is always on and needs no equivalent call. This is opt-in rather than automatic because it enables the CDP Runtime domain, which some anti-bot scripts probe for to detect an attached debugger. Leave it off while stealth matters, and use disable_console_collection when you are done debugging. Applies per page: a new tab needs its own call.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
evaluate_script Evaluate JavaScript ~532
Run 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.
| Name | Type | Req | Description |
|---|---|---|---|
| args | – | – | Element uids from the most recent take_snapshot, resolved to live DOM nodes and passed as the function's arguments. The first uid is also bound as `this`. Omit for page-level scripts. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| file_path | string | – | Write the JSON result to this local path instead of returning it — for extractions too large to put in the conversation. |
| frame | string | – | Run inside one of the page's frames instead of the main document: an index or frame id from list_frames. Runs in an isolated world, which shares the frame's DOM but not its own JavaScript variables,… |
| function | string | – | A JavaScript function expression, e.g. "() => document.title" or "(el) => el.innerText". It is invoked immediately and its return value is JSON-serialised back to you. Async functions are awaited, so… |
| script_path | string | – | Read the function from this local .js file instead of the `function` parameter. Use it for anything long or quote-heavy, where escaping the script into a JSON string is where the mistakes happen. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
fill Fill input ~380
Set the value of an input, textarea, select or contenteditable element. Selects whatever is already in the field and types over it, character by character, so `input` events fire and React/Vue-style controlled components register the change. It deliberately does not blank the field first: that makes such components re-render, the focus follows the replaced node, and the keystrokes end up nowhere. For <select>, the option is chosen by value and a `change` event is dispatched. The value is read back afterwards. If it did not land you get an error, not a success you cannot trust — a field can be read-only, disabled, covered by an overlay, or rewrite what you type, and silently reporting success would send you looking for the problem several steps later. Use type_text instead when you want to append to a focused field rather than replace its contents. For several fields at once, fill_form does it in one round trip.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| uid | string | yes | Element uid from the most recent take_snapshot, e.g. "4_12". uids are invalidated whenever the page changes — if you get "unknown uid", take a fresh snapshot and retry with the new uid. |
| value | string | yes | The text to enter. For a <select> element this must match the option's value attribute, not its visible label. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
fill_form Fill form ~215
Fill several form fields in a single call. Behaves like `fill` per field, but costs one round trip instead of one per field. Fields are processed in the order given, which matters on pages that reveal or enable later fields in response to earlier ones. A field that fails does not abort the rest: the response reports success or the specific error per uid, so a partially filled form is always visible rather than silent.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| elements | array | yes | The fields to fill, in order — each entry is {"uid": "...", "value": "..."}. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
get_computed_styles Get computed styles ~326
Read an element's computed styles, as the browser actually resolved them. Computed styles are the end result of the whole cascade, so this answers what a stylesheet alone cannot: why an element is invisible (`display: none`, `visibility: hidden`, `opacity: 0`), what a CSS variable resolved to, or which font actually got used. Also reports the element's box: position, size, and whether it is currently in the viewport. An element with zero width or height is present in the DOM but not rendered, which is the usual reason a click appears to do nothing. By default only properties that differ from the browser default are returned, because the full set runs to several hundred entries and buries the interesting ones.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| properties | – | – | Only return these CSS properties, e.g. ["display", "color", "font-size"]. Omit to get the properties that differ from the browser default, which is usually what you want — the full computed set is se… |
| selector | string | – | CSS selector of the element to inspect, e.g. "#header" or ".btn.primary". The first match is used. Leave empty to use `uid` instead. |
| uid | string | – | Element uid from the most recent take_snapshot, as an alternative to `selector`. Ignored when `selector` is given. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
get_console_message Get console message ~138
Read one console message in full, by id. list_console_messages truncates every entry to 200 characters; use this to get a complete stack trace or error payload. Requires enable_console_collection to have been called for this page. The server leaves the CDP Runtime domain disabled by default because enabling it is itself something sites can detect.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| msgid | integer | yes | Message id, as shown in square brackets by list_console_messages. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
get_cookies Get cookies ~155
List browser cookies with their domain, path and secure flag. Reads the whole browser cookie jar, not just the current page, unless you pass `url`. Values are returned in full, so treat the output as sensitive — it contains live session tokens. To carry these across a browser restart use save_session, or switch to a persistent profile with use_profile.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| url | string | – | Only return cookies that would be sent to this URL (matching domain, path and secure flag). Empty string returns every cookie in the browser. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
get_local_storage Get localStorage ~118
Read all localStorage entries for the current page's origin. Values are truncated to 200 characters each; for one entry in full use evaluate_script with `() => localStorage.getItem("key")`. localStorage is scoped per origin, so this returns nothing on about:blank — navigate to the site first. sessionStorage is not included.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
get_network_request Get network request ~263
Inspect one network request: URL, method, resource type and both bodies. Often the fastest way to get structured data out of a site: find the page's own API call with list_network_requests, then read the JSON it already received here, instead of scraping the rendered DOM. Inline bodies are truncated at 5000 characters, so pass a file path for anything larger. Response bodies are only available while Chrome still holds them in its buffer — for a long-finished request the entry may still be listed while its body is already gone.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| reqid | – | – | Request id, as shown in square brackets by list_network_requests. Omit to get the most recent request. |
| request_file_path | string | – | Write the request body (POST data) to this local file path instead of including it in the response. |
| response_file_path | string | – | Write the response body to this local file path instead of including it. Use this for binary responses (images, PDFs, archives) — they are base64-decoded on the way out. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
get_page_content Get page content ~352
Get the page's visible text, or its full HTML. The cheapest way to read a page when you only need content and not the uids take_snapshot provides: no accessibility tree is built, and the text form carries no markup overhead. Returns the DOM as it stands right now, so on pages that render asynchronously call wait_for or wait_for_selector first. Use take_snapshot when you intend to interact with elements, and query_selector when you want specific elements rather than the whole page.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| file_path | string | – | Write the content to this local path instead of returning it — the way to capture a large page without flooding the conversation. |
| format | string | – | "text" returns document.body.innerText — visible text without markup, which is what you want for reading. "html" returns the full outerHTML including scripts and attributes, needed when you care abou… |
| frame | string | – | Read inside one of the page's frames instead of the main document: an index or frame id from list_frames. Chrome's DOM queries never cross a frame boundary, so anything inside an iframe is unreachabl… |
| max_chars | integer | – | Truncate the output at this many characters. 0 means no limit, which is risky on large pages — especially with format="html". |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
handle_dialog Handle JavaScript dialog ~195
Answer an open JavaScript dialog — alert, confirm or prompt. A dialog blocks the page and every subsequent tool call until it is handled, so call this as soon as one appears. Returns an error if no dialog is open. beforeunload dialogs triggered by navigating away are handled automatically via navigate_page's `handle_before_unload` parameter; this tool is for dialogs the page opens by itself.
| Name | Type | Req | Description |
|---|---|---|---|
| action | string | – | Accept (OK) or dismiss (Cancel) the dialog. Either closes an alert(); for confirm() the choice is what the page's JavaScript receives. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| prompt_text | string | – | Text to enter into a prompt() dialog before accepting it. Ignored for alert() and confirm() dialogs. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
hover Hover element ~255
Move the mouse over an element without clicking it. This is how you open hover-triggered menus, tooltips and dropdowns before reading what they reveal — pass include_snapshot=true to get the revealed content back in the same call. The element is scrolled into view and hit-tested first, the same way click does it: a point that lands on a sticky header instead opens that header's menu, not the one you asked for. If something covers the element, this says what, rather than hovering the wrong thing.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| uid | string | yes | Element uid from the most recent take_snapshot, e.g. "4_12". uids are invalidated whenever the page changes — if you get "unknown uid", take a fresh snapshot and retry with the new uid. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
list_browsers List browsers ~142
List every browser this server currently holds: its name, whether its Chrome is running, which profile it uses, how many tabs it has and what they show. Reports without starting anything. There is always a browser called "default", which runs in the server process. Others are created implicitly, by passing a new name as the `browser` argument of any tool, so this only shows names already used. A name missing here is not an error; it simply has not been opened. chrome_running=false means the browser exists but has no Chrome, either because no tool has needed one yet or because close_browser was called. The next call relaunches it.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
list_console_messages List console messages ~248
List console output collected for the selected page. Requires enable_console_collection first. Console capture is opt-in because it needs the CDP Runtime domain, which some sites use to detect an attached debugger; without it this returns a reminder instead of messages. Each line is prefixed with its id and truncated to 200 characters — pass the id to get_console_message for the full text. Only the most recent 1000 messages are retained.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| include_preserved_messages | boolean | – | Also include messages from before the last navigation — the server keeps the previous 3 navigations. Default false: current page only. |
| page_idx | integer | – | 0-based page number, used together with page_size. |
| page_size | – | – | Maximum number of messages to return. Omit to return all. |
| types | – | – | Only return these console types, e.g. ["error", "warning"]. Note the value is "warning", not "warn". Omit for all types. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
list_frames List frames ~155
List 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
list_network_requests List network requests ~301
List the network requests the selected page has made. Collection is automatic — unlike console capture, nothing needs enabling. The main use is finding the JSON API a page calls: filter with resource_types=["XHR", "Fetch"], then pass the id from the square brackets to get_network_request to read the actual response body. That is usually far cheaper and more reliable than scraping the rendered DOM. Only the most recent 1000 requests are retained, and each URL is truncated to 150 characters in this listing.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| include_preserved_requests | boolean | – | Also include requests from before the last navigation — the server keeps the previous 3 navigations. Default false: current page only. |
| page_idx | integer | – | 0-based page number, used together with page_size. |
| page_size | – | – | Maximum number of requests to return. Omit to return all. |
| resource_types | – | – | Only return these resource types. ["XHR", "Fetch"] is the useful filter for finding a page's own API calls. Matching is case-insensitive. Omit for all types. |
| url_filter | string | – | Only return requests whose URL contains this substring (plain text, not a regex), e.g. "/api/" or "graphql". |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
list_pages List open pages ~184
List every open browser tab with its index, URL and title. The index shown here is the `page_id` taken by select_page and close_page. Indices are positional and shift whenever a tab opens or closes, so re-read them here rather than reusing an index from an earlier call. Tools act on the page chosen with select_page, or on the most recently opened tab if none was selected; the selected one is marked here. Each entry also carries the page's CDP targetId, which is the stable identity of a tab: unlike the index it survives other tabs opening and closing, and it is what CDP-level tooling and logs refer to.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
list_profiles List profiles ~148
List persistent Chrome profiles and show which one is active. By default the browser runs on a fresh ephemeral temp profile that is deleted when the session ends. That default is what lets several nodriver instances — Claude Desktop, Claude Code, the VS Code extension — run at the same time without fighting over one profile directory. Persistent profiles keep cookies, logins and installed extensions across sessions. Create one with create_profile, switch with use_profile, and return to ephemeral with use_temp_profile.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
list_sessions List sessions ~115
List saved session files with their name, save time and contents. Shows the filename to hand to load_session, along with how many cookies and localStorage entries each one holds, newest first. Sessions live in ~/.nodriver-mcp/sessions/ and are never cleaned up automatically, so old logins accumulate there over time.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
load_session Load session ~200
Restore cookies and localStorage from a saved session file. Use it at the start of a run to arrive already logged in. The page is first navigated to the saved origin so localStorage lands where it belongs, then reloaded so the restored cookies take effect. Cookies that no longer apply are skipped rather than failing the whole restore, and the response reports how many were actually restored. Expired tokens still leave you logged out, so check the page afterwards.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| filename | string | yes | Session filename as shown by list_sessions, or an absolute path to a session JSON file. |
| restore_pages | boolean | – | Also re-open the tabs that were open when the session was saved. Default false, which restores only cookies and localStorage. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
manage_extensions Manage Chrome extensions ~350
List, enable, disable or load Chrome extensions. Two separate mechanisms are in play. The master switch: Chrome runs with --disable-extensions by default, so extensions installed in the profile stay dark until it is turned on. And unpacked extensions loaded from a folder on disk. The master switch governs both — with it off, unpacked extensions stay registered but do not load. "load" only works on Chromium or Chrome for Testing. Official Google Chrome builds have ignored --load-extension since v137 (still true on Chrome 151, even with --enable-unsafe-extension-debugging), and this tool says so rather than pretending it worked. On official Chrome the working path is: install the extension once from the Web Store into a persistent profile, then switch it on with "on". Extensions only persist in a persistent profile — on the default ephemeral one nothing can stay installed.
| Name | Type | Req | Description |
|---|---|---|---|
| action | string | – | "list" shows the extensions installed in the active profile plus the current state, and changes nothing. "on" and "off" flip the master switch. "load" registers an unpacked extension from `path` and… |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| path | string | – | Folder containing the extension's manifest.json, for "load" and "unload". Ignored by the other actions. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
navigate_page Navigate page ~484
Navigate the selected page — load a URL, or go back, forward or reload. Navigating rotates the collected logs: the previous page's console and network entries move into the preserved history (the last 3 navigations are kept), still reachable via `include_preserved_*` on the list tools. Passing `device` applies the emulation before the request is sent, so the server sees mobile signals on the very first byte — calling emulate_device afterwards is too late for that first request. Returns the resulting URL together with all open pages. This reuses the current tab; use new_page to open an additional one.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| device | string | – | Device to emulate — sets user agent, UA client hints, viewport, device pixel ratio and touch together. Presets: "pixel_7" (aliases: pixel7, android, android_phone), "pixel_7_landscape" (pixel7_landsc… |
| handle_before_unload | string | – | How to answer a beforeunload confirmation ("Leave site?") raised by the page being left. "accept" leaves, "dismiss" stays — in which case the navigation does not happen. |
| ignore_cache | boolean | – | Bypass the HTTP cache (a hard reload). Only applies to type="reload". |
| init_script | string | – | JavaScript executed in every new document before any of the page's own scripts, for this navigation. Use it to stub out APIs or plant values a page reads at startup. Plain statements, not a function… |
| timeout | integer | – | Maximum wait in milliseconds. 0 uses the built-in default. |
| type | string | – | The kind of navigation: "url" loads `url`, "back" and "forward" move through session history, "reload" reloads the current page. |
| url | string | – | Target URL — required when type="url", ignored otherwise. Include the scheme: "https://example.com", not "example.com". |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
new_page Open new page ~423
Open a new browser tab and load a URL. Chrome always starts with one tab, so the first call here reuses that empty startup tab rather than leaving a stray blank page behind for the rest of the session. A blank tab sitting among others is left alone, and an isolated context always gets a target of its own. Unless `background` is set, the new page becomes the selected page for every subsequent tool call. Passing `device` applies emulation before the first real request, so mobile signals are present from the very first byte — which calling emulate_device afterwards cannot achieve.
| Name | Type | Req | Description |
|---|---|---|---|
| background | boolean | – | Open the tab without focusing it, leaving the current page selected. Default false, which focuses the new page and makes it the target of all following tool calls. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| device | string | – | Device to emulate — sets user agent, UA client hints, viewport, device pixel ratio and touch together. Presets: "pixel_7" (aliases: pixel7, android, android_phone), "pixel_7_landscape" (pixel7_landsc… |
| isolated_context | string | – | Open the page in a named isolated browser context — its own cookie jar and storage, comparable to a separate incognito window. Pages given the same name share that context; different names are fully… |
| timeout | integer | – | Maximum wait in milliseconds. 0 uses the built-in default. |
| url | string | – | URL to load in the new tab, including the scheme. Defaults to "about:blank" for an empty tab. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
performance_start_trace Start performance trace ~287
Record a Chrome performance trace of page load or interaction. Captures the same DevTools timeline categories the Performance panel uses: rendering, scripting, loading, screenshots and the V8 CPU profile. With the defaults (reload + auto_stop) this is one self-contained call that reloads, records for ~5s and returns. Only one trace can run at a time. Pass file_path to keep the data — without it you get just an event count, which confirms the trace ran but says nothing about what it measured. The output is raw trace JSON: load it via DevTools -> Performance -> Load profile.
| Name | Type | Req | Description |
|---|---|---|---|
| auto_stop | boolean | – | Record for about 5 seconds, then stop and return the result in this same call. Default true. Set false to control the window yourself and end it with performance_stop_trace. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| file_path | string | – | Write the raw trace JSON to this local path. Omit to get only the collected event count back. |
| reload | boolean | – | Reload the page after starting the trace so the recording covers page load. Default true, which is what you want for load performance; set false to profile the page as it currently stands. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
performance_stop_trace Stop performance trace ~139
Stop the running performance trace and collect its data. Only needed when performance_start_trace was called with auto_stop=false — otherwise the trace has already ended and this returns an error. Waits up to 30 seconds for Chrome to flush its buffered events.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| file_path | string | – | Write the raw trace JSON to this local path (e.g. "trace.json"), loadable in DevTools -> Performance. Omit to get only the event count. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
press_key Press key ~307
Send a key press or keyboard shortcut to the page. Goes to whatever currently holds focus, so click or fill the target element first — with nothing focused the key lands on the document body. Modifiers are held around the main key with the proper modifier bitmask, so real chords such as Control+A or Control+Shift+R register as shortcuts instead of arriving as unrelated key presses. For entering text use fill (replaces the field) or type_text (appends to it); this tool is for single keys and shortcuts.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| key | string | yes | A key or chord, e.g. "Enter", "Tab", "Escape", "ArrowDown", "a", "Control+A" or "Control+Shift+R". Modifiers are Control, Shift, Alt and Meta, joined with +. Named keys: Enter, Tab, Backspace, Delete… |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
query_selector Query CSS selector ~276
Find elements by CSS selector; list their tag, text, href, id and class. The efficient way to pull a repeated structure off a page — search results, product tiles, table rows — without paying for a full snapshot. Returns a compact listing only. It yields no uids, so it cannot drive clicks: take a snapshot when you need to interact, or operate on the elements directly via evaluate_script. Element text is truncated to 200 characters.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| frame | string | – | Read inside one of the page's frames instead of the main document: an index or frame id from list_frames. Chrome's DOM queries never cross a frame boundary, so anything inside an iframe is unreachabl… |
| limit | integer | – | Maximum number of matching elements to return. |
| selector | string | yes | A CSS selector, e.g. "a.result", "#nav li", "input[type=file]". Standard querySelectorAll syntax — no jQuery extensions such as :contains(). |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
reset_emulation Reset emulation ~138
Clear 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.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
resize_page Resize page ~163
Resize the browser window so the page gets the given dimensions. Moves the actual OS window, which is what you want for responsive-layout checks that should also come out right in a screenshot. To emulate a viewport size without touching the window — including device pixel ratio, the mobile flag and touch — use emulate's `viewport` parameter or emulate_device. Those are what a site's media queries and fingerprinting read as a genuine device change.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| height | integer | yes | Target page height in CSS pixels. |
| width | integer | yes | Target page width in CSS pixels. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
save_pdf Save page as PDF ~190
Export the current page to a PDF using Chrome's print-to-PDF. Renders the entire document rather than just the viewport, and applies the page's print stylesheet — so the result can differ from the screen layout. A good way to archive a rendered page as one file. For a pixel-accurate copy of what is on screen, use take_screenshot with full_page instead.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| file_path | string | yes | Local path to write the .pdf file to. |
| landscape | boolean | – | Use landscape orientation instead of portrait. |
| print_background | boolean | – | Include background colours and images. Default true, which resembles the page as seen on screen; false gives the leaner print view. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
save_session Save session ~199
Save cookies, localStorage and open page URLs to a reusable file. This is how you keep a login obtained interactively, so a later run can skip the login flow entirely — restore it with load_session. Stored as JSON under ~/.nodriver-mcp/sessions/. That file holds live session tokens in plain text, so treat it as a credential. Only the current page's origin contributes localStorage. For a login meant to survive without an explicit restore step, a persistent profile (create_profile + use_profile) is the sturdier option.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| name | string | yes | A human-readable name for this session, e.g. "github-logged-in". It forms the filename, with a timestamp appended so repeated saves never overwrite one another. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
scroll_page Scroll page ~215
Scroll the page up or down by a percentage of the viewport. The way to trigger lazy-loaded content and infinite scroll; take a fresh take_snapshot afterwards to see what was added. The response reports how far the page actually moved and whether the end has been reached, so a scroll loop has something to stop on: "did not move" means you are at the end, and a page height that keeps growing means more content is still loading. To bring one known element into view, scroll_to_selector is more precise. `click` already scrolls to its target, so no scrolling is needed before it.
| Name | Type | Req | Description |
|---|---|---|---|
| amount | integer | – | How far to scroll, as a percentage of the viewport height — 25 is a quarter screen, 100 a full screen. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| direction | string | – | Direction to scroll. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
scroll_to_selector Scroll to element ~141
Scroll the first element matching a CSS selector into view, centered. More precise than scroll_page when you already know what you are looking for, and the usual preparation for click_at, which needs its target inside the viewport. `click` scrolls to its own target, so this is unnecessary before it. Reports whether anything matched instead of failing silently.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| selector | string | yes | CSS selector of the element to scroll to; the first match wins. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
select_option Choose a select option ~271
Choose an option in a <select>, by value, visible label or index. fill also works on a <select>, but only matches the value attribute, which is not what a snapshot shows you — so it had to be guessed. This matches what you can actually see, and when nothing matches it lists the real options instead of only saying no.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| include_snapshot | boolean | – | Append a fresh page snapshot to the response. Worth it when this action changes the page and take_snapshot would be your next call anyway — it saves a round trip, at the cost of a much larger respons… |
| option | string | yes | The option to choose. Matched against the value attribute first, then the visible label, then — if it is a plain number — the position in the list. A failure names every option the <select> actually… |
| uid | string | yes | Element uid from the most recent take_snapshot, e.g. "4_12". uids are invalidated whenever the page changes — if you get "unknown uid", take a fresh snapshot and retry with the new uid. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
select_page Select page ~210
Choose which open tab every subsequent tool call acts on. Without a selection, tools act on the most recently opened tab. Selecting makes that choice explicit and sticky — needed when a click opened a tab you now want to drive, or when working across several sites at once. Indices come from list_pages and shift as tabs open and close, so the response lists them again. If the selected tab is later closed, the selection is dropped and the default applies again.
| Name | Type | Req | Description |
|---|---|---|---|
| bring_to_front | boolean | – | Also focus the tab in the real browser window. Default true. Many pages pause animations, timers and media while backgrounded, so leave this on unless you specifically want the page to stay hidden. |
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| page_id | integer | yes | Index of the page to select, as listed by list_pages. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
set_browser_flags Set browser launch flags ~312
Change Chrome's launch flags at runtime, and show the current ones. Call with no arguments at all to just read the effective configuration — that form changes nothing. These are launch-time flags, so applying them restarts Chrome and closes every open page; on an ephemeral profile that also drops its cookies. Values set here override the NODRIVER_ENABLE_* environment variables.
| Name | Type | Req | Description |
|---|---|---|---|
| browser | string | – | Which browser to act on. Each name is an independent Chrome, created on first use. Omit for the shared default; parallel agents each need their own name. |
| extensions | – | – | True allows externally-installed Chrome extensions, false blocks them. Omit to leave unchanged. Blocked by default, so no "an extension requires your attention" prompt appears. manage_extensions offe… |
| extra_args | – | – | Replace the set of extra Chrome launch flags with this list, e.g. ["--lang=de-DE", "--window-size=1280,800"]. A leading "--" is added if missing. Pass [] to clear them, omit to leave unchanged. This… |
| restart | boolean | – | Restart Chrome now so the flags take effect, closing all open pages. Default true; false defers them to the next browser start. |
| translate | – | – | True allows Chrome's Google Translate popup, false suppresses it. Omit to leave unchanged. Suppressed by default, because the popup covers page content and swallows clicks. |
| Name | Type | Req | Description |
|---|---|---|---|
| result | string | yes | – |
No examples provided.
What is the nodriver — undetected browser automation MCP server?
nodriver — undetected browser automation is an MCP server listed in the public MCP registry as io.github.andresolbach/nodriver-mcp-server. Undetected anti-bot Chrome automation. 65 tools, parallel isolated browsers. This page covers its PyPI package (nodriver-mcp).
Is the nodriver — undetected browser automation MCP server safe to use?
nodriver — undetected browser automation scores 65 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the nodriver — undetected browser automation MCP server expose?
nodriver — undetected browser automation exposes 65 tools: list_frames, bypass_insecure_warning, cf_verify, click, click_at, and 60 more. Their descriptions and schemas cost roughly 16,297 tokens of context every time the server is loaded.
Is the nodriver — undetected browser automation MCP server still maintained?
nodriver — undetected browser automation is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.