Structured Events
mcpr parses every MCP request at the protocol level and emits structured JSON events. This gives you visibility into what’s happening through your proxy — which tools are called, by whom, how long they take, and what fails.
Enabling events
Section titled “Enabling events”mcpr --mcp http://localhost:9000 --events 2>/dev/null | jqThe --events flag emits newline-delimited JSON to stdout. The 2>/dev/null redirects the TUI logs to stderr so only clean JSON goes to stdout.
What you get without —events
Section titled “What you get without —events”Even without the --events flag, mcpr logs every request with MCP-level context in the terminal:
21:23:11 POST 200 8.0KB 16ms 15ms↑ 1ms↓ initialize → http://localhost:9000/mcp21:23:11 POST 200 73.6KB 11ms 7ms↑ 4ms↓ tools/list → http://localhost:9000/mcp21:23:11 POST 200 147B 8ms 8ms↑ 0ms↓ tools/call get_weather → http://localhost:9000/mcp21:23:11 POST 200 147B 8ms 8ms↑ 0ms↓ tools/call search [-32602 Invalid params] → ...Each line shows: HTTP method, status, response size, total time, upstream time (↑), proxy overhead (↓), MCP method, tool name, and any JSON-RPC errors.
This is the “protocol-level debugging” that generic HTTP proxies can’t do — mcpr understands JSON-RPC and shows you tool names and error codes, not just HTTP status codes.
Event format
Section titled “Event format”{ "ts": "2026-04-02T10:15:30.142Z", "type": "tool_call", "method": "tools/call", "tool": "search_products", "session": "sess_abc123", "user": null, "latency_ms": 142, "status": "ok", "upstream": "http://localhost:9000", "csp_applied": true}Event types
Section titled “Event types”| Type | When | Key fields |
|---|---|---|
tool_call | An MCP tool was called | tool, latency_ms, status, error_code |
tool_list | Client requested the tool list | latency_ms, status |
session_start | New MCP session initialized | session |
session_end | Session closed | session |
widget_serve | Widget HTML was served | path |
csp_violation | CSP violation detected | blocked_uri, directive |
See Event Schema for the full specification of all fields.
Filtering events
Section titled “Filtering events”# Only tool callsmcpr --events 2>/dev/null | jq 'select(.type == "tool_call")'
# Only errorsmcpr --events 2>/dev/null | jq 'select(.status == "error")'
# Only CSP violationsmcpr --events 2>/dev/null | jq 'select(.type == "csp_violation")'
# Slow tool calls (over 500ms)mcpr --events 2>/dev/null | jq 'select(.type == "tool_call" and .latency_ms > 500)'
# Specific toolmcpr --events 2>/dev/null | jq 'select(.tool == "search_products")'Piping to log aggregators
Section titled “Piping to log aggregators”Events are newline-delimited JSON (NDJSON) — compatible with any log pipeline:
# Write to filemcpr --events 2>/dev/null >> mcpr-events.jsonl
# Datadogmcpr --events 2>/dev/null | datadog-agent ...
# CloudWatchmcpr --events 2>/dev/null | aws logs put-log-events ...
# Grafana Lokimcpr --events 2>/dev/null | promtail ...Cloud event sync
Section titled “Cloud event sync”With mcpr Cloud, events sync automatically to the cloud dashboard. Add your cloud config:
[cloud]token = "mcpr_xxxxxxxx"server = "my-proxy" # identifies this server in your cloud projectEvent metadata is synced — tool parameters and response bodies are not included. Events are batched in memory and sent via HTTPS. If the cloud is unreachable, the proxy falls back to stdout — cloud connectivity never blocks proxy operation.
See mcpr Cloud for dashboard, Studio, and alerting features.
