Intercept and visualize all communication between Claude Code CLI and the Anthropic API — requests, responses, and SSE streaming events.
pip3 install mitmproxy aiohttp./bin/trace_claude listenOutput:
✓ Proxy listening on http://127.0.0.1:8000
✓ Web UI at http://127.0.0.1:8001
✓ Logs: sessions/session_20260329_112707.jsonl
Run claude with:
trace_claude_run claude -p "hello"
Press Ctrl+C to stop.
./bin/trace_claude_run claude -p "hello"trace_claude_run automatically sets ANTHROPIC_BASE_URL=http://127.0.0.1:8000 then execs your command. All Claude Code arguments work as usual:
# Interactive mode
./bin/trace_claude_run claude
# Specify model
./bin/trace_claude_run claude --model opus -p "explain this code"
# With tool restrictions
./bin/trace_claude_run claude --allowedTools "Read,Grep" -p "find all TODOs"You can also skip trace_claude_run and set the environment variable manually:
ANTHROPIC_BASE_URL=http://127.0.0.1:8000 claude -p "hello"Navigate to http://127.0.0.1:8001
Features:
- Real-time timeline: API events displayed chronologically, new events auto-appended
- Event type filtering: REQ (request), HDR (response headers), SSE (streaming events), RES (full response), END (stream end)
- Click cards to expand/collapse JSON details
- Auto-scroll toggle button
- Expand All / Collapse All for batch operations
- Clear to reset the display
trace_claude listen [--port PORT] [--web-port WEB_PORT] [--sessions-dir DIR]
| Parameter | Default | Description |
|---|---|---|
--port |
8000 | mitmproxy reverse proxy listen port |
--web-port |
8001 | Web UI listen port |
--sessions-dir |
./sessions |
Directory for JSONL log files |
trace_claude_run <command> [args...]
Injects ANTHROPIC_BASE_URL then executes the specified command. Use TRACE_CLAUDE_PORT to specify a non-default proxy port:
TRACE_CLAUDE_PORT=9000 ./bin/trace_claude_run claude -p "hello"# Equivalent to ./bin/trace_claude listen
python3 -m trace_claude listen
# trace_claude_run via module
python3 trace_claude/trace_claude_run.py claude -p "hello"Each listen session creates a session_YYYYMMDD_HHMMSS.jsonl file in the sessions/ directory. One JSON event per line:
{"ts":"...","type":"request","flow_id":"...","method":"POST","path":"/v1/messages","headers":{...},"body":{...}}
{"ts":"...","type":"response_headers","flow_id":"...","status_code":200,"headers":{...},"streaming":true}
{"ts":"...","type":"sse","flow_id":"...","event":"message_start","data":{...}}
{"ts":"...","type":"sse","flow_id":"...","event":"content_block_delta","data":{"delta":{"type":"text_delta","text":"Hello"}}}
{"ts":"...","type":"sse","flow_id":"...","event":"message_stop","data":{}}
{"ts":"...","type":"stream_end","flow_id":"..."}| type | Description |
|---|---|
request |
Request sent to Anthropic API (headers + body) |
response_headers |
Response headers arrived (includes streaming boolean) |
sse |
A single SSE event from a streaming response |
response |
Full response (non-streaming response body) |
stream_end |
Streaming response finished |
| event | Description |
|---|---|
message_start |
Response started, contains model and input_tokens |
content_block_start |
Content block started (text or tool_use) |
content_block_delta |
Incremental content (text_delta or input_json_delta) |
content_block_stop |
Content block ended |
message_delta |
stop_reason, output_tokens |
message_stop |
Response ended |
ping |
Heartbeat |
# View model and message count for all requests
jq 'select(.type=="request") | {model: .body.model, msgs: (.body.messages | length)}' sessions/*.jsonl
# Extract all text output
jq -r 'select(.type=="sse" and .event=="content_block_delta") | .data.delta.text // empty' sessions/*.jsonl
# View token usage
jq 'select(.type=="sse" and .event=="message_start") | .data.message.usage' sessions/*.jsonl
jq 'select(.type=="sse" and .event=="message_delta") | .data.usage' sessions/*.jsonl
# View all tool calls
jq 'select(.type=="sse" and .event=="content_block_start" and .data.content_block.type=="tool_use") | .data.content_block.name' sessions/*.jsonl
# Count SSE events by type
jq -r 'select(.type=="sse") | .event' sessions/*.jsonl | sort | uniq -c | sort -rn- The proxy uses mitmproxy reverse proxy mode. Client-to-proxy communication is HTTP (not HTTPS) — no certificate installation required
- One proxy can serve multiple Claude Code instances simultaneously; traffic is distinguished by
flow_id - JSONL files store full bodies without truncation (system prompts can be very long); use jq to filter as needed
- API keys appear in request headers — be mindful of log file security
