How to Use Feature Flags in Desktop Commander MCP: A Complete Guide
Desktop Commander MCP provides a lightweight feature-flag system via the featureFlagManager singleton in 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:
FeatureFlagManagerclass: Defined insrc/utils/feature-flags.ts, this class handles loading from cache, fetching from remote JSON endpoints, and managing refresh timers.featureFlagManagersingleton: The exported instance imported by consumers throughout the codebase, ensuring consistent state across the application.- Non-blocking initialization: In
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:
- Cache First: On startup, it loads existing flags from
<config-dir>/feature-flags.json(defined viaCONFIG_FILEinsrc/config.ts), ensuring immediate availability even offline. - Background Fetch: It then fetches fresh JSON from
DC_FLAG_URL(defaulting tohttps://desktopcommander.app/flags/v2/production.json) and updates the cache. - Periodic Refresh: A
setIntervaltimer runs every 30 minutes (cacheMaxAge), callingfetchFlags()to retrieve updates. The timer isunref-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:
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, 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:
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, 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:
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:
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_FILEinsrc/config.ts, typically resolving to<config-dir>/feature-flags.json. - Cache Duration: The
cacheMaxAgeis set to 30 minutes (1800000ms), balancing freshness with network efficiency.
Summary
- Desktop Commander MCP implements feature flags via the
featureFlagManagersingleton insrc/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, andrefresh()for manual updates. - The manager supports A/B testing through the v2 flag schema and experiment configurations.
- Configuration is controlled via the
DC_FLAG_URLenvironment variable andCONFIG_FILEpath insrc/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 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 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 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 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.
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 →