# How the Remote Feature Flag System Works in Desktop Commander MCP and How to Disable It

> Learn how Desktop Commander MCP's remote feature flag system works via WebSocket or HTTP. Discover how to disable remote fetching and manage flags efficiently.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: internals
- Published: 2026-07-28

---

**Desktop Commander MCP implements a cached remote feature flag system that fetches flag payloads via WebSocket or HTTP long-polling through [`src/remote-device/remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/remote-device/remote-channel.ts), stores them in [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json), and gates functionality via `FeatureFlagManager.isEnabled()`; you can disable remote fetching entirely by setting the environment variable `DC_DISABLE_REMOTE_FLAGS=1` or deleting the local cache file.**

Desktop Commander MCP uses a lightweight remote feature flag system that enables dynamic toggling of experimental features without redeploying the application binary. The architecture centers on the `FeatureFlagManager` class defined in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), which maintains an in-memory flag set synchronized with a remote service while providing a local file cache for offline resilience. Understanding this mechanism is essential for developers operating in air-gapped environments or requiring deterministic feature states.

## Architecture of the Remote Feature Flag System

The feature flag architecture consists of three primary components that coordinate to provide synchronous flag evaluation after initial startup.

### Core Components

- **`FeatureFlagManager`** ([`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts)): The central singleton that holds the latest flag payload in memory. It exposes `isEnabled(name)` for synchronous checks, `waitForFreshFlags()` for async initialization, and `updateFlags(payload)` for ingesting remote updates. It persists state to a local JSON file.
- **Remote Channel** ([`src/remote-device/remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/remote-device/remote-channel.ts)): Manages the network connection to the feature flag service using WebSocket or HTTP long-polling. It requests the flag payload on application startup and periodically refreshes it, invoking `featureFlagManager.updateFlags()` when new data arrives.
- **Cache File** ([`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json)): Located in the user configuration directory (typically `~/.config/desktop-commander-mcp/`), this file stores the last known flag set to enable instant cold starts and serves as a fallback when remote services are unreachable.

### Data Flow

1. **Initialization**: On startup, `FeatureFlagManager` instantiates and attempts to load [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) from the user config directory.
2. **Remote Fetch**: [`remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/remote-channel.ts) establishes a connection to the central service and requests the current flag payload, including application version metadata.
3. **Update**: When the server responds, the channel calls `featureFlagManager.updateFlags()`, which updates the in-memory map and overwrites the local cache file.
4. **Evaluation**: Application code throughout the codebase calls `featureFlagManager.isEnabled('flag-name')` to gate features, onboarding steps, or UI components without blocking the main thread.

All flag checks remain **synchronous** after the initial load, ensuring zero UI-blocking network calls. If the remote fetch fails due to timeout, network error, or server unavailability, the system falls back to the cached [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) or a static **default-deny** set defined in the class constructor.

## Evaluating Feature Flags at Runtime

The system prioritizes performance and reliability through synchronous evaluation and robust fallback mechanisms.

### Synchronous Flag Checks

Once initialized, feature flag evaluation requires no asynchronous operations. The `isEnabled()` method performs a simple lookup in the in-memory map:

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

if (featureFlagManager.isEnabled('welcome-page')) {
  // Render experimental welcome UI
}

```

This pattern appears throughout the codebase, including in [`src/utils/welcome-onboarding.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/welcome-onboarding.ts) and the A/B test wrapper ([`src/utils/ab-test.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/ab-test.ts)), which delegates to `featureFlagManager.isEnabled()` under the hood.

### Fallback and Default-Deny Behavior

When `featureFlagManager` cannot retrieve fresh flags and finds no valid cache file, it initializes with an empty flag set `{}`. Per the implementation in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), the `isEnabled()` method treats missing flags as **disabled** (default-deny), ensuring that network failures cannot accidentally enable unstable features.

## How to Disable the Remote Feature Flag System

To run Desktop Commander MCP without remote flag evaluation, use one of the following non-destructive configuration methods.

### Disable via Environment Variable

Set `DC_DISABLE_REMOTE_FLAGS=1` before launching the application. The [`remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/remote-channel.ts) module checks this variable early in its initialization and skips the WebSocket connection entirely:

```typescript
// In src/remote-device/remote-channel.ts
if (process.env.DC_DISABLE_REMOTE_FLAGS === '1') {
  console.log('Remote feature-flag fetching is disabled by env var.');
  return;
}
// WebSocket connection logic follows...

```

Apply this setting in your shell or a `.env` file at the repository root:

```bash
export DC_DISABLE_REMOTE_FLAGS=1

```

### Remove the Local Cache File

Deleting [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) forces the manager to start with an empty flag set, triggering the default-deny behavior for all flags. This is useful for one-time resets without modifying environment variables:

```javascript
import { promises as fs } from 'fs';
import path from 'path';
import { appConfigDir } from './utils/system-info';

async function disableFeatureFlags() {
  const cachePath = path.join(appConfigDir(), 'feature-flags.json');
  await fs.rm(cachePath, { force: true });
  console.log('Feature-flag cache removed; all remote flags disabled.');
}

```

## Implementation Examples

The following patterns demonstrate practical interactions with the feature flag system based on the reference implementation.

### Checking if a Feature is Enabled

Reference the `FeatureFlagManager` instance to gate application logic:

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

// Check specific flag
if (featureFlagManager.isEnabled('new-search')) {
  activateNewSearch();
}

```

### Disabling Remote Fetch in Source

When debugging or operating offline, you can verify the remote channel respects the disable flag:

```typescript
// src/remote-device/remote-channel.ts (excerpt)
const remoteFlagsEnabled = process.env.DC_DISABLE_REMOTE_FLAGS !== '1';
if (!remoteFlagsEnabled) {
  console.log('Remote feature-flag fetching disabled');
  return;
}

```

### Clearing the Feature Flag Cache

Force a clean slate by removing the persisted cache before initialization:

```javascript
const fs = require('fs').promises;
const path = require('path');
const configDir = require('./utils/system-info').appConfigDir;

async function clearFlagCache() {
  const filePath = path.join(configDir(), 'feature-flags.json');
  await fs.unlink(filePath).catch(() => {});
}

```

## Summary

- **Desktop Commander MCP** synchronizes feature flags via [`src/remote-device/remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/remote-device/remote-channel.ts), stores them in [`src/utils/feature-flags.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/feature-flags.ts), and persists to [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json).
- The **`FeatureFlagManager`** provides synchronous `isEnabled()` checks with a **default-deny** fallback for missing or stale flags.
- **Disable remote fetching** by setting the environment variable `DC_DISABLE_REMOTE_FLAGS=1` before startup.
- **Clear local state** by deleting the [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) cache file from the user configuration directory.
- The system includes test coverage in [`test/test-onboarding-injection-flag.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-onboarding-injection-flag.js) and [`test/test-feature-flags-timeout.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-feature-flags-timeout.js) for timeout and fallback validation.

## Frequently Asked Questions

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

If the remote service is unreachable, [`remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/remote-channel.ts) aborts the request after a timeout (tested in [`test/test-feature-flags-timeout.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-feature-flags-timeout.js)). The `FeatureFlagManager` then falls back to the cached [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) file or initializes with an empty set, causing `isEnabled()` to return `false` for all flags due to the default-deny implementation.

### Can I override individual feature flags without disabling the entire system?

Yes. You can manually edit the [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) file in the user configuration directory to inject specific flag values. The `FeatureFlagManager` loads this cache on startup, allowing you to force-enable or force-disable specific features while keeping the remote sync active for other flags.

### Where is the feature flag cache stored on disk?

The cache file [`feature-flags.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/feature-flags.json) resides in the Desktop Commander MCP user configuration directory, typically located at `~/.config/desktop-commander-mcp/feature-flags.json` on Linux/macOS or the equivalent platform-specific application data directory. The path is resolved via `appConfigDir()` in `src/utils/system-info`.

### Is the remote feature flag system enabled by default?

Yes. By default, the application attempts to connect to the remote flag service on startup via [`src/remote-device/remote-channel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/remote-device/remote-channel.ts). Remote fetching only disables if the `DC_DISABLE_REMOTE_FLAGS` environment variable is set to `1` or if the network connection is unavailable and no cache exists.