Skip to content

maksugr/acp-devtools

Repository files navigation

ACP Devtools

npm version npm downloads CI License: MIT Node

Architecture: editor and agent exchange ACP frames through the acp-devtools proxy, which feeds three consumer interfaces — CLI, UI, and MCP.

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 ↓

A 16-second tour: pick a session, click a frame to see its payload, cycle the Tree/Raw/Meta/Spec tabs, open the session-info drawer, open the performance dashboard with its waterfall, diff two sessions, switch theme.

What is ACP?

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.

Who it's for

  • 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/prompt took 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).

Features

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 clustersagent_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.

Quickstart

Install via npm

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-discoverable

Install via Homebrew

brew tap maksugr/tap
brew trust maksugr/tap     # required once — Homebrew 5+ blocks third-party taps by default
brew install acp-devtools

Run the inspector

acp-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 from which acp-devtools — GUI apps inherit a minimal PATH that 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:

The inspector

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.

The CLI

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 help

Full reference: docs/cli.md. Task-driven walkthroughs (headless debugging, A/B two agents, mock-based CI): docs/recipes.md.

MCP

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/prompt between 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 mcp

Stdio 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.

Supported editors & agents

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.

Editors

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

Agents

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)

Launch shortcuts

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…].

Architecture

   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.

Security & privacy

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.

What ACP captures contain

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 — redacts by default

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.

What it does not redact (your call)

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.

Sharing for a bug report

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.

Reporting a security issue

Email security concerns to maksugr@gmail.com with subject prefix [acp-devtools security]. Please don't open a public issue for actual disclosures.

Playground

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.

Documentation


Co-developed with Claude Code (Opus). Pair-programmed, but every line was read, shaped, and handcrafted by an experienced human, with love 🖤

License

MIT — see LICENSE.

About

See exactly what your editor and your coding agent say to each other — a transparent ACP proxy with a live web inspector, CLI, and read-only MCP server.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors