Building cdpx: A Driverless CDP Browser Engine for AI Agents
How one Rust binary replaces Playwright's Node.js driver with direct WebSocket CDP, compresses live page state under 800 tokens, and serves MCP over stdio at under 25MB RSS.
Playwright in Python or Go spent over 150MB on a Node.js subprocess before it touched a single DOM node. For an AI agent driving a browser, that driver is most of the cost. cdpx removes the driver entirely.
cdpx is a single Rust binary that speaks Chrome DevTools Protocol (CDP) over a raw WebSocket, injects a 4KB tree-walker into the page, and compresses interactive state into handles like @e1, @e2. It idles under 25MB RSS. No Node.js, no Python, no external process.
+------------------+ +------------------+ +------------------+
| AI Agent | | cdpx (Rust) | | Chromium |
| stdio JSON-RPC | <----> | ~1.2 MB binary | <----> | WebSocket :9222 |
| or CLI | MCP | tokio runtime | CDP | remote-debug |
+------------------+ +------------------+ +------------------+
|
| injects
v
+------------------+
| dom_walker.min.js|
| 4KB tree walker |
| + occlusion test |
+------------------+
The CDP Layer
cdpx skips driver bootstrap entirely. It connects tokio-tungstenite straight to Chromium's --remote-debugging-port WebSocket and issues CDP JSON-RPC commands. The crate pulls tokio, serde, clap, reqwest, which, url, and tempfile; the lockfile resolves to 148 transitive crates, and the release binary strips to 1.2MB.
The transport is one file: src/cdp.rs (229 lines). It holds an [Target.setAutoAttach] listener and routes raw CDP messages. Because there is no Runtime.enable on by default and no injected __playwright__binding__ global, the page sees no driver footprint that Cloudflare Turnstile or DataDome fingerprint.
Semantic Action-State Projection
Full DOM dumps are token garbage for an agent. cdpx injects dom_walker.min.js (4,001 bytes) which walks the tree, culls viewport-invisible and offscreen nodes, runs an occlusion hit-test, and emits only visible interactive elements. src/sasp.rs (97 lines) assigns monotonic handles and serializes the projection.
The per-turn output stays under 800 tokens. An open on Hacker News produces:
[Page: Hacker News] (url: https://news.ycombinator.com)
------------------------------------------------------------
@e1: link "Hacker News"
@e2: link "new"
@e3: link "past"
@e4: link "comments"
Hydration-Safe Actions
Next.js and Remix pages eat naive clicks: the handler is not attached yet when the driver clicks. cdpx runs actions in two phases:
- Scroll into view and measure the center rect in-page.
- Dispatch a trusted
Input.dispatchMouseEventsequence only after the DOM mutation and React fiber quiet periods settle.
src/action.rs (226 lines) owns this. The guard is not a fixed sleep; it waits for the mutation stream to go quiet, so dead clicks on rehydrating routes stop happening.
MCP over stdio
The same binary is an MCP server. cdpx --mcp speaks stdio JSON-RPC 2.0 and exposes three tools:
browser_navigate(url)- navigates, settles hydration, returns the SASP mapbrowser_act(ref, action, value?)- performsclickortypeon an@eNhandlebrowser_snapshot()- re-emits the token-compressed element map
Any agent host that reads stdio servers - Claude Code, Antigravity, Codex - declares one entry:
{
"mcpServers": {
"cdpx": {
"command": "cdpx",
"args": ["--mcp"]
}
}
}
CLI
For one-off automation the same engine is a CLI:
cdpx open "https://github.com/login"
cdpx type @e2 "octocat"
cdpx click @e4
Flag --headed runs a visible browser; the default is headless.
Build numbers
The whole engine is 1,145 lines of Rust across six source files, plus one embedded 4KB script. Release profile compiles with opt-level = "z", lto = true, codegen-units = 1, panic = "abort", and strip = true to keep the binary at 1.2MB and RSS under 25MB.
cdpx is published on crates.io and the source lives at github.com/AkashPriyadarshii/cdpx.
More Essays
Building jev-curate: Fast Synthetic Dataset Sifter in Rust
Filtering synthetic training data with TypeSafe AI Jev: streaming JSONL and Parquet rows through calibrated System One gates at 70ms latency with zero memory accumulation. � 5 min
systemsBuilding jev-git: Sub-Second Git Reflex Gate in Rust
Screening staged git diffs for leaked secrets, destructive payloads, and AI hallucinations in 80ms using TypeSafe AI Jev System One. � 4 min
systemsBuilding jev-scout: Zero-Hallucination Crate and Repo Scout
Preventing AI package hallucinations: discovering real crates and GitHub repositories using live registry APIs and TypeSafe Jev speculative fan-out scoring. � 4 min