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:
toolCountsmapping individual tool names to execution counts - Category aggregations:
filesystemOperationsand 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:
- The environment variable
DESKTOP_COMMANDER_DISABLE_TELEMETRYis not set - The configuration flag
telemetryEnabledremains 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 fromsrc/utils/system-info.ts - Runtime detection: Source identification (docker, npx, smithery)
- Client attribution:
client_nameandclient_versionfrom the current or remote client context - Remote signaling:
remote: trueflag whencurrentCallIsRemoteindicates the invocation originated from a remote device - Engagement metrics: Static
engagement_time_msecvalues 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
UsageTrackerinsrc/utils/usageTracker.ts, which manages session state (30-minute timeout), tool success/failure counters, and asynchronous persistence viaconfigManager. - Privacy controls include the
DESKTOP_COMMANDER_DISABLE_TELEMETRYenvironment variable, thetelemetryEnabledconfig 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()insrc/utils/capture.ts, which enriches events with platform/container metadata and posts tohttps://telemetry.desktopcommander.app/mp/collectusing 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →