What is the Chrome DevTools Protocol (CDP)? How Puppeteer, Playwright and AI agents drive a browser
CDP is the JSON channel behind Puppeteer, Playwright's raw sessions and several AI-agent browsers; how it works, how to try it, and why Chrome 136 changed its debugging switches.
· 7 min read
Written by AI from the sources linked below. How we use AI
On this page
In short
The Chrome DevTools Protocol (CDP) is the JSON-based control channel that lets a program inspect and drive a Chromium-based browser: open a page, navigate, click, read the page's structure, take a screenshot, read cookies. Puppeteer speaks it, Playwright can speak it, and a growing number of AI-agent browsers and tools expose it so existing scripts keep working. It is powerful because it is complete, and risky for the same reason: whoever can reach a browser's debugging port controls that browser, including any logged-in sessions.
Why this matters now
CDP used to be something you met only in a test suite or in the Chrome DevTools panel. Agents changed that. A coding or research agent that browses needs a way to load pages, run scripts and capture results, and CDP is the common language. On 13 November 2026 Cloudflare open-sourced Kitesurf, a Rust browser engine for AI agents, whose README says it speaks CDP so that clients such as Puppeteer can connect to it. A tool built for one browser can talk to a very different engine, as long as both implement enough of the protocol.
What CDP is
The protocol is defined in the public ChromeDevTools/devtools-protocol repository, under the BSD-3-Clause licence. The repository holds the definitions in two forms, .pdl and json/, plus TypeScript types, and publishes them as the devtools-protocol npm package. The README says those files are generated daily by a script and must not be edited by hand, and that the repository does not track issues about the protocol's definition or implementation: bugs go to the Chromium tracker. The viewer at chromedevtools.github.io/devtools-protocol offers three versions: Tip-of-Tree (the latest), Stable (1.3) and the V8 Inspector variant used by Node.js.
The protocol is organised into domains, each covering one area of the browser, and each domain has commands (things you ask the browser to do), events (things it tells you about) and types. We counted them in the two JSON files on the repository's main branch on 14 November 2026, version 1.3:
browser_protocol.json: 53 domains, 595 commands and 210 events. These include Page, Network, DOM, CSS, Input, Emulation, Target and Accessibility.js_protocol.json: 6 domains, 82 commands and 22 events, including Runtime (JavaScript evaluation) and Debugger.
Not all of it is stable. In the browser protocol, 39 of the 53 domains are marked experimental in the JSON. The README gives no compatibility policy, so a script that leans on an experimental domain should expect to be adjusted when the browser updates. The accessibility tree command Accessibility.getFullAXTree and the DOM snapshot command DOMSnapshot.captureSnapshot are examples: both live in domains marked experimental.
What a conversation looks like
Over a WebSocket, the client sends a command as a JSON message with an id, a method of the form Domain.command, and params. The browser answers with the same id and a result; events arrive as messages with a method and params and no id. An illustrative exchange, using the definitions in the JSON files:
-> {"id": 1, "method": "Page.navigate", "params": {"url": "https://example.com"}}
<- {"id": 1, "result": {"frameId": "...", "loaderId": "..."}}
<- {"method": "Page.frameNavigated", "params": {...}}
In the JSON, Page.navigate takes a required url and optional referrer, transitionType and frameId, and returns frameId, loaderId, errorText and isDownload. Page.captureScreenshot takes an optional format (jpeg, png or webp, png by default), a quality for JPEG, and an optional clip region. Other commands we checked exist as you would expect: Runtime.evaluate, Input.dispatchMouseEvent, Network.getCookies, Page.printToPDF and Emulation.setDeviceMetricsOverride. The Target domain, described as supporting "additional targets discovery", lets a client attach to other targets such as tabs and frames.
How you connect
A Chromium-based browser started with remote debugging serves an HTTP endpoint. Chrome's own remote-debugging guide shows the idea with an Android device forwarded to port 9222: http://localhost:9222/json lists page targets and http://localhost:9222/json/version exposes the browser target endpoint, the WebSocket address a client connects to.
To try it on a desktop, start Chrome with a dedicated profile folder (this matters, see the security section), then ask for the version:
chrome --remote-debugging-port=9222 --user-data-dir=/tmp/cdp-profile
curl http://localhost:9222/json/version
You do not have to write the messages yourself. Three common routes:
- Puppeteer. Its documentation describes it as a JavaScript library with a high-level API for automating a browser, communicating through CDP and WebDriver BiDi. It drives Chrome and Firefox and runs headless by default. Use cases it lists include screenshots and PDFs, form submission, UI testing and crawling single-page applications.
- Playwright, raw session. Playwright's
CDPSessionlets you "talk raw" CDP: create one withpage.context().newCDPSession(page), send commands withsession.send(method, params)and listen to every event withsession.on('event'). The documented example enables the Animation domain and changes the playback rate. - Playwright, attach to a running browser.
browserType.connectOverCDP(endpointURL)attaches to an existing Chromium-based browser, with an address such ashttp://localhost:9222/. The docs warn that "This connection is significantly lower fidelity than the Playwright protocol connection" and say it supports only Chromium-based browsers; for advanced features they point tobrowserType.connect()instead.
That last point is the practical lesson for agent builders: the closer a client stays to CDP's raw model, the more it depends on each engine implementing the same commands. An engine that supports a subset will work for screenshots and page text and may fail on the rest. Kitesurf's own README says some Chrome DevTools features do not work yet.
Why agents like it, and what they should be careful about
A few things make CDP a good fit for agents:
- One protocol, many clients. Existing automation code, MCP servers and test tools already speak it.
- Everything is reachable. Screenshots, PDFs, accessibility data, network traffic and input events are all commands, so an agent can choose a compact text view of a page instead of pixels. The accessibility and DOM snapshot commands are one way to get structure, though they are in experimental domains.
- Browser state is a first-class object. The same channel that clicks a button can read cookies or evaluate JavaScript in the page, which is also the source of the risk.
The security side
Remote debugging is a full-control interface. Chrome's developer blog (Will Harris, 17 March 2025) said that since App-Bound Encryption was enabled, Google had seen more attackers use Chrome remote debugging to extract cookies, helped by public tools and blog posts. From Chrome 136, the --remote-debugging-port and --remote-debugging-pipe switches are ignored when they target the default Chrome data directory; they work only together with a --user-data-dir that points at a non-standard directory, which uses a different encryption key. In the post's words, "These switches will no longer be respected if attempting to debug the default Chrome data directory." Google's advice for developers is to use a custom user data directory, and for browser automation to use Chrome for Testing, which keeps the previous behaviour.
That is why the command above uses a throwaway profile folder: it keeps your real logins out of the debugged browser.
The change is a hardening step, not a boundary. On 13 August 2026 SpecterOps published "Return of the Cookie Monster", describing a technique that turns CDP on inside an already running Chrome or Edge process instead of launching it with a flag. The author's tool locates the browser module, resolves internal symbols and starts the browser's remote debugging server on a chosen local port, after which it can dump cookies, list tabs and drive the logged-in session. It requires code execution that can inject into the browser process, a signature set matching the exact browser build, a local TCP port and a logged-in user. The post suggests detecting process injection into browsers with Sysmon events 8 and 10 (a remote thread created in chrome.exe or msedge.exe, and a process access with mask 0x143a). Our reading: CDP access is equivalent to the user's logged-in browser, so the defence is to keep unknown code off the machine and the port closed, not only to change launch flags.
Practical rules for anyone running agents with CDP:
- Use a dedicated profile with no personal logins; never point an agent at your everyday browser profile.
- Keep the debugging port on localhost. The examples in Chrome's documentation use
localhost; do not expose it to a network. Kitesurf's README warns that the engine has no built-in authentication and says not to deploy it without Cloudflare Access or another authentication layer; the same thinking applies to any CDP endpoint. - Treat page content as untrusted input to the agent, because the agent's browser can click and read on your behalf.
- Pin and test your client against the browser version you run; experimental domains change.
Quick reference
| Task | Where to look |
|---|---|
| Browse commands and events | the protocol viewer, Tip-of-Tree for the latest |
| Types for TypeScript | the devtools-protocol npm package |
| Raw access from Playwright | page.context().newCDPSession(page) |
| Attach Playwright to a running Chromium | browserType.connectOverCDP('http://localhost:9222/') |
| Debug safely in Chrome 136 and later | --remote-debugging-port plus a non-default --user-data-dir |
What we could not confirm
We did not find a stability policy for the protocol in the repository README, so how long an experimental command stays unchanged is unknown. We have not benchmarked any engine's CDP coverage, so we cannot say how much of the protocol a given agent browser implements; check its documentation and test suite before relying on it.
Sources
- ChromeDevTools/devtools-protocol repository (README,
browser_protocol.json,js_protocol.json) - Chrome DevTools Protocol viewer
- Chrome for Developers: Remote debugging Android devices
- Chrome for Developers: Changes to remote debugging switches to improve security
- Puppeteer: What is Puppeteer?
- Playwright: CDPSession and BrowserType.connectOverCDP
- SpecterOps: Return of the Cookie Monster (13 August 2026)
- Cloudflare: Kitesurf is now open source and the Kitesurf repository
Written by
The official account of the people who run audiotale.net: announcements, conversation starters and the newsroom's drafts. Posts written with AI say so. Every other account here is a real person.
Read next
AWS Nitro Sandboxes: a hardware-isolated VM per EKS pod, ECS task or AI agent, at no extra charge
AWS says Nitro Sandboxes are available in all commercial regions, adding a VM boundary per pod, task or agent session on shared EC2 instances, with optional attestation.
audiotale.net team6 min read
How to audit the plugins, hooks and MCP servers your AI coding agent loads
A step-by-step check of what Claude Code and Codex run on your machine, using each vendor's own commands: /hooks, /plugin, claude plugin details and how to remove what you don't trust.
audiotale.net team7 min read
Postman 12.33.4 auto-installs Codex and Claude Code plugins: how to check and remove
A GitHub issue says Postman desktop added plugins, a startup hook and an MCP entry to developers' AI agents without asking; Postman says it is reviewing the behavior.
audiotale.net team7 min read