# How to Use Feature Flags in Desktop Commander MCP: A Complete Guide

> Learn to use feature flags in Desktop Commander MCP with our complete guide. Leverage the featureFlagManager for runtime toggling of features, avoiding redeployments.

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

---

**Desktop Commander MCP provides a lightweight feature-flag system via the `featureFlagManager` singleton in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), enabling runtime toggling of functionality without redeployment.**

The Desktop Commander MCP repository (`wonderwhy-er/DesktopCommanderMCP`) ships with a built-in feature flag manager that lets developers safely roll out, test, or disable features remotely. This system caches flag values locally while maintaining a fresh connection to a remote configuration source, allowing the application to respond to configuration changes without requiring a full restart or redeployment.

## Understanding the Feature Flag Architecture

The feature flag system centers around a singleton manager that handles asynchronous fetching, local caching, and periodic background refresh.

### Core Components

The architecture consists of three primary elements working together:

- **`FeatureFlagManager` class**: Defined in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), this class handles loading from cache, fetching from remote JSON endpoints, and managing refresh timers.
- **`featureFlagManager` singleton**: The exported instance imported by consumers throughout the codebase, ensuring consistent state across the application.
- **Non-blocking initialization**: In [`src/index.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/index.ts), the manager initializes at startup without blocking the main application thread, allowing the app to serve cached flags immediately while fetching fresh data in the background.

### Data Flow and Refresh Mechanism

The manager implements a dual-source strategy for high availability:

1. **Cache First**: On startup, it loads existing flags from `<config-dir>/feature-flags.json` (defined via `CONFIG_FILE` in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts)), ensuring immediate availability even offline.
2. **Background Fetch**: It then fetches fresh JSON from `DC_FLAG_URL` (defaulting to `https://desktopcommander.app/flags/v2/production.json`) and updates the cache.
3. **Periodic Refresh**: A `setInterval` timer runs every 30 minutes (`cacheMaxAge`), calling `fetchFlags()` to retrieve updates. The timer is **`unref`**-ed to prevent it from blocking process exit.

The manager also exposes a `freshFetchPromise` that resolves once the first successful network fetch completes, allowing consumers to wait for guaranteed fresh data when needed.

## Key API Methods for Feature Flag Management

The `featureFlagManager` exposes a concise API for checking flag states:

| Method | Description |
|--------|-------------|
| `get(flagName: string, defaultValue?: any): any` | Returns the current flag value, falling back to `defaultValue` if the flag is missing. |
| `wasLoadedFromCache(): boolean` | Indicates whether the current flag set originated from the local cache. |
| `waitForFreshFlags(): Promise<void>` | Returns a promise that resolves once the first successful network fetch completes. |
| `refresh(): Promise<boolean>` | Forces an immediate fetch from the remote source, useful for debugging. |
| `getAll(): Record<string, any>` | Returns a shallow copy of the entire flag map for debugging purposes. |
| `initialize(): Promise<void>` | Loads cache and starts the background refresh cycle; called once at application startup. |

## Practical Implementation Examples

### Checking Simple Boolean Flags

For basic feature toggling, import the singleton and call `get()` with a default value:

```typescript
import { featureFlagManager } from './utils/feature-flags.js';

function maybeShowBetaBanner() {
  if (featureFlagManager.get('beta_banner_enabled', false)) {
    // Render beta banner UI
    console.log('Beta features are visible');
  }
}

```

This pattern appears in [`src/utils/usageTracker.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/usageTracker.ts), where functionality is gated behind specific flag checks.

### Coordinating with Fresh Flag Data

When launching features that require up-to-date configuration (such as onboarding flows), check whether flags were loaded from cache and wait for fresh data if necessary:

```typescript
import { featureFlagManager } from './utils/feature-flags.js';

export async function startOnboarding() {
  if (featureFlagManager.wasLoadedFromCache()) {
    // Fresh flags are still loading; wait for them
    await featureFlagManager.waitForFreshFlags();
  }

  const onboardingEnabled = featureFlagManager.get('onboarding_injection', false);
  if (onboardingEnabled) {
    // Launch onboarding flow
  }
}

```

This approach is implemented in [`src/utils/welcome-onboarding.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/welcome-onboarding.ts), ensuring users see onboarding only after remote settings are confirmed.

### Forcing Manual Refreshes

For debugging or administrative interfaces, force an immediate refresh to bypass the normal cache cycle:

```typescript
import { featureFlagManager } from './utils/feature-flags.js';

async function debugRefresh() {
  const success = await featureFlagManager.refresh();
  console.log('Feature flags refreshed:', success);
  console.log('Current flags:', featureFlagManager.getAll());
}

```

### Implementing A/B Testing

The system supports weighted variants through the v2 schema. Access experiment configurations to determine which variant a user belongs to:

```typescript
import { featureFlagManager } from './utils/feature-flags.js';

function isNewSidebarEnabled() {
  const experiments = featureFlagManager.get('experiments', {});
  return experiments.sidebar_variant === 'new';
}

```

## Configuration and Environment Variables

The feature flag system respects environment-specific configuration:

- **`DC_FLAG_URL`**: Override the default production URL (`https://desktopcommander.app/flags/v2/production.json`) by setting this environment variable.
- **Cache Location**: The local cache file path is determined by `CONFIG_FILE` in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts), typically resolving to `<config-dir>/feature-flags.json`.
- **Cache Duration**: The `cacheMaxAge` is set to 30 minutes (1800000ms), balancing freshness with network efficiency.

## Summary

- Desktop Commander MCP implements feature flags via the `featureFlagManager` singleton in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts).
- The system loads cached flags immediately at startup, then fetches fresh configuration from a remote JSON endpoint in the background.
- Use `get()` for simple boolean checks, `waitForFreshFlags()` for flows requiring guaranteed fresh data, and `refresh()` for manual updates.
- The manager supports A/B testing through the v2 flag schema and experiment configurations.
- Configuration is controlled via the `DC_FLAG_URL` environment variable and `CONFIG_FILE` path in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts).

## Frequently Asked Questions

### How do I check if a feature flag is enabled in Desktop Commander MCP?

Import the `featureFlagManager` from [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts) and call the `get()` method with the flag name and a default value. For example: `featureFlagManager.get('beta_banner_enabled', false)`. This returns the current flag value or the default if the flag is undefined.

### What happens if the remote feature flag server is unavailable?

The application falls back to the locally cached [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) file stored in the configuration directory. The manager loads this cache immediately on startup via `initialize()`, ensuring the application remains functional even during network outages. You can detect this fallback state by calling `wasLoadedFromCache()`.

### How can I force an immediate update of feature flags without waiting for the 30-minute refresh cycle?

Call `await featureFlagManager.refresh()` to trigger an immediate fetch from the remote URL. This method returns a boolean indicating success and is useful for debugging or when you need to verify configuration changes instantly. The method updates the internal cache and the local [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) file upon successful retrieval.

### Where should I initialize the feature flag manager in my application?

The manager should be initialized once at application startup. In the Desktop Commander MCP source, this occurs in [`src/index.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/index.ts) via `await featureFlagManager.initialize()`. This non-blocking call loads the cache and starts the background refresh timer, making flags available immediately while fetching fresh data asynchronously.