How Desktop Commander's Usage Tracker Collects and Reports Telemetry Data

Desktop Commander tracks tool interactions through the UsageTracker class in src/utils/usageTracker.ts, which records session-based statistics locally and forwards anonymized events via the capture helper in src/utils/capture.ts.

The wonderwhy-er/DesktopCommanderMCP repository implements a comprehensive telemetry pipeline that balances detailed usage analytics with user privacy. This system captures granular metrics about tool execution while ensuring sensitive data never leaves the user's machine, providing the development team with actionable insights without compromising security.

Local Data Collection via UsageTracker

The UsageTracker class serves as the central hub for aggregating interaction data before any network transmission occurs.

Session Management and Timeouts

Each user interaction belongs to a UsageSession object that tracks temporal boundaries. The system defines a 30-minute inactivity threshold (SESSION_TIMEOUT) to determine session boundaries.

When a tool executes, isNewSession() checks if the elapsed time since the last activity exceeds this threshold. If so, updateSession() instantiates a fresh session object recording the start time, last activity timestamp, and command counter. This segmentation allows the team to analyze user engagement patterns and session duration metrics.

Tool Call Metrics and Categorization

Every tool invocation updates granular counters through trackSuccess() and trackFailure() methods. The tracker maintains:

  • Global counters: totalToolCalls, successfulCalls, failedCalls
  • Per-tool tallies: toolCounts mapping individual tool names to execution counts
  • Category aggregations: filesystemOperations and other domain-specific groupings

This categorization enables the product team to identify which capabilities see heavy adoption versus those requiring improvement.

Non-Blocking Persistence

To prevent telemetry I/O from degrading tool responsiveness, statistics persist asynchronously via saveStats(). The method invokes configManager.setValueNonBlocking('usageStats', …), ensuring that write operations to the local configuration store never block the critical path of tool execution.

Feedback and Onboarding State

Beyond raw usage metrics, the tracker manages user journey states through methods like markFeedbackPrompted() and markOnboardingShown(). These respect explicit opt-out flags including feedbackGiven and telemetryEnabled, ensuring the system honors user preferences regarding prompt frequency and data collection.

Remote Telemetry Reporting

While UsageTracker handles local aggregation, the capture function in src/utils/capture.ts manages network transmission to the analytics infrastructure.

Kill Switches and Privacy Controls

The telemetry pipeline implements multiple guardrails to respect user privacy. Before transmitting any data, captureBase() and sendToTelemetryProxy() verify:

  1. The environment variable DESKTOP_COMMANDER_DISABLE_TELEMETRY is not set
  2. The configuration flag telemetryEnabled remains true

Additionally, the system excludes UI-origin calls entirely through the isInsideUiOriginCall() guard:

if (isInsideUiOriginCall()) return;

This prevents telemetry noise from interface rendering while capturing meaningful tool execution events.

Data Sanitization and Anonymization

Before transmission, all payloads undergo rigorous scrubbing via sanitizeError(). This function strips path-like keys and error objects that might contain Personally Identifiable Information (PII). The sanitization ensures that file system paths, user directories, or error stack traces containing local system details never reach remote servers.

Payload Enrichment

The buildEventProperties() function enriches events with contextual metadata:

  • Platform identification: os.platform() output and container metadata from src/utils/system-info.ts
  • Runtime detection: Source identification (docker, npx, smithery)
  • Client attribution: client_name and client_version from the current or remote client context
  • Remote signaling: remote: true flag when currentCallIsRemote indicates the invocation originated from a remote device
  • Engagement metrics: Static engagement_time_msec values for session analysis

Transport and Retry Logic

Events POST to the primary endpoint https://telemetry.desktopcommander.app/mp/collect with a 3-second timeout to maintain UI responsiveness. The implementation uses fire-and-forget semantics—errors do not bubble up to the user interface.

If the primary proxy fails, the system falls back to a Cloud Run URL defined in the configuration. Each payload includes a unique anonymous client ID generated via configManager.getOrCreateClientId(), enabling longitudinal analysis without identifying individual users.

const payload = JSON.stringify({
  client_id: uniqueUserId,
  timestamp_micros: Date.now() * 1000,
  events: [{ name: event, params: eventProperties }],
});
// POST to proxy (fallback on error)

Practical Implementation Examples

The following patterns demonstrate integrating the telemetry system into tool implementations:

// Record a successful tool execution
import { usageTracker } from './utils/usageTracker.js';

async function executeReadFile(filePath: string) {
  try {
    const content = await fs.readFile(filePath, 'utf-8');
    await usageTracker.trackSuccess('read_file');
    return content;
  } catch (error) {
    await usageTracker.trackFailure('read_file');
    throw error;
  }
}
// Emit custom telemetry events
import { capture } from './utils/capture.js';

async function handleUserOptOut() {
  await capture('server_telemetry_opt_out', {
    reason: 'user_request',
    timestamp: new Date().toISOString()
  });
}
// Conditional feedback prompting
async function checkFeedbackEligibility() {
  if (await usageTracker.shouldPromptForFeedback()) {
    const { variant, message } = await usageTracker.getFeedbackPromptMessage();
    await usageTracker.markFeedbackPrompted();
    return { variant, message };
  }
  return null;
}

Summary

  • Local tracking occurs through UsageTracker in src/utils/usageTracker.ts, which manages session state (30-minute timeout), tool success/failure counters, and asynchronous persistence via configManager.
  • Privacy controls include the DESKTOP_COMMANDER_DISABLE_TELEMETRY environment variable, the telemetryEnabled config flag, and automatic exclusion of UI-origin calls.
  • Data sanitization removes PII through sanitizeError() and path stripping before network transmission.
  • Remote reporting happens via capture() in src/utils/capture.ts, which enriches events with platform/container metadata and posts to https://telemetry.desktopcommander.app/mp/collect using fire-and-forget semantics with a 3-second timeout.
  • Non-blocking architecture ensures telemetry operations never degrade tool responsiveness, using asynchronous writes for local storage and background HTTP requests for remote reporting.

Frequently Asked Questions

How can users completely disable telemetry in Desktop Commander?

Users can disable telemetry by setting the environment variable DESKTOP_COMMANDER_DISABLE_TELEMETRY or by setting the telemetryEnabled configuration flag to false via the application's settings. When either condition is met, the capture() function returns early without transmitting data, though local usage statistics may still be collected for session management purposes.

What specific data does the usage tracker record about tool executions?

The tracker records the tool name, success or failure status, categorical groupings (such as filesystemOperations), and temporal metadata including session start time and last activity. It explicitly excludes file paths, error stack traces, and other PII through the sanitizeError() function before any network transmission occurs.

Where are the telemetry events sent and how reliable is the delivery?

Events are sent to https://telemetry.desktopcommander.app/mp/collect as the primary endpoint, with an automatic fallback to a Cloud Run URL if the primary request fails. Delivery uses fire-and-forget semantics with a 3-second timeout, meaning the system prioritizes UI responsiveness over guaranteed delivery—failed transmissions do not retry or alert the user.

How does Desktop Commander distinguish between different user sessions?

The system creates a new UsageSession object after 30 minutes of inactivity, defined by the SESSION_TIMEOUT constant in src/utils/usageTracker.ts. The isNewSession() method compares the current timestamp against the lastActivity property, resetting session counters when the threshold is exceeded, which enables accurate engagement time calculations without requiring user authentication.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →