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

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

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:

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: Contains the ConfigManager singleton implementing the promise chain (this.writeChain) and coalesced save logic (scheduleSave()).
  • src/config.ts: Defines the CONFIG_FILE constant (path to config.json) and default configuration values.
  • 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, 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. The path is defined by the CONFIG_FILE constant in src/config.ts, and all read/write operations are managed by the ConfigManager class in 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. 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.

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 →