Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chromium-cdp-mcp

Model Context Protocol server that lets an AI assistant drive any Chromium-based browser via the Chrome DevTools Protocol. No Puppeteer, no Playwright — a thin layer of native WebSocket directly to the browser's --remote-debugging-port.

What it is

A standalone MCP server (TypeScript). Once an MCP-aware client (Claude Desktop, Claude Code, Cursor, Windsurf, Cline, Continue.dev, Zed, Codex CLI, Goose, …) loads it, the AI can:

  • navigate, click, fill, scroll, type, select options, take screenshots
  • run arbitrary JavaScript and receive the real return value
  • handle JS dialogs (alert / confirm / prompt / beforeunload)
  • click inside cross-origin iframes (Cloudflare Turnstile, reCAPTCHA, embed widgets)
  • manage tabs, override the viewport, send raw CDP commands

The server is browser-agnostic: every tool takes a port argument and connects to whatever Chromium-based browser is listening on that port.

What it is NOT

  • Not a profile / fingerprint manager. For antidetect profile creation, proxies, cookies, and batched profile orchestration use a separate MCP such as undetectable-local-api-mcp-ts.
  • Not a browser launcher. You start the browser yourself (or via the antidetect software); this server attaches.

Install

From npm

# in your MCP client config — auto-fetches and runs
npx -y chromium-cdp-mcp

From source

git clone https://github.com/<your-org>/chromium-cdp-mcp
cd chromium-cdp-mcp
npm install
npm run build
node dist/server.js     # or `npm start`

Requires Node.js ≥ 22 (native WebSocket).

MCP client config

Same shape for every client. Example (claude_desktop_config.json etc.):

{
  "mcpServers": {
    "chromium-cdp": {
      "command": "npx",
      "args": ["-y", "chromium-cdp-mcp"]
    }
  }
}

For a local checkout:

{
  "mcpServers": {
    "chromium-cdp": {
      "command": "node",
      "args": ["/absolute/path/to/MCP-CDP/dist/server.js"]
    }
  }
}

Launch a browser with CDP enabled

The server connects to an already-running Chromium instance via its remote debugging port. Start one of these first:

Chrome / Chromium

# Linux / macOS
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-cdp

# Windows (PowerShell or cmd)
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
  --remote-debugging-port=9222 ^
  --user-data-dir="%TEMP%\chrome-cdp"

Microsoft Edge

"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" ^
  --remote-debugging-port=9222 ^
  --user-data-dir="%TEMP%\edge-cdp"

Brave / Opera / Vivaldi / Yandex Browser

Same --remote-debugging-port=PORT flag — every Chromium fork supports it.

Puppeteer / Playwright launchers

// Puppeteer
const browser = await puppeteer.launch({
  args: ["--remote-debugging-port=9222"],
});

// Playwright (chromium)
const browser = await chromium.launch({
  args: ["--remote-debugging-port=9222"],
});

Use a fresh profile

--user-data-dir points to a clean profile directory. Without it, Chrome refuses to enable remote debugging on your default profile (a 122+ security restriction).

Verify the port is live

curl http://127.0.0.1:9222/json/version

If you see JSON with webSocketDebuggerUrl, you're ready.

Connection block

Every tool accepts the same connection fields:

Field Type Description
port number The browser's --remote-debugging-port. Required.
host string Default 127.0.0.1.
target_id string Explicit page targetId from list_tabs. Skips auto-pick.
url_contains string Pick the first page whose URL contains this substring.
tab_index number Pick page by index in Target.getTargets order.

Auto-pick (when none of target_id / url_contains / tab_index is given): the first attached page, falling back to the first page in the target list.

Tools

Connection / tabs

Tool What it does
connect Probe /json/version + /json/list. Diagnostic.
list_tabs Target.getTargets. Returns {targetId, type, url, title, attached}.
new_tab Open a new tab. Optional url. Returns the new targetId.
close_tab Close the tab specified by target_id.

Navigation

Tool What it does
navigate Page.navigate + best-effort wait for load. Returns final URL.
back history.back().
forward history.forward().
reload Page.reload, optional ignore_cache.

JavaScript

Tool What it does
evaluate Runtime.evaluate with awaitPromise=true, returnByValue=true. Returns the actual value.
get_page_html document.documentElement.outerHTML.
get_url location.href.

DOM interactions

