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 globaldisableOnboardingparsed insrc/index.ts) - Client name validation – disabled entirely for the
desktop-commanderclient - 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:
- Feature flag disabled – Remote
onboarding_injectionset tofalse - Local config override – Local
onboarding_injectionexplicitly set tofalse - CLI opt-out –
--no-onboardingflag present at startup - Client mismatch – Running under the
desktop-commanderclient name - Successful engagement – User invokes
get_promptsand triggersmarkOnboardingPromptsUsed(), settingpromptsUsed: true - Usage threshold – User makes 10 or more tool calls (no longer considered "new")
- Attempt limit –
attemptsShownreaches 3 - 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 withgetOnboardingState,shouldShowOnboarding,getOnboardingMessage,markOnboardingShown, andmarkOnboardingPromptsUsedsrc/utils/welcome-onboarding.ts– Manages the separate "welcome page" A/B test while respecting the same feature flagssrc/index.ts– Parses--no-onboardingCLI flag and sets globaldisableOnboardingsrc/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()insrc/utils/usageTracker.ts - The system tracks
attemptsShown,lastShownAt, andpromptsUsedto 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-commanderclient 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →