See exactly what your editor and your coding agent say to each other.
ACP Devtools captures every JSON-RPC frame between them, stores each session in SQLite, and streams it to a live web inspector — replay, diff, spec-validation, plus a CLI and a read-only MCP server.
Try it in your browser → opens the playground pre-loaded with a sample session. See the timeline, inspect frames, open the perf panel. Drop your own session.json to inspect your traffic. More about the playground ↓
The Agent Client Protocol is an open, newline-delimited JSON-RPC wire format that lets an editor (Zed, WebStorm, IntelliJ, Neovim, Visual Studio via ReSharper) drive a coding agent (Claude Code, Codex, Goose, OpenCode, and 40+ others) over stdio, without either side knowing the other's implementation. ACP Devtools sits in the middle of that stdio pipe — neither side knows it's there.
Never seen the wire before? Anatomy of an ACP session walks one real capture frame by frame — handshake, prompt turn, a tool call behind a permission dialog, a cancelled turn — and it's the same session the playground loads, so you can click along.
- Agent authors — see exactly what an editor sends, and validate your wire output against the spec.
- Editor / plugin developers — see what the other side sends (WebStorm and
Zed disagree on capabilities,
_meta, and id format) and test against recorded traffic without burning tokens. - Anyone debugging a chat — find why
session/prompttook 60s, or replay yesterday's broken session and step to the failing tool call. - CI — gate releases on spec conformance and latency regressions, headless (exit-code-driven recipe).
Transparent proxy — captures every frame in both directions
Sits between editor and agent over stdio (newline-delimited JSON-RPC 2.0). Neither side knows it's there.
Timeline + JSON detail — vertical scroll of every frame, click for the payload
Each row shows direction, kind, method, rpc id, size, and latency to its paired
request. The detail panel renders the full payload as a spec-aware tree —
hover any field for its schema description, ⚠ ext badges mark _meta and
fields not declared in the spec.
Stream clusters — agent_message_chunk runs collapse to one STR row
A shimmer bar marks the cluster while chunks are still arriving — tells "agent thinking" apart from "agent stuck". Click to expand the individual chunks.
Spec validation — every frame against the official ACP JSON schema
Invalid frames get a red ⚠ SPEC N badge in the timeline, per-error ajv
details in the detail panel, and a footer aggregate across the session. CLI:
acp-devtools validate <id> exits 1 on violations — wire into CI.
Performance dashboard — per-method p50/p99/max + waterfall + insights
Sortable table with latency sparklines, auto-detected insights (hotspot,
long-tail, outlier, busiest, errors), and a waterfall canvas with
gap-compression for multi-hour sessions. CLI mirror: stats <id> --by-method
— same percentile algorithm, same numbers to the millisecond.
Multi-session diff — frame-level + metadata + per-method p99 deltas
LCS-aligned frame view with click-to-expand field-level changes, plus a
metadata diff layer (versions, capabilities) and a per-method p99-delta layer.
CLI: diff <a> <b> (add --json for machine output).
Session metadata — versions, capabilities, mode/model, slash commands
Derived from the captured frames — client/agent versions, capability matrix,
runtime mode/model, available slash commands, JetBrains _meta.proxyConfig.
Drawer in the UI; CLI: session-info <id>; MCP: get_session_summary.
Cross-session search — full-text across every saved frame
Click a result to jump to that row in the timeline. CLI: search <pattern>
with grep-style exit codes (1 if no match).
Replay — play/pause/speed/seek through a saved session
Every frame is on screen from the start; a left-rail playhead marks the
current position and the timeline follows it as it runs (0.5×–8× speed via
the scrubber). CLI: replay <id> rebroadcasts over WebSocket on a fixed
port for repeatable demos.
Export / import — JSON dumps you can share or re-import
Two engineers can debug the same capture without spinning up an editor.
Imported sessions appear in the picker tagged IMPORTED. export redacts
auth headers and proxy tokens (including JetBrains proxy_key) by default
— see Security & privacy.
Mock agent / editor — record-replay primitives for CI
mock-editor --script <export.json> drives a real agent with recorded editor
traffic (no IDE, no tokens burned); mock-agent does the inverse. Pair with
validate and stats --json for headless spec/latency gating.
Read-only MCP server — 11 tools so an AI agent can query your captures
Tools: list_sessions, get_session_summary, find_spec_violations,
diff_sessions, search_messages, and 6 more. Wire via acp-devtools mcp
and add to your AI agent's MCP config.
Concurrent captures — multiple editor windows in one inspector tab
Every capture registers in ~/.acp-devtools/active/<pid>.json with an
ephemeral port. The session picker auto-discovers them — no port conflicts
even with several chats open.
npm install -g acp-devtools-g is the recommended install — it puts acp-devtools on your PATH, which
is how editors (Zed, JetBrains, Neovim) find it when they spawn the proxy as a
subprocess. Without it, every editor config has to use an absolute path.
If you only want to try the inspector once without installing, npx works too:
npx acp-devtools ui # downloads on first run, slower start; cache path is not PATH-discoverablebrew tap maksugr/tap
brew trust maksugr/tap # required once — Homebrew 5+ blocks third-party taps by default
brew install acp-devtoolsacp-devtools ui # → http://127.0.0.1:3737/ (auto-opens)Connect any editor (Zed as an example) — open ~/.config/zed/settings.json (Cmd+,) and merge:
{
"agent_servers": {
"Claude Code (via ACP Devtools)": {
"type": "custom",
"command": "acp-devtools"
}
}
}That's the whole config for the default Claude Code setup — acp-devtools
detects it was spawned by an editor (stdin is a pipe) and runs
proxy --agent claude-code internally. Send a prompt → the proxy spawns the
agent, and within a second the inspector shows the handshake:
┌─ SESSION #1 · LIVE ────────────────────────────────────┐
│ #1 18:34:06 → OUT REQ initialize id:0 │
│ #2 18:34:06 ← IN RSP — +402ms │
│ #3 18:34:06 → OUT REQ session/new id:1 │
│ #4 18:34:08 ← IN RSP — +1.84s │
│ #5 18:34:15 → OUT REQ session/prompt id:2 │
│ #6 18:34:16 ← IN STR agent_message_chunk ×42 │
└────────────────────────────────────────────────────────┘
If the timeline stays empty after a prompt, the editor most likely couldn't find the binary:
If editor reports "agent command not found", replace
"acp-devtools"with the absolute path fromwhich acp-devtools— GUI apps inherit a minimalPATHthat often misses Node binaries.
Per-editor setups — each covers Claude Code, Codex, Goose, OpenCode, and how to wrap a custom agent — plus a Claude Code multi-profile / auth recipe:
- Zed setup with every agent
- JetBrains setup (WebStorm / IntelliJ / PyCharm / …)
- Claude Code multi-profile (personal vs work OAuth)
A vertical timeline of every frame plus a JSON detail panel. Frames stream in live; clicking a row expands its payload, with latency annotations, stream clustering, spec badges, a performance waterfall, replay controls, and a session diff view. Full tour — labels, detail tabs, perf dashboard, diff panel, shortcuts: docs/ui.md.
Every UI control has a headless equivalent, colorized and grep/jq-friendly.
acp-devtools list # saved sessions, newest first
acp-devtools stats 23 --by-method # latency percentiles
acp-devtools inspect 23 --kind req --grep prompt # filtered timeline, in the terminal
acp-devtools diff 23 41 # what changed between two sessions
acp-devtools <command> --help # grouped, colorized helpFull reference: docs/cli.md. Task-driven walkthroughs (headless debugging, A/B two agents, mock-based CI): docs/recipes.md.
acp-devtools mcp exposes saved captures to an AI agent (Claude Code, Claude
Desktop, …) as eleven read-only tools so you can ask it to investigate your own
traces:
"find spec violations in the last 10 sessions" · "compare p99 of
session/promptbetween WebStorm and Zed" · "diff sessions 41 and 42 — what changed?"
Setup in one command (Claude Code as an example):
claude mcp add acp-devtools -- acp-devtools mcpStdio only; no network surface. Auth tokens and proxy keys are unconditionally redacted in every tool response — see Security & privacy.
Full tool reference and setup variants for other MCP clients: docs/mcp.md.
The tables below cover the ACP ecosystem directory, with two verification levels:
verified means we captured a full prompt turn through acp-devtools;
handshake means the agent answered initialize / session/new through
the proxy and a full turn only needs an account for that agent. Every
verified and handshake row links its proof twice: playground opens the
actual capture in the playground in one click, json is the
same redacted export in fixtures/handshakes/ for
acp-devtools import or your own tooling.
Everything else should work but hasn't crossed our wire yet: run
fixtures/drive-full-turn.mjs against it
(node fixtures/drive-full-turn.mjs <agent-command> — exit 0 means a full
prompt turn worked; exit 3 means the agent answered the handshake but needs
an account for a full turn) and open an
issue with an acp-devtools export attached (auth tokens are redacted on export) — we'll mark the agent
verified in the table.
| Editor | ACP support | Status |
|---|---|---|
| Zed | native | verified — Zed 1.3.5–1.5.4 · playground · json · setup |
| JetBrains IDEs | native (AI Assistant) | verified — WebStorm 2026.1.2 · playground · json · setup |
| Visual Studio Code | vscode-acp extension |
untested |
| Neovim | CodeCompanion, avante.nvim | untested |
| Emacs | agent-shell package |
untested |
| Obsidian | Agent Client plugin | untested |
| marimo | built into the notebook | untested |
| AionUi | desktop GUI | untested |
| DeepChat | desktop chat app | untested |
| Tidewave | web app | untested |
| aizen | desktop app | untested |
| Sidequery | browser-based, announced | untested |
| Web Browser (AI SDK) | @mcpc/acp-ai-provider |
untested |
| Agent | Status |
|---|---|
| AgentPool | untested |
| Agoragentic | untested |
| Amp | handshake — amp-acp 0.8.1 · playground · json |
| Augment Code | handshake — auggie 0.29.0 · playground · json |
| Autohand Code | untested |
| Blackbox AI | untested |
| Claude Code | verified — claude-agent-acp 0.37–0.44 · playground · json |
| Cline | untested — CLI 3.0.24 exposes no ACP mode |
| Code Assistant | untested |
| Codebuddy Code | untested |
| Codex CLI | handshake — codex-acp 0.16.0 · playground · json |
| Cortex Code | untested |
| Corust Agent | untested |
| crow-cli | fails — 0.3.0 npm binary links Intel-Homebrew libgc, crashes on Apple Silicon |
| Cursor | verified — cursor-agent 2026.05.16 · playground · json |
| DeepAgents | untested |
| DimCode | untested |
| Dirac | untested |
| Docker cagent | untested |
| Factory Droid | untested |
| fast-agent | untested |
| Gemini CLI | handshake — gemini-cli 0.46.0 · playground · json |
| GitHub Copilot | handshake — copilot 1.0.61 · playground · json |
| GLM Agent | untested |
| Goose | verified — goose 1.37.0 · playground · json |
| Grok Build | untested |
| JetBrains Junie | untested |
| Kilo | untested |
| Kimi CLI | handshake — Kimi Code CLI 1.47.0 · playground · json |
| Kiro CLI | untested |
| Minion Code | untested |
| Mistral Vibe | handshake — vibe 2.15.0 · playground · json |
| Nova | untested |
| OpenCode | handshake — OpenCode 1.17.4 · playground · json |
| OpenHands | untested |
| Pi | untested |
| Poolside | untested |
| Qoder CLI | untested |
| Qwen Code | handshake — qwen-code 0.17.1 · playground · json |
| siGit Code | untested |
| Stakpak | untested |
| VT Code | fails — vtcode acp 0.52.8 exits silently (no response to initialize) |
The built-in registry maps four common agents to ready commands. npm-based
agents auto-install on first use via npx -y …; binary-based agents need a
one-time install.
| Shortcut | What it runs | Install |
|---|---|---|
claude-code (default) |
npx -y @agentclientprotocol/claude-agent-acp |
npx — automatic |
codex |
npx -y @zed-industries/codex-acp |
npx — automatic |
goose |
goose acp |
install Goose from https://goose-docs.ai first |
opencode |
opencode acp |
curl -fsSL https://opencode.ai/install | bash |
Every other agent runs through the explicit form:
acp-devtools proxy <your-command> [args…].
Editor (Zed / JetBrains / Neovim / VS via ReSharper)
│ ACP via stdio (newline-delimited JSON-RPC 2.0)
▼
┌─────────────────────────────────────────┐
│ acp-devtools proxy │
│ spawns the agent │
│ captures every frame in both directions│
│ writes ~/.acp-devtools/captures.db │
│ broadcasts on a local WebSocket │
│ registers ~/.acp-devtools/active/ │
└──────────────┬──────────────────────────┘
│ stdio
▼
ACP agent (claude-agent-acp, codex-acp, …)
Browser ◄── HTTP ──── acp-devtools ui (3737)
serves the React bundle + discovers live captures
Neither side of the pipe knows the proxy exists. Multiple captures (one chat per editor window) coexist on independent ephemeral ports and all appear in the inspector's session picker.
ACP traffic carries real secrets. Anything sent on the wire is captured
verbatim into captures.db, including the JetBrains AI gateway token
WebStorm puts on every initialize. acp-devtools is local-first by
design — nothing leaves your machine unless you choose to share an export.
This section spells out what's at risk and what the tool does about it.
| Source | Lives in | Sensitive? |
|---|---|---|
initialize._meta.proxyConfig.proxies[].proxy.headers |
every WebStorm session | YES — JetBrains LLM gateway auth (proxy_key) |
HTTP-style Authorization / X-Api-Key / Cookie headers in any field |
uncommon but possible in custom agents / _meta extensions |
YES |
fs/read_text_file results |
every session that opens files | depends — proprietary source vs. public code |
| Prompts you typed and model responses | every session | depends — internal context vs. generic question |
| Method names, latencies, frame counts, schema shapes | every session | no — useful for bug reports |
The capture file stays on your machine; nothing in the tool uploads it. The risk surface is when you share an export — and that's what default redaction targets.
acp-devtools export 21 > capture.json # safe-by-default for sharing
acp-devtools export 21 --raw # opt-in: keep everything (self-debug only)What gets replaced with <REDACTED>: every string value under any
proxyConfig.proxies[*].proxy.headers.* subtree (catches future JetBrains
fields, not just proxy_key), plus standard HTTP auth headers
(Authorization, X-Api-Key, Cookie, …) anywhere in the JSON. Full list
lives in packages/core/src/storage/redact.ts.
Redaction rewrites both payload (parsed) and raw (wire string), so
the secret can't leak via either field. A summary lands on stderr:
redacted N field(s) across M message(s) — re-run with --raw to keep them.
The same default applies everywhere output is likely to leave your machine:
export, the UI's download button, every MCP tool response, diff, and
session-info (the last two take the same --raw opt-out). Two surfaces
stay raw on purpose: inspect prints the actual wire bytes for local
triage, and the local HTTP API the UI reads
(GET /api/sessions/:id/messages) returns frames as captured — treat both
like the database file itself, not like an export.
File contents loaded via fs/read_text_file and the prompts / responses
stay as-is. There's no reliable heuristic for "is this code proprietary" —
that's a judgement call. Before sharing, audit with:
acp-devtools inspect 21 --kind ntf # all notifications (responses, streaming, tool calls)
acp-devtools inspect 21 --grep proxy_key # see which frames carry tokens (inspect shows raw bytes)If something in those fields shouldn't ship, either drop the offending
messages with jq before sharing, or capture a smaller reproducer with
fresh sessions.
A typical Zed / agent-author bug report:
acp-devtools export 21 > acp-bug.json # auto-redacted
acp-devtools inspect 21 --grep <secret> # double-check nothing slipped through
# Attach acp-bug.json to the GitHub issue.The playground lets the reviewer drop acp-bug.json into
a browser and see the same timeline you saw — no install on their side.
Email security concerns to maksugr@gmail.com with subject prefix
[acp-devtools security]. Please don't open a public issue for actual
disclosures.
The inspector also runs as a static page at
playground.acp-devtools.dev —
drop a session.json export (or a public gist URL) into a browser and
you get the same timeline you'd get locally. No backend; nothing
uploaded to any server we run (we don't run any). Built from this repo
with VITE_PLAYGROUND=1 and published to GitHub Pages on every push to
main.
This gist sample pre-loads a real WebStorm ↔ claude-agent-acp session (97 frames: handshake, a tool call behind a permission dialog, a cancelled turn) — the same capture Anatomy of an ACP session walks through frame by frame. Here's how that flow works: the playground fetches the JSON export from the gist URL and renders it client-side — the gist is the storage, GitHub serves it over CORS, the playground just renders.
- Anatomy of an ACP session — how to read a capture: handshake, prompt turn, what broken looks like
- CLI reference — every command, flag, and sample output
- The inspector (UI) — timeline, detail panel, perf, diff
- MCP server — tools and setup
- Recipes — headless debugging, diffing, mock-based testing
- Changelog — what shipped in each release
- Contributing — build from source, layout, conventions
Co-developed with Claude Code (Opus). Pair-programmed, but every line was read, shaped, and handcrafted by an experienced human, with love 🖤
MIT — see LICENSE.
