Skip to content

Events (Request, Schema, Session, Heartbeat)

Every action in mcpr produces a ProxyEvent — a request transaction completed, a session started, a schema captured, a heartbeat ticked. Events flow through the event bus and fan out to registered sinks.

mcpr’s event bus emits four ProxyEvent variants:

VariantWhenKey data
RequestA request transaction completesrequest and response projections, latency, span breakdown, OpenAI client context
SessionAn MCP initialize handshake captures session infosession ID, AI client name, version, platform
SchemaA discovery response (tools/list, resources/list, etc.) is capturedmethod, items, change diff
HeartbeatEvery 30 secondsproxy state snapshot — upstream URL, export port, tunnel status, tunnel address

Request is the primary observability event. One event covers the full request/response transaction, with HTTP body bytes dropped from the logging projection so non-MCP traffic does not bloat sinks. MCP traffic carries the full JSON-RPC envelope.

SinkWhat it doesWhen it’s enabled
StderrOne-line summary per request to stderrAlways on
SQLiteWrites events to ~/.mcpr/store.db (override via MCPR_DB)Always on
CloudBatched POST to the cloud ingest endpointWhen [cloud].token is set
[cloud]
token = "mcpr_xxxxxxxx"
server = "my-proxy"
# Optional tuning
batch_size = 100
flush_interval_ms = 5000

The CloudSink batches events in memory and flushes on size or interval. If the cloud is unreachable, the proxy keeps operating — cloud connectivity never blocks the proxy.

mcpr passively captures your server’s tools, resources, and prompts by intercepting discovery responses. No extra requests, no auth needed, no added latency.

MCP methodCaptured
initializeServer name, version, protocol version, capabilities
tools/listTool names, descriptions, inputSchema
resources/listResource URIs, names, descriptions
resources/templates/listTemplate URIs, names, descriptions
prompts/listPrompt names, descriptions, arguments

mcpr detects when entries are added, removed, or modified and writes a full change history. Cloud’s Tools tab surfaces this; locally, query the events table directly with sqlite3.

Implement the EventSink trait to send events anywhere — Prometheus, Datadog, webhooks, Kafka:

use mcpr_core::event::{EventSink, ProxyEvent};
pub struct MySink;
impl EventSink for MySink {
fn on_event(&self, event: &ProxyEvent) {
if let ProxyEvent::Request(e) = event {
// e.request, e.response, e.latency_us, e.upstream_us, e.spans
println!("request {} latency_us={}", e.request_id, e.latency_us);
}
}
fn name(&self) -> &'static str { "my-sink" }
}

Rules: on_event must not block. Buffer internally, do I/O in flush(). flush() is called on a fixed interval and on shutdown.

See Event Schema for the full field specification.