telemetry

package
v0.24.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const FirstRunNoticeText = `` /* 192-byte string literal not displayed */

FirstRunNoticeText is the one-time banner printed to stderr the first time `mcpproxy serve` runs against a config that has no telemetry_notice_shown flag. Spec 042 User Story 10.

View Source
const SchemaVersion = 2

SchemaVersion is the heartbeat payload schema version. v1 payloads have no such field; receivers can route by absence vs presence.

Variables

This section is empty.

Functions

func IsBuiltinTool added in v0.24.0

func IsBuiltinTool(name string) bool

IsBuiltinTool reports whether the given tool name is in the fixed enum.

func IsValidErrorCategory added in v0.24.0

func IsValidErrorCategory(c ErrorCategory) bool

IsValidErrorCategory reports whether the given category is in the fixed enum.

func MaybePrintFirstRunNotice added in v0.24.0

func MaybePrintFirstRunNotice(cfg *config.Config, w io.Writer) bool

MaybePrintFirstRunNotice prints the first-run telemetry notice to w if it has not been shown before. It mutates cfg.Telemetry.NoticeShown so the caller can persist the change. Returns true if the notice was printed.

Spec 042 User Story 10. Idempotent: subsequent calls with the same config are no-ops.

func RecordBuiltinToolOn added in v0.24.0

func RecordBuiltinToolOn(reg *CounterRegistry, name string)

RecordBuiltinToolOn calls reg.RecordBuiltinTool(name) if reg is non-nil.

func RecordErrorOn added in v0.24.0

func RecordErrorOn(reg *CounterRegistry, c ErrorCategory)

RecordErrorOn calls reg.RecordError(c) if reg is non-nil.

func RecordRESTRequestOn added in v0.24.0

func RecordRESTRequestOn(reg *CounterRegistry, method, template, statusClass string)

RecordRESTRequestOn calls reg.RecordRESTRequest(...) if reg is non-nil.

func RecordSurfaceOn added in v0.24.0

func RecordSurfaceOn(reg *CounterRegistry, s Surface)

RecordSurfaceOn calls reg.RecordSurface(s) if reg is non-nil.

func RecordUpstreamToolOn added in v0.24.0

func RecordUpstreamToolOn(reg *CounterRegistry)

RecordUpstreamToolOn calls reg.RecordUpstreamTool() if reg is non-nil.

func SortedOAuthProviderTypes added in v0.24.0

func SortedOAuthProviderTypes(types []string) []string

SortedOAuthProviderTypes returns a sorted, deduplicated list. Helper used by feature_flags.go but defined here to avoid an extra file.

func ValidateCategory

func ValidateCategory(category string) bool

ValidateCategory checks if the feedback category is valid.

func ValidateMessage

func ValidateMessage(message string) error

ValidateMessage checks if the feedback message meets length requirements.

Types

type CounterRegistry added in v0.24.0

type CounterRegistry struct {
	// contains filtered or unexported fields
}

CounterRegistry aggregates Tier 2 telemetry counters in memory. All methods are safe for concurrent use. Counters are zeroed only by Reset(), which the telemetry service calls after a successful heartbeat send.

func NewCounterRegistry added in v0.24.0

func NewCounterRegistry() *CounterRegistry

NewCounterRegistry creates an empty registry. All counters start at zero.

func (*CounterRegistry) RecordBuiltinTool added in v0.24.0

func (r *CounterRegistry) RecordBuiltinTool(name string)

RecordBuiltinTool increments the counter for the named built-in tool. Unknown names (i.e., upstream tool names) are silently dropped.

func (*CounterRegistry) RecordDoctorRun added in v0.24.0

func (r *CounterRegistry) RecordDoctorRun(results []DoctorCheckResult)

RecordDoctorRun aggregates the structured doctor check results into the registry's doctor counter. Each result increments either Pass or Fail for its check name.

func (*CounterRegistry) RecordError added in v0.24.0

func (r *CounterRegistry) RecordError(c ErrorCategory)