Tool What it does
click Real mouse press+release at the element's center. CSS or XPath selector.
click_at Real mouse press+release at raw viewport (x, y).
click_iframe Real click inside any iframe (cross-origin friendly). dx / dy offsets, default center.
click_and_accept_dialog Click AND auto-handle any alert / confirm / prompt dialog the click triggers.
handle_dialog Handle a currently-open JS dialog. accept, optional prompt_text.
fill Focus + Input.insertText. Clears existing value unless append=true. Supports unicode.
select_option Set <select> value + dispatch input and change events so frameworks react.
focus el.focus().
scroll_to Scroll element into view (center). Returns its bounding rect.
scroll window.scrollTo(x,y) (absolute) or scrollBy(dx,dy) (relative).
press_key Input.dispatchKeyEventEnter / Tab / Escape / arrows / printable chars / unicode.
wait_for Poll for a selector until it appears (or timeout).

Capture / emulation

Tool What it does
screenshot Page.captureScreenshot → file. full_page, format (png / jpeg), quality.
set_viewport Emulation.setDeviceMetricsOverride. Mobile emulation. clear: true to reset.

Escape hatch

Tool What it does
raw Send any CDP method directly. For things not exposed as a dedicated tool: Network.*, Storage.*, Fetch.*, Target.activateTarget, etc.

Selectors

Any selector starting with / or (/ is treated as XPath. Everything else is treated as CSS. Examples:

input[name="email"]                    # CSS
#submit                                # CSS
//button[contains(.,"Войти")]          # XPath
(//a[@class="title"])[3]               # XPath

Quickstart for an AI assistant

A natural step sequence for filling and submitting a form looks like this:

1. connect            { port: 9222 }                                # confirm browser is alive
2. navigate           { port: 9222, url: "https://example.com" }    # load page
3. wait_for           { port: 9222, selector: "input#email" }       # wait for hydration
4. fill               { port: 9222, selector: "input#email", text: "user@example.com" }
5. fill               { port: 9222, selector: "input#password", text: "<entered by user>" }
6. click_and_accept_dialog
                      { port: 9222, selector: "#submit", accept: true }
7. wait_for           { port: 9222, selector: ".dashboard" }
8. screenshot         { port: 9222, path: "/tmp/dashboard.png" }

Integration with antidetect browsers

Antidetect / multi-profile browsers expose a remote-debugging port per running profile. Hand that port to this MCP and it can drive the underlying browser without caring about fingerprinting, proxies, or profile state — those are managed by the antidetect's own MCP / API.

Example: Undetectable + chromium-cdp-mcp

Undetectable's local API (and its undetectable-local-api-mcp-ts wrapper) returns a debug_port whenever a profile is started:

// undetectable-local-api-mcp-ts → start_profile response
{
  "code": 0,
  "data": {
    "debug_port": "52967",
    "websocket_link": "ws://127.0.0.1:52967/devtools/browser/<id>",
    "name": "my-profile"
  },
  "status": "success"
}

Feed 52967 straight into this MCP's tools:

// chromium-cdp-mcp → navigate
{ "port": 52967, "url": "https://example.com" }

This split keeps responsibilities clean:

  • undetectable-local-api-mcp-ts — create profile, set proxy, set OS/browser, start / stop, batch operations, cookies, fingerprint configs.
  • chromium-cdp-mcp — everything the AI does inside the browser session.

The same pattern works for any antidetect browser or headless launcher that publishes a webSocketDebuggerUrl on a known port (AdsPower, GoLogin, Multilogin, Puppeteer, Playwright …).

Env

Variable Default Notes
CDP_TIMEOUT 30 Per-command timeout in seconds.

Known limitations

  • Native WebSocket required. Node ≥ 22.
  • Cross-origin iframe selectors. click / fill / select_option use a selector evaluated against the main page DOM. For elements inside a cross-origin iframe (Cloudflare Turnstile, reCAPTCHA, embedded YouTube …) use click_iframe (selector resolves to the iframe element, the click lands inside) or click_at with raw coordinates.
  • Dialog handling. Native alert / confirm / prompt block page JS until handled. Use click_and_accept_dialog to do both in one call, or handle_dialog to dismiss an already-open one.
  • Ephemeral session. Each tool call opens a fresh WebSocket and closes it on return. That keeps the server stateless but means CDP event subscriptions are scoped to a single call (this is fine for the supplied tools — the dialog tool handles its own subscription internally).

⚠️ AI gets full browser control. Once connected, an AI client can drive any tab on the target browser: navigate, click, fill forms, run JS, read page content, take screenshots. Only point this server at browsers you are willing to give the AI full access to. Review tool calls in your MCP client.

License

MIT

About

MCP server that drives any Chromium-based browser (Chrome, Edge, Brave, Opera, Vivaldi, antidetect, headless) via the Chrome DevTools Protocol — no Puppeteer, no Playwright. Navigate, click, fill, screenshot, run JS, handle dialogs, click cross-origin iframes.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages