# Desktop Commander MCP featureFlagManager: Purpose and Initialization Guide

> Learn the purpose of `featureFlagManager` in Desktop Commander MCP. Discover how this service centralizes runtime feature flag handling and initializes flags by fetching remote configuration.

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

---

**The `featureFlagManager` is a singleton service that centralizes runtime feature flag handling by fetching remote configuration from `desktopcommander.app`, caching it locally in `<config-dir>/feature-flags.json`, and providing synchronous access with graceful network fallback.**

Desktop Commander MCP uses feature flags to control feature rollouts and experimental functionality without redeploying the client. The `featureFlagManager` singleton, implemented in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), orchestrates the initialization and lifecycle of these flags to ensure the application starts instantly while maintaining fresh configuration data.

## What is the featureFlagManager?

The `featureFlagManager` is the sole instance of the `FeatureFlagManager` class that acts as the central authority for runtime configuration in Desktop Commander MCP. It bridges remote flag definitions with local execution, ensuring the application can respond to feature toggles immediately while refreshing data silently in the background.

### Remote Configuration and Environment Overrides

According to the source code in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), the manager pulls flag definitions from `https://desktopcommander.app/flags/v2/production.json` by default. You can override this endpoint by setting the `DC_FLAG_URL` environment variable, allowing custom deployments or testing environments to use alternate flag configurations without modifying the codebase.

### Local Caching for Offline Resilience

The manager persists flag payloads to `<config-dir>/feature-flags.json` via the `saveToCache` method (lines 24-31). This guarantees that `loadFromCache` (lines 39-59) can populate the in-memory store on startup even when the network is unavailable, reducing latency and ensuring feature availability during offline usage.

### Defensive Network Handling

The `fetchFlags()` method implements a defensive fetching strategy with a 3-second abort timeout to prevent application hangs on slow networks. It also utilizes `Promise.race` (lines 74-91) to enforce a hard timeout as a safeguard against platform-specific abort inconsistencies, ensuring the bootstrap process never stalls waiting for flag data.

## How Feature Flags Are Initialized

Initialization follows a resilient three-phase process defined in the `initialize()` method (lines 42-73) and invoked early in the application lifecycle from [`src/bootstrap.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/bootstrap.ts).

### Construction and Promise Setup

When `new FeatureFlagManager()` executes (line 25), it resolves the cache file path using the configuration directory and determines the remote URL by checking `process.env.DC_FLAG_URL`. It initializes a `freshFetchPromise` that tracks the completion of the first successful network fetch, enabling optional waiting for live data.

### Cache-First Loading Strategy

The `initialize()` method begins by calling `await this.loadFromCache()`, which reads the local JSON cache if present and populates `this.flags`. This ensures `loadedFromCache` is set to `true` and the application can immediately respond to feature checks without network latency.

### Background Fetch and Periodic Refresh

After cache loading, `initialize()` triggers `this.fetchFlags()` asynchronously (lines 48-53). This operation runs in parallel with application startup, resolving `freshFetchPromise` upon completion without blocking the main thread. Errors during this fetch are logged but do not reject the initialization promise, preventing startup failures due to transient network issues.

The method then starts a periodic refresh interval using `setInterval(..., this.cacheMaxAge)` (lines 61-66), defaulting to a 5-minute interval (300,000ms). The timer is `unref()`-ed (line 70) to ensure the process can exit gracefully even if a refresh is pending.

### Cleanup on Shutdown

The `destroy()` method (lines 38-45) clears the periodic refresh interval, preventing memory leaks and dangling timers when the application shuts down or undergoes hot reloads during development.

## Runtime Flag Access Patterns

Once initialized, the manager offers two distinct access patterns depending on whether you need immediate availability or guaranteed freshness.

### Synchronous Reading with get()

The `get(flag, fallback)` method provides instant synchronous access to the in-memory flag store. This is the primary interface for checking feature states without introducing async complexity:

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

// Returns current value or false if undefined
const isBetaEnabled = featureFlagManager.get('betaFeature');

// Provide custom fallback default
const maxRetries = featureFlagManager.get('retryAttempts', 3);

```

### Waiting for Fresh Data with waitForFreshFlags()

For critical logic requiring the latest remote configuration, `waitForFreshFlags()` returns a promise that resolves when the background fetch completes or the 3-second timeout elapses. This is essential for onboarding flows or experiment activation:

```typescript
await featureFlagManager.waitForFreshFlags();
if (featureFlagManager.get('showWelcomeTour')) {
  launchWelcomeTour();
}

```

### Manual Refresh Triggers

You can force immediate updates outside the normal refresh cycle using the `refresh()` method. This is particularly useful in testing scenarios or when responding to specific user actions that require immediate flag consistency.

## Summary

- **Singleton Architecture**: The `featureFlagManager` in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts) provides centralized, thread-safe feature flag management for Desktop Commander MCP.
- **Resilient Initialization**: The `initialize()` method loads cached flags instantly via `loadFromCache()`, then fetches fresh values in the background with 3-second timeouts and graceful error handling.
- **Continuous Synchronization**: A 5-minute refresh interval (`cacheMaxAge`) keeps flags current without blocking the main thread, with `unref()` ensuring clean process termination.
- **Dual Access Modes**: Synchronous `get()` calls serve general use cases from memory, while `waitForFreshFlags()` supports latency-sensitive decisions requiring guaranteed live data.
- **Environment Flexibility**: Custom endpoints via `DC_FLAG_URL` and atomic cache persistence enable offline operation and custom deployment pipelines.

## Frequently Asked Questions

### How does featureFlagManager handle network failures during initialization?

The `initialize()` method catches fetch errors without rejecting the initialization promise, ensuring the application starts successfully using cached values from the previous run. Network failures are logged via the internal logger, and the periodic refresh interval continues attempting updates every 5 minutes until connectivity restores.

### Can I use feature flags offline in Desktop Commander MCP?

Yes. The manager writes flag payloads atomically to `<config-dir>/feature-flags.json` during each successful fetch via `saveToCache`. On startup, `loadFromCache()` populates the in-memory store from this file, making flags available immediately even without network access. Values remain cached for 30 minutes before considered stale.

### What is the difference between get() and waitForFreshFlags()?

`get()` returns the current in-memory value synchronously, using cached data if the background fetch hasn't completed. `waitForFreshFlags()` returns a promise that resolves only after a fresh network fetch succeeds or the 3-second timeout elapses, ensuring you access the most recent remote configuration. Use `get()` for general checks and `waitForFreshFlags()` before critical user-facing decisions.

### How do I configure a custom feature flag endpoint?

Set the `DC_FLAG_URL` environment variable before starting Desktop Commander MCP. The constructor at line 25 checks `process.env.DC_FLAG_URL` first, falling back to the production URL only if undefined. This allows testing environments or forks to use alternate flag configurations without code changes, as the manager will fetch from your specified URL during the next `fetchFlags()` cycle.