A live mitmproxy viewer that shows Claude Code API traffic in the browser in real time — system prompt, tool definitions, messages, thinking, tool calls, response, and token usage.
┌──────────────┐ HTTPS ┌───────────┐ parse + reassemble ┌──────────┐
│ Claude Code │ ─────────▶ │ mitmproxy │ ─────────────────────▶ │ Hub + │
│ │ proxy │ + addon │ (live_viewer.py) │ SSE feed │
└──────────────┘ └───────────┘ └────┬─────┘
│ SSE /events
┌────▼─────┐
│ live.html│ http://localhost:8082
└──────────┘
- Terminal 1 — start proxy + viewer:
make capture
- Open the viewer: http://localhost:8082
- Terminal 2 — start Claude Code through the proxy:
make claude
- Use Claude Code. Traffic appears live at http://localhost:8082.
- Stop:
Ctrl-Cin both terminals.
First HTTPS run needs the mitmproxy CA trusted
(~/.mitmproxy/mitmproxy-ca-cert.pem); NODE_EXTRA_CA_CERTS in the claude
target handles that for the Node CLI.
Ports: viewer :8082 · mitmweb proxy :8080 · mitm raw UI :8081.
| file | what |
|---|---|
live/live_viewer.py |
mitmproxy addon: filters the Anthropic Messages API, reassembles flows, serves live.html + /events (SSE) from a stdlib ThreadingHTTPServer. |
live/reassemble.py |
Pure-stdlib parsing: Anthropic SSE → message object, new_start diff, labels. No third-party imports. |
live/live.html |
The viewer (source of truth — edit directly). |
live/test_reassemble.py |
Offline tests for the reassembler (make viewer-test). |
- Anthropic Messages API (
/v1/messages) — streamed (SSE) and non-streamed. Streaming is reassembled:text/thinkingdeltas concatenated,signaturecaptured,tool_useinput_json_deltafragments joined then JSON-decoded, usage merged acrossmessage_start(input/cache) andmessage_delta(output/stop_reason). - Errors (HTTP ≥ 400) render as a response block with the error body.
Claude Code sends context_management: {edits:[{type:"clear_thinking_…"}]},
which strips the plaintext of thinking blocks from assistant history before
the request leaves your machine (only the signature survives) — so those
blocks arrive empty and the proxy can't read text that was never transmitted.
The addon keeps a signature → text map of every thinking block it does see
with text (e.g. on the response of the turn that first produced the thought).
When a later request echoes that same thinking signature with the text cleared,
the addon backfills the original and the viewer flags it ↺ recovered via
signature. If the text was never on the wire for a signature, the block stays
empty with an honest "cleared by context_management" note rather than a guess.
Skills and MCP need no special capture — they arrive as ordinary tool
definitions + system text. The sidebar badges Skill and mcp__* tools so
they're easy to spot.
| env | default | meaning |
|---|---|---|
LLM_VIEWER_PORT |
8082 |
viewer HTTP/SSE port |
LLM_VIEWER_BUFFER |
500 |
pairs kept for replay to newly-opened tabs |
LLM_VIEWER_DEBUG |
0 |
when 1, dump raw captured flows to live/debug/ (gitignored) |
Sidebar lists turns (model, msg count, duration). Follow auto-selects each new request; click any turn to pin it (turns off Follow). Clear empties the view. The dot shows live-connection state; the page auto-reconnects if the proxy restarts.
Each panel (Request / Response) has a Visual ⇄ JSON switch in its header. Visual is the rendered, foldable layout; JSON shows the original request / reassembled-response payload as a collapsible, syntax-highlighted tree — expand the node behind any block to see exactly how it looked on the wire, or hit ⧉ Copy raw JSON. The two panels toggle independently.