Give your coding agent a browser, on Nix
Install Nix, add one APM dep, and your agent — Claude Code, Codex, or OpenCode — gets a real Chrome to drive: DOM inspection, screenshots, network traces, heap snapshots.
If you’ve ever wanted your AI coding agent to actually see your app running — take a screenshot, dump the DOM, check the network panel, snapshot the JS heap — Google ships an MCP server for exactly that: chrome-devtools-mcp. It speaks the Model Context Protocol, so any compliant agent harness can plug into it.
Wiring it into your project used to be irritating — you needed a Chrome binary, a launcher script, and a per-runtime config block (one for Claude Code, another for Codex, another for OpenCode). This guide gets you to a working setup by leaning on two tools you may not yet be using together: Nix and APM.
One-sentence pitches: Nix gives you reproducible binaries — Chrome, Node, anything — without manual installers. APM is the package manager for AI agent context: like
npm, but the things you install are skills and MCP servers rather than libraries.
Step 1 — Install Nix
Follow nixos.asia/en/install. The installer takes a minute on macOS or Linux. Verify:
nix --version
If you used the nixos.asia path, flakes are already enabled. If you installed Nix some other way, add this to ~/.config/nix/nix.conf:
experimental-features = nix-command flakes
Step 2 — Drop one dep into apm.yml
APM is a Python tool. You don’t need to install it — Nix can pull uvx on demand, and uvx in turn fetches APM from its Python package on first run:
nix shell nixpkgs#uv -c uvx --from apm-cli apm --version
In your project root, create apm.yml:
name: my-project
version: 0.1.0
targets:
- claude # for Claude Code
- codex # for OpenAI Codex CLI
- opencode # for OpenCode CLI
dependencies:
apm:
- juspay/nix-chrome-devtools-mcp
Keep only the targets you actually use. Then install:
nix shell nixpkgs#uv -c uvx --from apm-cli apm install --target claude,codex,opencode
APM clones juspay/nix-chrome-devtools-mcp, deploys its launcher into .agents/skills/nix-chrome-devtools-mcp/bin/serve, and writes a per-runtime MCP config for each target — .mcp.json for Claude Code, .codex/config.toml for Codex, opencode.json for OpenCode.
Step 3 — Verify
Open .mcp.json (or your runtime’s equivalent). You should see a chrome-devtools server entry:
{
"mcpServers": {
"chrome-devtools": {
"command": ".agents/skills/nix-chrome-devtools-mcp/bin/serve",
"args": [],
"type": "stdio"
}
}
}
Launch your agent and ask:
Open https://kolu.dev in a fresh page and tell me what’s in the H1.
Under the hood the agent calls mcp__chrome-devtools__new_page, then mcp__chrome-devtools__take_snapshot, and inspects the DOM tree. A non-exhaustive shopping list of what’s now available:
new_page,navigate_page,close_page— page lifecycletake_snapshot,take_screenshot— DOM + image captureevaluate_script— run arbitrary JS, get the result backlist_console_messages,list_network_requests— observability panestake_memory_snapshot,performance_start_trace— perf + heap
What we actually use it for
The first use is iteration. The agent opens the page it just changed, takes a snapshot or screenshot, sees whether the output matches what it intended, and adjusts before moving on. It pulls console errors with list_console_messages, runs assertions through evaluate_script, checks network behavior with list_network_requests — the same DevTools panels a human would reach for, the agent reaches for too, on its own work. The feedback loop closes inside the agent’s own turn.
The second use is PR evidence. Our /do workflow (from srid/agency) ends with an evidence step: spawn a dev server on a free port, point chrome-devtools-mcp at the relevant routes, run take_screenshot, upload to a long-lived evidence-assets GitHub release, embed the URLs in the PR comment. The reviewer sees exactly what changed without checking out the branch.
Here’s what an evidence comment looks like, lifted from Kolu PR #835:

(See also #866 for a cross-palette icon check, and #867 for a three-step diagnostic table comparing master vs the fix.)
Overrides (optional)
The launcher honours two env vars, set before you launch the agent:
| Var | Default | Effect |
|---|---|---|
NIXPKGS_FLAKE | nixpkgs | which nixpkgs to pull Chrome + Node from. Override to bump the Chrome milestone — e.g. NIXPKGS_FLAKE=github:NixOS/nixpkgs/nixpkgs-unstable. |
CHROME_DEVTOOLS_MCP_VERSION | latest | npm version of the MCP server itself. Pin to 0.26.0 (or any release) when you need reproducibility. |
What’s actually happening
bin/serve is twenty lines of bash. It runs nix build nixpkgs#playwright-driver.browsers to materialise Chrome-for-Testing (the same Chrome binary the Playwright project bundles), then nix shell nixpkgs#nodejs --command npx -y chrome-devtools-mcp@latest --executable-path=$chrome to start the server. All inputs are resolved through Nix; nothing gets dropped into your project tree as a side-effect.
Where next
juspay/nix-chrome-devtools-mcp— the package itself, plus README.- APM’s docs —
apm.ymlschema, install / compile semantics. chrome-devtools-mcpdocs — full tool list, configuration flags, troubleshooting.