How DesktopCommanderMCP Evaluates Feature Flags and Manages A/B Testing
DesktopCommanderMCP implements a two-layer architecture that combines local flag caching with remote configuration updates, plus a deterministic A/B testing utility that assigns weighted variants based on a persistent client identifier.
The Model Context Protocol (MCP) server known as DesktopCommanderMCP relies on dynamic configuration to roll out features gradually and run experiments. Its architecture separates flag management from experiment assignment, enabling fast startup times while ensuring consistent user experiences across sessions.
Feature Flag Manager Architecture
The core flag management logic resides in src/utils/feature-flags.ts inside the FeatureFlagManager class. This singleton handles loading, caching, and refreshing flag definitions from a remote JSON endpoint.
Initialization and Cache Loading
When the featureFlagManager singleton is constructed, it records the cache path (feature-flags.json), the remote URL (from the DC_FLAG_URL environment variable or a default production endpoint), and prepares a promise that resolves once a fresh network fetch completes.
During application startup, the bootstrap process calls initialize(), which performs three critical tasks:
- Loads cached flags from disk via
loadFromCache()to ensure immediate availability. - Kicks off a background
fetchFlags()operation and resolves the initialization promise when fresh data arrives. - Starts a periodic refresh interval (defaulting to 5 minutes) controlled by the
cacheMaxAgeparameter.
This design guarantees that the application can render using cached values while silently updating definitions in the background.
Remote Fetching with Timeout Handling
The fetchFlags() method contacts the remote endpoint using a hard-coded 3-second timeout (FETCH_TIMEOUT_MS). To ensure reliability across different platforms, the implementation combines AbortController with a Promise.race fallback. This guarantees that the timeout triggers even on platforms where abort() might not interrupt an active TCP connection.
Successful responses are persisted to feature-flags.json via saveToCache(), making them available for subsequent launches without network access.
The get() API
Once initialized, code can query flag values through the simple get(name, default) method. This synchronous API returns the current in-memory value or the supplied default if the flag is undefined, allowing components to make split-second rendering decisions without awaiting network operations.
A/B Testing Implementation
Built atop the flag manager, the A/B testing utility in src/utils/ab-test.ts manages experiment assignments. It reads the experiments section from the flag payload, where each experiment defines weighted variants using objects containing name and weight properties.
Deterministic Variant Assignment
The private getVariant(experimentName) method implements the assignment logic using a three-tier lookup strategy:
- Check an in-process
variantCachefor active session consistency. - Fall back to persistent storage using keys formatted as
abTest_<experimentName>in the user configuration managed byconfigManager. - If no assignment exists, generate a deterministic hash using
configManager.getOrCreateClientId()combined with the experiment name.
This hash-based approach ensures that the same client identifier always maps to the same variant, eliminating flickering or reassignment across app restarts.
Weighted Variant Selection Algorithm
When creating a new assignment, the system calculates the total weight of all variants and computes hash % totalWeight. It then iterates through the variant list, accumulating weights until the remainder falls within a specific bucket. If all configured weights are zero, the code automatically falls back to an equal distribution among variants.
The selected variant is immediately persisted to the config store, ensuring stable assignment for future sessions.
Persistence and Helper Functions
The A/B layer exposes several convenience methods:
hasFeature(featureName): Iterates through all experiments to find a variant matching the requested feature name, returning a boolean indicating assignment status.getABTestVariant(name): Public wrapper around the privategetVariant()logic.getABTestAssignments(): Aggregates all storedabTest_*entries from the configuration, useful for analytics reporting and telemetry.
Coordination Between Layers
When code requires up-to-date experiment data, it can await featureFlagManager.waitForFreshFlags(). This method waits for the background fetch promise to resolve (or times out after 5 seconds), ensuring decisions use the latest remote configuration rather than potentially stale cached values.
Typical usage follows this pattern:
import { featureFlagManager } from '@/utils/feature-flags';
import { hasFeature } from '@/utils/ab-test';
async function renderOnboarding() {
await featureFlagManager.waitForFreshFlags();
if (await hasFeature('showOnboardingPage')) {
// Display experiment variant
}
}
Practical Code Examples
Checking a Simple Feature Flag
import { featureFlagManager } from '@/utils/feature-flags';
// Returns boolean or default value
if (featureFlagManager.get('enableNewUI', false)) {
renderNewInterface();
}
Retrieving Current A/B Assignments for Analytics
import { getABTestAssignments } from '@/utils/ab-test';
async function sendTelemetry() {
const assignments = await getABTestAssignments();
// Structure: { experimentName: variantName, ... }
analytics.track('experiment_enrollment', assignments);
}
Waiting for Fresh Configuration
import { featureFlagManager } from '@/utils/feature-flags';
async function initializeExperiments() {
// Ensures flags are no older than cacheMaxAge
await featureFlagManager.waitForFreshFlags();
console.log('Using latest experiment configuration');
}
Summary
- DesktopCommanderMCP uses a two-layer architecture separating flag management from experiment assignment.
- The FeatureFlagManager in
src/utils/feature-flags.tshandles caching tofeature-flags.json, remote fetching with 3-second timeout guarantees viaAbortControllerandPromise.race, and periodic 5-minute refreshes. - A/B testing in
src/utils/ab-test.tsuses deterministic hashing based onconfigManager.getOrCreateClientId()to ensure consistent variant assignment across sessions. - The system supports weighted variants with automatic equal-distribution fallback and persists assignments using
abTest_<experimentName>keys. waitForFreshFlags()provides an optional synchronization point for code requiring the latest remote configuration before making experiment decisions.
Frequently Asked Questions
How does DesktopCommanderMCP handle network timeouts when fetching feature flags?
The fetchFlags() method implements a 3-second timeout using FETCH_TIMEOUT_MS combined with AbortController and Promise.race. This dual approach ensures the timeout triggers reliably even on platforms where the abort signal might not immediately terminate TCP connections, preventing the application from hanging during startup.
What happens if the remote flag server is unavailable on startup?
The system first loads cached flags from feature-flags.json via loadFromCache(), allowing immediate operation. The background fetch proceeds silently, and if it fails, the application continues using cached values. The cache refreshes automatically every 5 minutes when connectivity returns.
How does the A/B testing system ensure users stay in the same variant across app restarts?
After initial assignment, the selected variant is persisted to the user configuration under keys formatted as abTest_<experimentName>. On subsequent launches, getVariant() checks this persistent storage before calculating a new assignment, ensuring users maintain consistent experiences unless explicitly reassigned by configuration changes.
Can experiments use unequal traffic splits between variants?
Yes. The variant selection algorithm supports weighted distributions by reading weight values from the experiment definition. It calculates the total weight, generates a hash-based remainder, and walks the variant list until the cumulative weight exceeds the remainder. If all weights are zero, it automatically falls back to equal distribution among all variants.
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 →