# How DesktopCommanderMCP's Configuration Manager Prevents Data Corruption During Concurrent Writes

> DesktopCommanderMCP's Configuration Manager prevents data corruption during concurrent writes by serializing disk operations and coalescing updates. Learn how it safeguards your data.

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

---

**DesktopCommanderMCP prevents configuration file corruption by serializing all disk writes through a promise chain and coalescing rapid successive updates into a single background operation.**

DesktopCommanderMCP stores runtime settings in a JSON file that multiple asynchronous tool calls may modify simultaneously. To prevent race conditions from corrupting this data, the **ConfigManager** class implements a serialized write pipeline in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts). This architecture guarantees that even when numerous operations attempt to persist changes concurrently, the configuration file remains atomically consistent.

## Serializing Writes with a Promise Chain

The core concurrency protection mechanism is a **write chain** (`this.writeChain`) maintained as a `Promise<void>`. Every disk operation appends to this chain, ensuring that each write completes before the next begins.

In [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) (lines 197-210), the `saveConfig()` method constructs a new promise that waits for the existing chain to resolve, performs the actual file write using `fs.writeFile`, and then updates the chain reference. This pattern forces sequential disk access:

- **Exclusive file access**: The promise chain eliminates race conditions by preventing interleaved write operations.
- **Atomic updates**: Each write sees the complete state left by the previous operation, avoiding partial writes or mixed data.

Because every configuration change funnels through this serialized pipeline, two asynchronous operations can never corrupt the JSON structure by writing simultaneously.

## Coalescing High-Frequency Updates

For scenarios involving rapid successive changes—such as usage statistics updates—the manager implements a **coalesced background save** via `scheduleSave()` (lines 221-230 in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts)).

When `scheduleSave()` is invoked:

1. If no write is currently in-flight, it initiates a background save immediately.
2. If a write is already pending, subsequent calls collapse into the same queued operation.
3. Only the latest in-memory state is written once the current operation completes.

This **write coalescing** prevents I/O thrashing by ensuring that a burst of updates generates exactly one disk write rather than many.

## Blocking vs. Non-Blocking Configuration Updates

The configuration manager exposes two distinct APIs for updating values:

- **`setValue(key, value)`**: Performs a blocking save. The method waits for the write chain to complete, guaranteeing the configuration is persisted to disk before returning. Use this when data durability is critical.
- **`setValueNonBlocking(key, value)`**: Updates the in-memory configuration instantly and schedules a background save. This returns immediately, keeping tool-call response times fast while the coalesced write persists asynchronously.

Both methods ultimately route through the same serialized pipeline, maintaining consistency regardless of which API callers choose.

## Usage Examples

The following patterns demonstrate safe configuration updates in DesktopCommanderMCP:

```typescript
import { configManager } from './config-manager.js';

// Immediate, blocking save – guarantees data is on disk before proceeding
await configManager.setValue('defaultShell', '/bin/bash');

// Non-blocking update – updates memory instantly, background write coalesced
await configManager.setValueNonBlocking('telemetryEnabled', false);

// Manual background scheduling (rarely needed; used internally)
configManager.scheduleSave();

```

## Key Source Files

The concurrency control logic spans three primary files:

- **[`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts)**: Contains the `ConfigManager` singleton implementing the promise chain (`this.writeChain`) and coalesced save logic (`scheduleSave()`).
- **[`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts)**: Defines the `CONFIG_FILE` constant (path to [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json)) and default configuration values.
- **[`src/tools/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/config.ts)**: Exposes CLI-level commands that invoke `configManager` methods for user-driven configuration changes.

## Summary

- **Serialized pipeline**: A promise chain (`this.writeChain`) in `saveConfig()` forces writes to execute sequentially, preventing race conditions.
- **Write coalescing**: The `scheduleSave()` method collapses rapid updates into single background writes, reducing I/O overhead.
- **Dual API design**: `setValue()` blocks until persistence completes, while `setValueNonBlocking()` returns immediately with coalesced background saves.
- **Atomic consistency**: All modifications funnel through [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), ensuring the JSON configuration remains uncorrupted even under high concurrency.

## Frequently Asked Questions

### What file format does DesktopCommanderMCP use to store configuration?

DesktopCommanderMCP persists runtime settings to a JSON file named [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json). The path is defined by the `CONFIG_FILE` constant in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts), and all read/write operations are managed by the `ConfigManager` class in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts).

### How does the configuration manager handle rapid successive updates?

The manager uses **write coalescing** via `scheduleSave()`. When multiple updates occur in quick succession, only one background write is queued while another is in-flight. Subsequent calls collapse into the same pending operation, ensuring the disk receives only the final state rather than a series of intermediate writes.

### What is the difference between `setValue()` and `setValueNonBlocking()`?

**`setValue()`** appends to the write chain and awaits completion, blocking until the configuration is safely persisted to disk. **`setValueNonBlocking()`** updates the in-memory configuration immediately and schedules a coalesced background save, returning instantly to maintain fast response times while the actual write proceeds asynchronously.

### Where is the concurrency control logic implemented?

All concurrency protection is implemented in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts). Specifically, lines 197-210 contain the promise chain logic that serializes writes, while lines 221-230 implement the coalesced background save mechanism that prevents write flooding during high-frequency updates.