Handling JSON Errors and Corruption in MCP Configuration Persistence: How DesktopCommanderMCP Implements Bulletproof Config Storage

DesktopCommanderMCP prevents configuration crashes by wrapping JSON.parse in try-catch blocks that fall back to default values on corruption, while serializing all disk writes through a single promise chain to eliminate race conditions.

DesktopCommanderMCP stores its runtime settings in a JSON file that must survive crashes, power failures, and concurrent modifications. The ConfigManager class in src/config-manager.ts implements a defensive persistence strategy that handles JSON errors and corruption in MCP configuration persistence through graceful recovery mechanisms and atomic write operations.

Detecting and Recovering from Corrupted Configuration Files

The ConfigManager class treats every configuration load as a potentially unsafe operation. Rather than assuming the JSON file is valid, it implements multiple layers of error detection that ensure the application never enters an undefined state.

Graceful Initialization with Automatic Fallback

When the configuration manager initializes via the init() method, it attempts to read and parse the existing config.json file. If JSON.parse throws due to truncation, syntax errors, or incomplete writes, the system immediately falls back to a fresh in-memory default configuration.

try {
  await fs.access(this.configPath);
  const configData = await fs.readFile(this.configPath, 'utf8');
  this.config = JSON.parse(configData);          // ← may throw
  this._isFirstRun = false;
} catch (error) {
  // Config file missing or corrupted → create fresh defaults
  this.config = this.getDefaultConfig();         // ← default config
  this._isFirstRun = true;
  await this.saveConfig();
}

See the implementation in src/config-manager.ts lines 73‑88.

This pattern ensures that even if the physical file contains malformed JSON or is missing entirely, the application continues running with sensible defaults. The _isFirstRun flag allows the system to detect fresh installations versus recoveries from corruption.

Comprehensive Error Logging and Safe State Guarantees

Beyond parsing errors, the initialization logic includes a secondary catch block that handles unexpected filesystem or permission errors. In all failure scenarios, the manager logs the specific error and guarantees that this.config contains a valid object before proceeding.

} catch (error) {
  console.error('Failed to initialize config:', error);
  this.config = this.getDefaultConfig();   // ← safe fallback
}

See the implementation in src/config-manager.ts lines 94‑99.

Preventing Corruption During Write Operations

Reading safely is only half the solution. DesktopCommanderMCP also prevents JSON corruption during writes by eliminating race conditions and batching high-frequency updates.

Serialized Write Queues

All configuration writes flow through a single promise chain (writeChain) managed by the saveConfig() method. This architecture ensures that concurrent calls to setValue() cannot interleave their fs.writeFile operations, which would otherwise result in partial writes or corrupted JSON structures.

private async saveConfig(): Promise<void> {
  const write = this.writeChain.then(() => this.writeConfigToDisk());
  this.writeChain = write.catch(() => {});   // keep chain alive
  return write;
}

See the implementation in src/config-manager.ts lines 94‑99.

By chaining writes sequentially, the system guarantees that only one write operation touches the disk at a time, even when multiple configuration changes occur simultaneously.

Coalesced Background Persistence

For high-frequency, non-critical updates such as usage statistics or telemetry toggles, the scheduleSave() method batches pending writes into a single background operation. This reduces disk I/O and minimizes the window where corruption could occur during unexpected shutdowns.

scheduleSave(): void {
  if (this.saveScheduled) return;
  this.saveScheduled = true;
  this.writeChain = this.writeChain.then(async () => {
    this.saveScheduled = false;
    await this.writeConfigToDisk();
  });
}

See the implementation in src/config-manager.ts lines 109‑119.

Working with the ConfigManager API

The ConfigManager exposes a clean API that abstracts these resilience mechanisms from the rest of the application. Tools throughout the codebase interact with configuration values without worrying about file corruption or serialization logic.

Loading Configuration Values Safely

Components retrieve settings through the getValue() method, which always returns a valid value or undefined rather than throwing:

// Example: a tool that needs the current configuration
import { configManager } from '../config-manager.js';

async function getShell() {
  const shell = await configManager.getValue('defaultShell');
  return shell ?? '/bin/sh';
}

Non-Blocking Updates for UI Responsiveness

When updating values that don't require immediate durability guarantees, use setValueNonBlocking() to persist changes in the background without blocking the caller:

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

async function disableTelemetry() {
  await configManager.setValueNonBlocking('telemetryEnabled', false);
  // The change is persisted in the background; the UI can continue immediately.
}

Manual Reset to Defaults

Users can trigger a complete reset to factory defaults when they suspect configuration corruption:

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

async function resetConfig() {
  const freshConfig = await configManager.resetConfig();
  console.log('Config reset to defaults:', freshConfig);
}

Supporting Configuration Architecture

The persistence layer relies on several supporting components:

  • src/config.ts – Defines the CONFIG_FILE constant that specifies the JSON file location used by ConfigManager.
  • src/utils/capture.js – Handles telemetry events, including opt-out notifications that trigger non-blocking configuration updates.
  • src/utils/feature-flags.ts – Demonstrates JSON fetch-and-cache logic for remote feature-flag payloads, mirroring the same error-tolerant patterns used for local configuration.

Summary

DesktopCommanderMCP implements a multi-layered defense against JSON errors and corruption in MCP configuration persistence:

  • Graceful initialization detects malformed JSON during startup and falls back to default configurations rather than crashing.
  • Comprehensive error handling ensures the application never operates with an undefined configuration state.
  • Serialized write chains prevent race conditions by guaranteeing only one write operation executes at a time.
  • Coalesced background saves batch high-frequency updates to reduce disk wear and corruption risk.

Together, these mechanisms ensure that the config.json file can be recovered from corruption, protected from concurrent access bugs, and maintained reliably across application sessions.

Frequently Asked Questions

What happens if the config.json file is corrupted during a power outage?

DesktopCommanderMCP detects the corruption during the next startup when JSON.parse throws an error. The ConfigManager catches this exception, logs the error, and initializes the application with a fresh default configuration from getDefaultConfig(). The system then writes these clean defaults back to disk, effectively resetting the file to a valid state.

How does DesktopCommanderMCP prevent concurrent writes from corrupting the JSON file?

All write operations flow through a single promise chain (writeChain) in the saveConfig() method. This serialization ensures that even if multiple setValue() calls occur simultaneously, they execute sequentially rather than interleaving their fs.writeFile operations, which would otherwise result in partial JSON structures.

Can users manually reset their configuration if they suspect data corruption?

Yes. The ConfigManager exposes a resetConfig() method that immediately replaces the in-memory configuration with defaults and persists them to disk. This provides a programmatic way to recover from corruption without manually deleting files or editing JSON.

Where does DesktopCommanderMCP store its configuration file?

The configuration file location is defined in src/config.ts as the CONFIG_FILE constant, which ConfigManager references to determine where to read and write the config.json file on the local filesystem.

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 →