docs/features/telemetry.md
MCPProxy collects anonymous usage statistics to help improve the product. This page explains what is collected, what is not, and how to disable it.
MCPProxy sends a daily heartbeat containing only aggregate, non-identifying information. The current schema is version 5 (schema_version: 5 in the JSON payload); the schema is forward-compatible so older consumers simply ignore fields they don't recognize.
| Field | Example | Purpose |
|---|---|---|
anonymous_id | 550e8400-... | Random UUID for deduplication (not linked to you) |
version | 0.21.3 | Track version adoption |
edition | personal | Understand edition usage |
os | darwin | Platform distribution |
arch | arm64 | Architecture distribution |
server_count | 12 | Understand scale of usage |
connected_server_count | 8 | Connection success rates |
tool_count | 156 | Tool ecosystem size |
uptime_hours | 47 | Usage patterns |
routing_mode | retrieve_tools | Feature adoption |
quarantine_enabled | true | Security feature adoption |
feature_flags.docker_available | true | Fraction of installs with a reachable Docker daemon (schema v3) |
server_protocol_counts | {"stdio":3,"http":2,"sse":0,"streamable_http":1,"auto":0} | Ratio of remote-HTTP vs local-stdio upstreams (schema v3) |
server_docker_isolated_count | 2 | How many configured servers the runtime actually wraps in Docker isolation (schema v3) |
feature_flags.docker_isolation_enabled | true | Whether global Docker isolation is turned on (schema v5). Lets us tell "isolation on, 0 matching servers" apart from "isolation off" |
feature_flags.docker_cli_source | bundled | How the docker CLI was located — fixed enum path / bundled / login_shell / absent (schema v5). The direct signal for "Docker installed but not on the spawn PATH" (issue #696). Never the path string itself |
The server_protocol_counts map uses a fixed enum of keys (stdio, http, sse, streamable_http, auto) — server names and URLs are never included. Unknown or misconfigured protocol values are bucketed into auto.
The docker_cli_source field is likewise a fixed enum (path, bundled, login_shell, absent); the resolved path is never transmitted.
Docker isolation failures surface in error_code_counts_24h via three stable diagnostic codes (schema v5): MCPX_DOCKER_CLI_NOT_FOUND (isolation requested but the docker binary is unresolved — issue #696), MCPX_DOCKER_EXEC_NOT_FOUND (the image lacks the interpreter the server needs, e.g. uvx missing in python:3.11), and MCPX_DOCKER_OCI_RUNTIME (OCI runtime / architecture-mismatch failures).
The following is never collected:
The anonymous ID is a random UUID (v4) generated on first run. It has no correlation to your hardware, user account, or identity. It exists solely to deduplicate heartbeats (so we don't count the same install twice in a day).
You can delete it by removing the telemetry.anonymous_id field from your config — a new random ID will be generated on next startup.
When telemetry transitions from enabled to disabled (via the CLI, the config
file, or the web UI / macOS app), MCPProxy sends exactly one final, anonymous
beacon — an event: "telemetry_disabled" carrying only your anonymous install
ID and no usage data. It lets us count how many installs opt out so we can
gauge how the feature is received. The send is best-effort: if it fails,
telemetry is still disabled. After it, no further telemetry is emitted.
Disabling while already disabled (or reloading a config that is already
disabled) sends nothing. Setting MCPPROXY_TELEMETRY=false is treated as
"never enabled" and also sends nothing.
There are three ways to disable telemetry:
mcpproxy telemetry disable
Verify with:
mcpproxy telemetry status
Re-enable anytime:
mcpproxy telemetry enable
Edit ~/.mcpproxy/mcp_config.json:
{
"telemetry": {
"enabled": false
}
}
export MCPPROXY_TELEMETRY=false
This overrides the config file setting and is useful for CI/CD environments or system-wide policies.
The telemetry implementation is fully open-source:
internal/telemetry/telemetry.go — heartbeat logicinternal/config/config.go — configuration (TelemetryConfig)