Desktop Commander MCP featureFlagManager: Purpose and Initialization Guide

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, 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, 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.

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:

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:

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 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.

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 →