Skip to content

Repository files navigation

cctrace

cctrace

See what your coding agent really sends.

Every request Claude Code makes -- messages, OAuth, usage/credits, MCP -- captured live in your browser. Codex, Grok, and Kimi Code too.

English | 简体中文

tests version license runtime

Docs · Install · Web UI · Saved traces · Beyond Claude · llms.txt

AI agents / LLMs: read /llms.txt; an agent skill ships in skills/cctrace.

cctrace live demo

cctrace sits between your coding agent and its API, recording every HTTP call to a live categorized web UI and a .jsonl trace you can reopen any time with cctrace view. No cloud, no account, nothing leaves your machine.

cctrace                # trace Claude Code
cctrace codex          # or the OpenAI Codex CLI
cctrace grok           # or the Grok CLI
cctrace kimi           # or the Kimi Code CLI (Moonshot AI)
cctrace opencode       # or opencode (any provider it routes to)

That's it. The agent launches normally. You get a browser tab showing everything it does.

Why

cctrace is built for exactly two jobs:

  1. LLM tracing -- see exactly what your agent sends and receives each turn: system prompt, context, tool definitions, streamed replies, token/cache usage.
  2. Security & privacy tracing -- audit what actually leaves your machine: which hosts get contacted, what telemetry goes out, what's inside every payload.

Both jobs need the full picture -- every request, not just the convenient ones. Claude Code ships as a Bun-compiled native binary, so the classic node --require fetch-hook is dead. cctrace captures at the transport layer instead: a zero-config TLS-intercepting proxy (Charles-style) that the agent routes through via HTTPS_PROXY, trusting an auto-generated CA. Intercepting below where URLs are built is what reaches the OAuth and usage/credit endpoints a base-URL proxy physically cannot see -- and since 0.16 the scope is deliberate: first-party hosts are decrypted, everything else (npm, GitHub, apt) passes through as an opaque byte-counted tunnel.

What you get

  • The full picture. /v1/messages, OAuth, usage/credits, MCP registry, bootstrap, telemetry -- not just the chat endpoint.

  • Live, categorized UI. Filter chips with counts, decoded SSE streams, reasoning-effort and prompt-cache verdicts, first-token latency, estimated cost per request. The full tour.

  • Reconstructed sessions. Turns the way a human counts them (user request -> agent work -> final response; a 213-message trace reads as 3 turns), tool rows naming the files they touched, subagent branches, /model epochs, compaction boundaries, superseded exchanges -- and replay: step or play back any captured session, deep-link any moment -- on a stage: lanes over time (prompts, requests, tools, subagents, cuts), the agent's observed state diagram with the current transition lit, and the beat of what it did at this step. Live, the model node lights the moment a request leaves.

  • Context insights, DevTools-shaped. A Context view shows the agent's window over time. An interactive overview owns the top and never scrolls away: one stacked column per wire request, colored by the six things a window is made of, ✂ marking the compactions, and a second track showing where that step's wall-clock went (model / tools / waiting). Drag across it to select a range, drag the handles to resize it, wheel to zoom in, click a column to pin a step. A margin beside it states the balance for that step -- prompt tokens, a six-color composition bar against the model's context window, how far the chars/4 estimate reads under the billed prompt, and the six categories as lines you click to zoom. Then the selection is read three ways, one deck at a time:

    • window -- the pinned step as a context graph: an icicle where width is tokens and rows are levels, decomposing the request into category -> group -> item, with tool results grouped by the tool that made them, schemas by MCP server, injections by producer. Bash x189, 38% of the window is the widest block on the row, not a number you had to go find; click it to zoom, click a leaf to read the exact bytes, and every row says which turn first carried it in.
    • stream -- the agent's path as one linear list of records: system prompt, your turns, the context the harness injected (inline, at the moment it entered), the model's thinking, each tool call fused with its result, the reply. MAP / READ / FULL filters it down to the skeleton or up to everything without ever summarizing; a kind filter isolates just the injections -- the context trajectory.
    • events -- every injection, compaction, model switch and tool-schema change, with what it did to the window.

    Traces holding several sessions compare their peaks on one scale. Anchored to provider-reported tokens -- the wire, not a guess.

  • Where the quota goes. The overview carries a third track: what each step cost, stacked cache read / cache write / input / output -- and an amber $ on the steps that bought their prefix twice. The margin says where the money went (per component, per model), counts those bumps and names each cause off the wire: cache expired (1h ttl, 15h idle), prefix changed (tool schemas changed), retry after 529 -- each with what a warm cache would have saved. Beside it, the account's quota as the client polled it: 5h / 7d / model-scoped, percent, when it resets, and how far it moved across this trace. Every dollar is an estimate from catalog rates; every cause is a wire fact.

  • The trajectory, in the timeline. Every step on the sessions rail carries a track: how full the window was, split into the prefix read from cache and what was billed fresh. Down the rail that column is the agent's context trajectory -- it climbs, a compaction drops it, the next step runs cold, then warms again.

  • Replayable traces. Every run writes a .jsonl; cctrace view reopens it anytime, --html renders an offline snapshot you can send around.

  • One dashboard for everything. /dashboard on any instance shows every live run and every finished trace across all your projects -- grouped by project or client, with size/tokens/cost per run -- and any row opens as a rendered session view in one click.

  • Zero config. Auto-generates its CA, auto-detects your install, full first-party capture by default.

  • Scoped by design. External hosts your agent's subprocesses contact pass through as opaque tunnels (host + byte counts) -- a go install never lands 53MB of tarball in your trace. Details in capture modes.

  • Safe by default. Credentials are redacted from headers, bodies, and URLs before anything hits disk (see Security & privacy).