RecordError increments the counter for the given error category. Unknown categories are silently dropped.

func (*CounterRegistry) RecordRESTRequest added in v0.24.0

func (r *CounterRegistry) RecordRESTRequest(method, template, statusClass string)

RecordRESTRequest increments the counter for the given route template and status class. Both inputs are expected to be from a fixed enum (Chi route template + "2xx"/"3xx"/"4xx"/"5xx") so no sanitization is needed here.

func (*CounterRegistry) RecordSurface added in v0.24.0

func (r *CounterRegistry) RecordSurface(s Surface)

RecordSurface increments the counter for the given surface.

func (*CounterRegistry) RecordUpstreamTool added in v0.24.0

func (r *CounterRegistry) RecordUpstreamTool()

RecordUpstreamTool increments the upstream tool call counter. The tool name itself is intentionally not accepted: only an aggregate count is recorded.

func (*CounterRegistry) Reset added in v0.24.0

func (r *CounterRegistry) Reset()

Reset zeros all counters. Called only after a successful heartbeat send.

func (*CounterRegistry) Snapshot added in v0.24.0

func (r *CounterRegistry) Snapshot() RegistrySnapshot

Snapshot returns an immutable view of all counters. The registry is NOT reset; call Reset() after a successful flush.

type DoctorCheckResult added in v0.24.0

type DoctorCheckResult interface {
	GetName() string
	IsPass() bool
}

DoctorCheckResult is the minimal interface the registry needs to record a doctor check outcome. It is satisfied by internal/doctor.CheckResult without importing that package (avoiding an import cycle).

type DoctorCounts added in v0.24.0

type DoctorCounts struct {
	Pass int64 `json:"pass"`
	Fail int64 `json:"fail"`
}

DoctorCounts holds pass/fail counts for a single doctor check.

type EnvDisabledReason added in v0.24.0

type EnvDisabledReason string

EnvDisabledReason explains why telemetry was disabled by an environment variable, if any. The empty string means env did not disable telemetry.

const (
	EnvDisabledNone         EnvDisabledReason = ""
	EnvDisabledByDoNotTrack EnvDisabledReason = "DO_NOT_TRACK"
	EnvDisabledByCI         EnvDisabledReason = "CI"
	EnvDisabledByMCPProxy   EnvDisabledReason = "MCPPROXY_TELEMETRY=false"
)

func IsDisabledByEnv added in v0.24.0

func IsDisabledByEnv() (bool, EnvDisabledReason)

IsDisabledByEnv evaluates the env var precedence chain for telemetry disablement. Precedence (highest first):

  1. DO_NOT_TRACK set to any non-empty, non-"0" value (consoledonottrack.com)
  2. CI=true or CI=1
  3. MCPPROXY_TELEMETRY=false

Returns true and the reason if telemetry should be disabled.

type ErrorCategory added in v0.24.0

type ErrorCategory string

ErrorCategory is a typed enum of error categories that telemetry will count. Only values defined here may be recorded; unknown categories are silently dropped by RecordError to prevent free-text error messages from leaking into telemetry.

const (
	ErrCatOAuthRefreshFailed      ErrorCategory = "oauth_refresh_failed"
	ErrCatOAuthTokenExpired       ErrorCategory = "oauth_token_expired"
	ErrCatUpstreamConnectTimeout  ErrorCategory = "upstream_connect_timeout"
	ErrCatUpstreamConnectRefused  ErrorCategory = "upstream_connect_refused"
	ErrCatUpstreamHandshakeFailed ErrorCategory = "upstream_handshake_failed"
	ErrCatToolQuarantineBlocked   ErrorCategory = "tool_quarantine_blocked"
	ErrCatDockerPullFailed        ErrorCategory = "docker_pull_failed"
	ErrCatDockerRunFailed         ErrorCategory = "docker_run_failed"
	ErrCatIndexRebuildFailed      ErrorCategory = "index_rebuild_failed"
	ErrCatConfigReloadFailed      ErrorCategory = "config_reload_failed"
	ErrCatSocketBindFailed        ErrorCategory = "socket_bind_failed"
)

