# How DesktopCommanderMCP's New User Onboarding System Guides First-Time Users

> DesktopCommanderMCP's new user onboarding system uses a three-stage pipeline to guide first-time users, showing the welcome page once to 80% of new installations while skipping existing users.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: how-to-guide
- Published: 2026-08-04

---

**DesktopCommanderMCP uses a three-stage pipeline—Config Manager, A/B test engine, and Welcome-Onboarding Logic—to show the welcome page exactly once to 80% of new installations while skipping existing users entirely.**

The onboarding system in wonderwhy-er/DesktopCommanderMCP ensures first-time users receive a guided introduction without ever interrupting returning users. By combining persistent configuration flags, deterministic A/B assignment, and conditional page launching, the system balances user experience with data-driven iteration.

---

## Core Components of the Onboarding Pipeline

The onboarding flow is implemented across three coordinated modules. Each handles a distinct responsibility: eligibility detection, experiment assignment, and final orchestration.

### Config Manager: Flagging Fresh Installations

In [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), the `ConfigManager` class determines whether the current session qualifies as a first run. When `ConfigManager.init()` executes, it checks for an existing config file at `this.configPath`.

If no config exists, the system creates a new one with default flags:

```typescript
// Inside ConfigManager.init()
if (await fs.access(this.configPath).catch(() => false)) {
  // Existing config → no onboarding
  this._isFirstRun = false;
} else {
  // First run → enable onboarding flags
  this.config = this.getDefaultConfig();   // pendingWelcomeOnboarding: true
  this._isFirstRun = true;
  await this.saveConfig();
}

```

The default configuration sets two critical flags:
- `welcomeOnboardingEligible: true` — marks this installation as qualifying for onboarding
- `pendingWelcomeOnboarding: true` — indicates the welcome page has not yet been shown

Existing configs are migrated with both flags set to `false`, ensuring retroactive onboarding never occurs.

### A/B Test Engine: Deterministic Variant Assignment

The `hasFeature()` function in [`src/utils/ab-test.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/ab-test.ts) implements deterministic experiment assignment. For the **OnboardingPreTool** experiment, the remote feature-flag JSON defines two variants:

| Variant | Weight | Description |
|---------|--------|-------------|
| `noOnboardingPage` | 20% | Control group — onboarding is skipped |
| `showOnboardingPage` | 80% | Treatment group — welcome page is displayed |

The assignment is deterministic per user based on a stable identifier, and the result is cached under `abTest_<experiment>` in the config. This guarantees the same user receives consistent treatment across restarts.

### Welcome-Onboarding Logic: Orchestrating the Flow

The `handleWelcomePageOnboarding()` function in [`src/utils/welcome-onboarding.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/welcome-onboarding.ts) runs at client startup and executes an eight-step verification sequence:

1. **Eligibility check** — verifies `welcomeOnboardingEligible` is `true`
2. **Pending status** — confirms `pendingWelcomeOnboarding` remains `true`
3. **Feature flag readiness** — waits for fresh flags if not yet cached
4. **Global toggle** — honors the `welcome_page_enabled` setting
5. **Exclusion list** — skips users in `welcome_page_excluded_clients`
6. **A/B decision** — calls `hasFeature('showOnboardingPage')` for variant assignment
7. **Page launch** — if treatment applies, calls `openWelcomePage()` and records `server_welcome_page_opened`
8. **Flag cleanup** — clears `pendingWelcomeOnboarding` regardless of outcome

```typescript
import { handleWelcomePageOnboarding } from './utils/welcome-onboarding.js';

async function startClient(clientName?: string) {
  // … other startup logic …
  await handleWelcomePageOnboarding(clientName);
}

```

---

## Opening the Welcome Page

The final step uses `openWelcomePage()` from [`src/utils/open-browser.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/open-browser.ts). This function constructs a parameterized URL and invokes the OS-appropriate command:

```typescript
export async function openWelcomePage(clientName?: string) {
  const url = `https://github.com/wonderwhy-er/DesktopCommanderMCP/wiki/Welcome?client=${encodeURIComponent(clientName ?? '')}`;
  await openBrowser(url);                     // OS‑specific command (xdg‑open, open, start)
}

```

The `openBrowser()` helper selects between `xdg-open` (Linux), `open` (macOS), or `start` (Windows). Success and failure cases are logged via `logToStderr`, and analytics events are captured through `capture()`.

---

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) | Persistent config with onboarding flags |
| [`src/utils/welcome-onboarding.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/welcome-onboarding.ts) | Core orchestration logic |
| [`src/utils/ab-test.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/ab-test.ts) | Deterministic A/B variant assignment |
| [`src/utils/open-browser.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/open-browser.ts) | Cross-platform browser launching |
| [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts) | Remote flag fetching and caching |

---

## Summary

- **Fresh installations** are flagged automatically in [`config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config-manager.ts) via `welcomeOnboardingEligible` and `pendingWelcomeOnboarding`
- **Existing users** are excluded through config migration that sets both flags to `false`
- **A/B testing** assigns 80% of eligible users to the treatment group using deterministic hashing in [`ab-test.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/ab-test.ts)
- **Conditional execution** in [`welcome-onboarding.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/welcome-onboarding.ts) runs eight verification checks before launching the page
- **Exactly-once guarantee** is enforced by clearing `pendingWelcomeOnboarding` immediately after the decision

---

## Frequently Asked Questions

### What prevents the welcome page from showing multiple times?

The `pendingWelcomeOnboarding` flag is cleared immediately after `handleWelcomePageOnboarding()` completes, regardless of whether the page was actually opened. This state change is persisted through `configManager`, making subsequent calls no-ops.

### Can existing users ever see the onboarding page?

No. During config initialization, existing configuration files are migrated with `welcomeOnboardingEligible: false`. Only brand-new installations where no prior config existed receive the eligible flag.

### How does the A/B test maintain consistent assignment across restarts?

The `hasFeature()` function in [`ab-test.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/ab-test.ts) uses deterministic hashing of a stable user identifier combined with the experiment name. The result is stored in config under `abTest_<experiment>`, ensuring the same variant is returned on every call.

### What happens if feature flags haven't loaded when onboarding runs?

`handleWelcomePageOnboarding()` explicitly waits for fresh feature flags if they aren't cached yet. This prevents premature decisions based on stale or missing experiment definitions.