How it compares

cctrace base-URL proxy claude-trace (node --require) Charles / mitmproxy
Works on the native binary yes yes no yes
Captures /v1/messages yes yes yes yes
Captures OAuth / usage / credits yes no no manual
Zero config (auto CA + trust) yes yes yes no
Agent-aware UI (categories, sessions, SSE decode) yes -- partial no
Local-only, nothing leaves your machine yes yes yes yes

The fetch()-hook approach (claude-trace and friends) stopped working when Claude Code went native. A base-URL proxy still works but only sees /v1/messages. A general TLS proxy sees everything but needs manual CA setup and knows nothing about the endpoints. cctrace is the middle path: zero-config, whole first-party picture, and it speaks your agent's wire.

Quick start

Requires Bun, openssl, and the CLI you want to trace.

npm install -g @thevibeworks/cctrace    # or: bunx @thevibeworks/cctrace

Or build the standalone binary (recommended -- no Bun at runtime, exact -- pass-through):

git clone https://github.com/thevibeworks/cctrace && cd cctrace
make install                            # compiles, installs to ~/.local/bin

Then:

cctrace                                    # trace claude, open the live UI
cctrace -- --continue                      # resume your last session, traced
cctrace -- -p "hello"                      # args after -- go to the agent verbatim
[cctrace] Live UI: http://localhost:8722/trace
[cctrace] Capture: MITM proxy http://127.0.0.1:44775 (all Anthropic hosts)

Open the Live UI and watch requests stream in. Ctrl-C when done -- the trace lands in the store (~/.local/share/cctrace/traces/<project>/, one dir per project, archived to .jsonl.zst at exit); reopen anytime with cctrace view, see what the store holds with cctrace store. Install variants, runtime notes, and the bun -- caveat: docs/install.md.

Everyday commands

cctrace view                     # reopen a saved trace (Enter = newest)
cctrace view <target> --html     # render a shareable offline snapshot
cctrace ps                       # live instances: URL, client, project, session
cctrace history                  # every traced run, all projects, newest first
cctrace clean|merge|compress     # housekeeping -- dry-run by default, --yes applies
cctrace purge                    # drop noise categories from saved traces
cctrace compact                  # fold redundant bodies (-95%+), view unchanged

Housekeeping never shrinks your data (verified deletes, union merges, live-append safety); compact is the one stated exception. The full guarantees: docs/traces.md.

Common options