type FeatureFlagSnapshot added in v0.24.0

type FeatureFlagSnapshot struct {
	EnableSocket                  bool     `json:"enable_socket"`
	EnableWebUI                   bool     `json:"enable_web_ui"`
	EnablePrompts                 bool     `json:"enable_prompts"`
	RequireMCPAuth                bool     `json:"require_mcp_auth"`
	EnableCodeExecution           bool     `json:"enable_code_execution"`
	QuarantineEnabled             bool     `json:"quarantine_enabled"`
	SensitiveDataDetectionEnabled bool     `json:"sensitive_data_detection_enabled"`
	OAuthProviderTypes            []string `json:"oauth_provider_types"`
}

FeatureFlagSnapshot captures the boolean / enum feature flags reported in the daily heartbeat. Spec 042 User Story 4.

func BuildFeatureFlagSnapshot added in v0.24.0

func BuildFeatureFlagSnapshot(cfg *config.Config) *FeatureFlagSnapshot

BuildFeatureFlagSnapshot returns a snapshot of the current feature flag state. It records boolean flags and a sorted, deduplicated list of OAuth provider TYPES (not URLs, client IDs, or tenant identifiers). The empty list is returned if no upstream servers have OAuth configured.

type FeedbackContext

type FeedbackContext struct {
	Version              string `json:"version"`
	Edition              string `json:"edition"`
	OS                   string `json:"os"`
	Arch                 string `json:"arch"`
	ServerCount          int    `json:"server_count"`
	ConnectedServerCount int    `json:"connected_server_count"`
	RoutingMode          string `json:"routing_mode"`
}

FeedbackContext provides automatic system context alongside feedback.

type FeedbackRequest

type FeedbackRequest struct {
	Category string          `json:"category"` // bug, feature, other
	Message  string          `json:"message"`
	Email    string          `json:"email,omitempty"`
	Context  FeedbackContext `json:"context"`
}

FeedbackRequest is the user-submitted feedback payload.

type FeedbackResponse

type FeedbackResponse struct {
	Success  bool   `json:"success"`
	IssueURL string `json:"issue_url,omitempty"`
	Error    string `json:"error,omitempty"`
}

FeedbackResponse is the response from the telemetry backend.

type HeartbeatPayload

type HeartbeatPayload struct {
	// v1 fields (preserved unchanged)
	AnonymousID          string `json:"anonymous_id"`
	Version              string `json:"version"`
	Edition              string `json:"edition"`
	OS                   string `json:"os"`
	Arch                 string `json:"arch"`
	GoVersion            string `json:"go_version"`
	ServerCount          int    `json:"server_count"`
	ConnectedServerCount int    `json:"connected_server_count"`
	ToolCount            int    `json:"tool_count"`
	UptimeHours          int    `json:"uptime_hours"`
	RoutingMode          string `json:"routing_mode"`
	QuarantineEnabled    bool   `json:"quarantine_enabled"`
	Timestamp            string `json:"timestamp"`

	// Spec 042 (Tier 2) additions
	SchemaVersion               int                         `json:"schema_version,omitempty"`
	AnonymousIDCreatedAt        string                      `json:"anonymous_id_created_at,omitempty"`
	CurrentVersion              string                      `json:"current_version,omitempty"`
	PreviousVersion             string                      `json:"previous_version"`
	LastStartupOutcome          string                      `json:"last_startup_outcome,omitempty"`
	SurfaceRequests             map[string]int64            `json:"surface_requests,omitempty"`
	BuiltinToolCalls            map[string]int64            `json:"builtin_tool_calls,omitempty"`
	UpstreamToolCallCountBucket string                      `json:"upstream_tool_call_count_bucket,omitempty"`
	RESTEndpointCalls           map[string]map[string]int64 `json:"rest_endpoint_calls,omitempty"`
	FeatureFlags                *FeatureFlagSnapshot        `json:"feature_flags,omitempty"`
	ErrorCategoryCounts         map[string]int64            `json:"error_category_counts,omitempty"`
	DoctorChecks                map[string]DoctorCounts     `json:"doctor_checks,omitempty"`
}

HeartbeatPayload is the anonymous telemetry payload sent periodically. Spec 042 expanded the payload with Tier 2 fields; v1 fields are preserved.

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter enforces a maximum number of requests per hour.

func NewRateLimiter

func NewRateLimiter(maxPerHour int) *RateLimiter

NewRateLimiter creates a rate limiter with the given max requests per hour.

func (*RateLimiter) Allow

func (rl *RateLimiter) Allow() bool

Allow returns true if the request is within the rate limit.

type RegistrySnapshot added in v0.24.0

type RegistrySnapshot struct {
	SurfaceCounts               map[string]int64            `json:"surface_requests"`
	BuiltinToolCalls            map[string]int64            `json:"builtin_tool_calls"`
	UpstreamToolCallCountBucket string                      `json:"upstream_tool_call_count_bucket"`
	RESTEndpointCalls           map[string]map[string]int64 `json:"rest_endpoint_calls"`
	ErrorCategoryCounts         map[string]int64            `json:"error_category_counts"`
	DoctorChecks                map[string]DoctorCounts     `json:"doctor_checks"`
}

RegistrySnapshot is an immutable view of the registry built by Snapshot(). It is safe to mutate the maps in a snapshot — they are copies.

type RuntimeStats

type RuntimeStats interface {
	GetServerCount() int
	GetConnectedServerCount() int
	GetToolCount() int
	GetRoutingMode() string
	IsQuarantineEnabled() bool
}

RuntimeStats is an interface to decouple from the runtime package.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service manages anonymous telemetry heartbeats and feedback submission.

func New

func New(cfg *config.Config, cfgPath, version, edition string, logger *zap.Logger) *Service

New creates a new telemetry service.

func (*Service) BuildPayload added in v0.24.0

func (s *Service) BuildPayload() HeartbeatPayload

BuildPayload renders the heartbeat payload at the current point in time. It is exported so the `mcpproxy telemetry show-payload` command can render the same payload that would next be sent, without making a network call.

func (*Service) EnvDisabledReason added in v0.24.0

func (s *Service) EnvDisabledReason() EnvDisabledReason

EnvDisabledReason returns the env-var reason telemetry is disabled, if any.

func (*Service) Registry added in v0.24.0

func (s *Service) Registry() *CounterRegistry

Registry returns the counter registry for Tier 2 telemetry events. Always non-nil after New, even if telemetry is disabled — that way callers can always Record* without nil checks; the data simply never leaves the process.

func (*Service) SetRuntimeStats

func (s *Service) SetRuntimeStats(stats RuntimeStats)

SetRuntimeStats sets the runtime stats provider (called after runtime is fully initialized).

func (*Service) Start

func (s *Service) Start(ctx context.Context)

Start begins the telemetry heartbeat loop. This is a blocking call; run in a goroutine.

func (*Service) SubmitFeedback

func (s *Service) SubmitFeedback(ctx context.Context, req *FeedbackRequest) (*FeedbackResponse, error)

SubmitFeedback sends feedback to the telemetry backend.

type Surface added in v0.24.0

type Surface int

Surface identifies which client surface originated a request.

const (
	SurfaceMCP Surface = iota
	SurfaceCLI
	SurfaceWebUI
	SurfaceTray
	SurfaceUnknown
)

func ParseClientSurface added in v0.24.0

func ParseClientSurface(header string) Surface

ParseClientSurface maps the X-MCPProxy-Client header value to a Surface enum. The expected format is "<surface>/<version>"; unknown prefixes and missing headers map to SurfaceUnknown.

func (Surface) String added in v0.24.0

func (s Surface) String() string

String returns the JSON key for a Surface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL