How DesktopCommander MCP Tracks Onboarding Progress and Auto-Disables

DesktopCommander MCP uses a persistent state machine in src/utils/usageTracker.ts that tracks onboarding attempts, tool usage, and time delays, automatically disabling after three attempts, ten tool calls, or when the user interacts with an onboarding prompt.

The onboarding system in the wonderwhy-er/DesktopCommanderMCP repository guides new users through the MCP server's capabilities while respecting strict limits to avoid overwhelming experienced users. This lightweight implementation persists progress in a JSON configuration and automatically terminates once specific engagement or usage thresholds are met.

Onboarding State Persistence

The system maintains a durable state object via getOnboardingState() in src/utils/usageTracker.ts. This function reads an onboardingState object from the global config manager or initializes a default structure:

{
  promptsUsed: false,
  attemptsShown: 0,
  lastShownAt: 0
}

This state survives server restarts because the config manager writes to persistent storage. The OnboardingState interface defines three critical tracking fields that drive the entire logic flow.

Progress Tracking Logic

The shouldShowOnboarding() function (line 421 in src/utils/usageTracker.ts) evaluates a cascade of conditions to determine if the onboarding message should inject into the LLM response. This method checks:

  • Remote feature flag onboarding_injection (kill-switch)
  • Local config override onboarding_injection
  • CLI flag --no-onboarding (exposed via global disableOnboarding parsed in src/index.ts)
  • Client name validation – disabled entirely for the desktop-commander client
  • Usage metrics – disables after 10 tool calls or 3 attempts
  • Time-based rate limiting – requires a 2-minute delay between attempts after the first show

Only if all conditions pass does the system proceed to generate the onboarding message.

Auto-Disable Triggers

The DesktopCommander MCP onboarding system auto-disables when any of the following thresholds are met:

  1. Feature flag disabled – Remote onboarding_injection set to false
  2. Local config override – Local onboarding_injection explicitly set to false
  3. CLI opt-out--no-onboarding flag present at startup
  4. Client mismatch – Running under the desktop-commander client name
  5. Successful engagement – User invokes get_prompts and triggers markOnboardingPromptsUsed(), setting promptsUsed: true
  6. Usage threshold – User makes 10 or more tool calls (no longer considered "new")
  7. Attempt limitattemptsShown reaches 3
  8. Time restriction – Less than 2 minutes elapsed since lastShownAt

These checks execute inside shouldShowOnboarding(), ensuring the system silently backs off once the user demonstrates proficiency or ignores the prompts.

Recording User Engagement

When onboarding displays, markOnboardingShown(variant) increments attemptsShown, updates lastShownAt to the current timestamp, and persists the state. If the user selects any onboarding option (triggering the get_prompts tool), markOnboardingPromptsUsed() (line 559) immediately sets promptsUsed: true, creating a permanent block against future injections.

The server integration in src/server.ts (around line 1560) orchestrates this flow:

// Server entry point (simplified)
if (await usageTracker.shouldShowOnboarding()) {
  const msg = await usageTracker.getOnboardingMessage();
  // inject msg into LLM response …
  await usageTracker.markOnboardingShown(msg.variant);
}

When a user engages with the UI:

await usageTracker.markOnboardingPromptsUsed();   // stops future prompts

Implementation Architecture

Key files supporting this system include:

  • src/utils/usageTracker.ts – Core state machine with getOnboardingState, shouldShowOnboarding, getOnboardingMessage, markOnboardingShown, and markOnboardingPromptsUsed
  • src/utils/welcome-onboarding.ts – Manages the separate "welcome page" A/B test while respecting the same feature flags
  • src/index.ts – Parses --no-onboarding CLI flag and sets global disableOnboarding
  • src/server.ts (~line 1560) – Injection point for onboarding messages into LLM responses

Summary

  • DesktopCommander MCP stores onboarding state in a persistent JSON config via getOnboardingState() in src/utils/usageTracker.ts
  • The system tracks attemptsShown, lastShownAt, and promptsUsed to determine when to stop showing prompts
  • Auto-disable occurs after 3 attempts, 10 tool calls, successful prompt usage, or explicit opt-out via flags/config
  • A 2-minute cooldown between attempts prevents spam while allowing legitimate discovery
  • The desktop-commander client name automatically bypasses onboarding entirely
  • State changes persist across server restarts through the config manager

Frequently Asked Questions

Where is the onboarding state stored?

The state lives in the global config manager as a JSON object named onboardingState, persisted to disk alongside other usage statistics. This ensures durability across server restarts and consistent tracking of attemptsShown, lastShownAt, and promptsUsed fields.

How many times will the onboarding prompt appear?

The system allows maximum 3 attempts spaced by at least 2 minutes each. If the user never interacts with the prompts after three displays, the system permanently disables itself by incrementing attemptsShown to the threshold value.

Can I disable onboarding manually?

Yes. You can pass the --no-onboarding CLI flag when starting the server (parsed in src/index.ts), set the local config value onboarding_injection to false, or rely on the remote feature flag kill-switch. Additionally, simply using any of the onboarding prompts once will trigger markOnboardingPromptsUsed() and permanently disable future injections.

What client name disables onboarding automatically?

The desktop-commander client name triggers an automatic disable in shouldShowOnboarding(), preventing the onboarding system from injecting messages when the MCP server runs under this specific client identifier.

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 →