Option Description
--mode MODE auto (default), mitm, base-url, node
-p, --port PORT Live UI port (default: 8722, walks 8722..8821 when busy)
--messages-only Capture only the model API calls
--capture-external Decrypt every host (bodies over 64KB summarized)
--intercept-host H Also decrypt host H (repeatable -- remote MCP servers)
--bypass-host H Exempt host H from the proxy entirely (child NO_PROXY)
--dir PATH Log directory (default: the project's dir in the store)
--client-path PATH Custom binary path for any client

Full table incl. --fresh, --with, --data-dir, --print-ca: docs/install.md.

How it works

flowchart LR
    CC["Claude Code<br/>(native binary)"]
    FD{"cctrace<br/>CONNECT front door"}
    TLS["TLS terminator<br/>(our leaf cert)"]
    BT["TLS terminator<br/>(dynamic cert)"]
    TUN["opaque tunnel<br/>(byte counts only)"]
    API[("api.anthropic.com")]
    PIN[("pinned / enrolled<br/>host")]
    EXT[("external host<br/>npm · github · apt")]
    TEE(["tee response"])
    RD["redact<br/>headers · bodies · URLs"]
    UI["live UI<br/>(categorized)"]
    OUT[["store · jsonl.zst"]]

    CC -- "HTTPS_PROXY +<br/>NODE_EXTRA_CA_CERTS" --> FD
    FD -- "Anthropic host" --> TLS
    FD -- "include-listed host" --> BT
    FD -- "anything else" --> TUN
    TLS --> API
    BT --> PIN
    TUN --> EXT
    PIN -- "response stream" --> TEE
    API -- "response stream" --> TEE
    TUN -- "one meta row" --> RD
    TEE -- "streamed to Claude,<br/>no buffering" --> CC
    TEE -- "captured copy" --> RD
    RD --> UI
    RD --> OUT

    classDef accent stroke:#3fb950,stroke-width:2px;
    class RD accent
Loading

The proxy terminates TLS with an auto-generated leaf cert, forwards to the real API, and tees the response so the agent gets bytes immediately while cctrace captures a copy -- zero SSE buffering. Every captured pair is redacted before it reaches any sink. Subprocess trust (the combined CA bundle), why HTTP_PROXY stays unset, and the tunnel scope model: docs/capture-modes.md.

Security & privacy

cctrace is a local debugging tool, but it intercepts real credentialed traffic, so it redacts before writing anything:

  • Headers -- authorization, x-api-key, cookie, etc. masked to a first-10/last-4 preview (enough to tell which key, not the key itself).
  • Bodies -- credential fields (access_token, refresh_token, client_secret, api_key, ...) masked in JSON and form bodies. Your conversation content is left intact.
  • URLs -- credential-bearing query params (e.g. OAuth ?code=) masked.
  • Identity ids (session/user/device UUIDs) are not masked by default -- they're workflow identity, not credentials, and session-keyed features depend on them. Sharing a trace outside your machine? --redact-ids (or CCTRACE_REDACT_IDS=1) masks them too.

Redaction happens at a single choke point, so it applies uniformly to the .jsonl, the .html, and the live WebSocket. Traces live outside the project tree (the store under ~/.local/share/cctrace/), so nothing lands in your repo by accident.

Still: a trace is a record of your real session. Review it before sharing. Never paste raw output into a public issue. Seriously.

Docs

Start here Go deeper
Install & options Capture modes & proxy internals
The web UI tour Saved traces & housekeeping
Codex / Grok / Kimi / opencode / providers Agent skill · CHANGELOG

Roadmap

  • Session replay P3/P4 -- opt-in --record-timing for chunk-timed streaming replay (design).
  • WebSocket relay -- capture ws frames instead of the current fast refusal + HTTP fallback.
  • Conversation dump -- export the reconstructed conversation as Markdown or JSON.
  • MCP server -- query captured traffic from any agent (the agent skill already ships; the MCP surface is the remaining half).
  • Tunnel PID attribution -- which subprocess called npm (Linux, investigated, deferred).

Development

bun test                                # unit tests
bun run tests/e2e-live.ts mitm "hi"     # end-to-end against real Claude

See CONTRIBUTING.md.

License

MIT

About

See exactly what your coding agent does on the wire. TLS-intercepting tracer for the Claude Code, Codex, Grok and Kimi CLIs: full first-party capture in a live web UI — reconstructed sessions, session replay, find-in-session, per-turn cost/cache/latency, and a cross-project dashboard of every run.

Topics

Resources

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages