cdp

Go Reference

cdp is a Go module for Chrome DevTools Protocol automation. It includes a live browser CLI, a txtar script runner, and a Go test harness for repeatable browser workflows.

The browser-automation stack has three primary layers:

For MCP actions tied to an inspected page, see browser observations and actions.

Commands

Install

The module requires Go 1.26 or newer.

Install the broad CDP CLI:

go install github.com/tmc/cdp/cmd/cdp@latest

Install individual tools:

go install github.com/tmc/cdp/cmd/chrome-to-har@latest
go install github.com/tmc/cdp/cmd/cdp@latest
go install github.com/tmc/cdp/cmd/churl@latest
go install github.com/tmc/cdp/cmd/chdb@latest
go install github.com/tmc/cdp/cmd/ndp@latest
go install github.com/tmc/cdp/cmd/cdpscript@latest
go install github.com/tmc/cdp/cmd/cdpscripttest@latest

Quick Start

Attach to a browser the user is already using:

cdp attach --port 9222
cdp --remote-host localhost --remote-port 9222 --tab <target-id> --shell
cdp --remote-host localhost --remote-port 9222 --tab <target-id> --await --format json --js 'document.title'

Run a repeatable browser script:

cdpscript script.txtar
cdpscript --tab <target-id> --port 9222 script.txtar
cdp run --tab <target-id> --port 9222 script.txtar

Test browser behavior from Go:

e := cdpscripttest.NewEngine()
cdpscripttest.Test(t, e, allocCtx, "http://localhost:8080", "testdata/*.txt", nil)

For cdpscript-format txtars, use cdpscripttest.RunCDPScript so tests run the same runtime used by cdpscript and cdp run.

err := cdpscripttest.RunCDPScript(ctx, "testdata/login.txtar", cdpscripttest.CDPScriptRunOptions{
	Headless: true,
	Env:      []string{"BASE_URL=http://localhost:8080"},
})

Capture network activity with chrome-to-har:

go install github.com/tmc/cdp/cmd/chrome-to-har@latest
chrome-to-har --url https://example.com --output out.har

Use --shell when you want to browse interactively while recording:

cdp --url https://example.com --har out.har --shell

Evaluate JavaScript in a page:

cdp --headless --url https://example.com --js 'document.title'

Fetch a page with browser execution:

churl https://example.com

Attach to a Node inspector target:

ndp node attach 9229

Common Tasks

Capture authenticated browser traffic with an existing profile:

chrome-to-har --profile "Default" \
  --url https://app.example.com \
  --wait-for '#app-root' \
  --output app.har

Render a JavaScript-heavy page to text:

churl --output-format=text --wait-for ".loaded" https://example.com

Take a screenshot or extract content from a page:

cdp --url https://example.com --screenshot 'full page.png'
cdp --url https://example.com --extract 'h1'
cdp --url https://example.com --render body

Connect to an existing Chrome instance:

cdp attach --port 9222
cdp --remote-host localhost --remote-port 9222 --tab <tab-id> --js 'document.title'

Connect to a remote browser:

cdp --remote-host 10.0.0.5 --remote-port 9222 --list-tabs
churl --remote-host 10.0.0.5 --remote-port 9222 https://example.com

Use ndp against a Node inspector:

node --inspect=9229 app.js
ndp node attach 9229

Run and test CDP scripts:

cdpscript login.txtar
cdpscripttest --url http://localhost:8090 testdata/login.txt

What chrome-to-har Does

chrome-to-har is the focused capture entry point. It is useful when you want HAR output, streaming entry logs, profile-based browsing, or differential capture that compares two runs of the same page.

What cdp Does

The main cdp command is the broader general-purpose entry point. It goes beyond chrome-to-har and can:

What churl Does

churl is a browser-powered fetch tool for pages that need JavaScript execution. It is useful for:

What ndp and chdb Do

ndp focuses on Node/V8 debugging flows. chdb focuses on Chrome-oriented debugging flows. Both are still evolving, but they are intended to expose debugger-oriented workflows rather than generic browser automation.

Automation Stack

cdp is generalized browser scripting — the live operator surface. Use it when you need to attach to an existing browser, inspect tabs, observe with screenshots, act with coordinates or element refs, capture HAR/PDF/screenshots, or fall back to raw CDP.

cdpscript is Unix-like tooling for browser operations — the durable automation surface. Scripts are txtar archives with main.cdp, optional embedded helper files, argv via ARG1..ARGN/ARGC, environment variables, output artifacts, and distinct exit codes for usage and assertion failures. A script with a #!/usr/bin/env cdpscript line and the executable bit is a command in its own right — its comment section becomes --help text:

chmod +x greet.cdpscript
./greet.cdpscript https://example.com

cdpscripttest is Go-native browser testing — the testing surface. It runs local browser fixtures, screenshot comparisons, network/WebRTC tests, and real cdpscript archives under go test. Fixtures should avoid third-party network dependencies; authenticated site workflows should live as explicitly live-only examples.

Stability and Compatibility

The module is pre-v1, so nothing here carries a v1 compatibility promise yet. The three surfaces are not equally settled, and the script DSL is deliberately the most stable of them.

Script DSL (main.cdp commands, exit codes). Treated as a contract. Command names and argument order do not change meaning, a renamed command keeps its old name as an alias, and new behavior arrives as optional flags. Scripts written today are expected to keep running. cdpscripttest canonical names are hyphenated (navigate, wait-visible) and also register the shell spellings (goto, wait) as aliases; both are kept working.

Go API (cdpscript, cdpscripttest). Stable in shape, not frozen. The engine types, the Execute methods, and the option constructors are unlikely to move; options are added over time. An exported option taking a type from an internal/ package may be removed outright, since no code outside this module can call it. Other deprecations are marked in godoc for at least one release before removal.

MCP tool set (cdp --mcp). The least stable surface. Tool names, input schemas, and result shapes follow what the hosts need and may change without notice. Pin a commit if you depend on a specific schema.

The screen recorder, the WebRTC shim, and screenshot-comparison thresholds are test-harness machinery and track the browser behavior they wrap, even where their Go API is exported. Baseline images and pixel thresholds are not a compatibility contract.

Trust Model

cdp drives a real browser with the privileges of the user who starts it. It is not a sandbox.

On the command line and in .cdp scripts, the operator is the author. js/jsfile/eval run arbitrary JavaScript in the page, goto reaches any URL the host can reach including file:// and localhost, and screenshot/pdf/har/download-dir write where they are pointed — absolute paths verbatim, relative paths under -output-dir, unconfined either way. That is the intended power of a browser automation tool. A .cdp script is not a shell: the engine registers only its own commands, not rsc.io/script’s defaults, so there is no exec, rm, or cp.

Under --mcp the author changes. The caller is an agent, and everything above reaches it. Three tools are stronger than “run JavaScript in a page”: evaluate and raw_cdp expose the full page-JS and CDP surfaces, upload_file reads any absolute path on the host and can hand it to a page the same agent navigated to, and run_cdpscript runs agent-supplied script text with the server’s own environment — credentials included — exposed as ${NAME}. Tool and context names are validated so define_tool and push_context stay inside their directories, but artifact paths are not confined. Run the server with an environment and working directory you would hand to whatever is on the other end.

Captures are redacted by default — request headers and query parameters, request and response bodies, WebSocket frames, and saved sources, unless --no-scrub is set. Text over 512 KiB passes through unscrubbed (a deliberate trade for capture speed on large bundles), and scrubbing covers captured artifacts rather than tool results: get_cookies returns cookie values as they are, and save_state writes them to disk in cleartext.

Full statement: go doc github.com/tmc/cdp/cmd/cdp, section “Trust model”.

Documentation

Each command documents its own flags and subcommands, checked by a test:

go doc ./cmd/cdp
go doc ./cmd/churl
go doc ./cmd/chrome-to-har

The guides live in docs/, which opens with an “I want to…” table covering every page. The usual entry points:

For command-level help, use:

cdp --help
churl --help
chdb --help
ndp --help
cdpscript --help
cdpscripttest --help
Last updated: 2026-09-23