# How the DesktopCommanderMCP Configuration Manager Persists Settings Between Server Restarts

> Learn how the DesktopCommanderMCP configuration manager persists settings during server restarts. Discover its JSON file persistence and write strategies for reliable data handling.

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

---

**The DesktopCommanderMCP server persists settings between restarts by maintaining a singleton `ConfigManager` that reads from and writes to a JSON file on disk, using synchronous writes for immediate persistence and queued non-blocking writes for high-frequency updates.**

The [DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP) MCP server relies on a robust configuration persistence mechanism to maintain user settings across process terminations, container restarts, and system reboots. At the heart of this system is the **`ConfigManager`** singleton defined in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), which manages a JSON-based configuration file that serves as the single source of truth for all server settings. Understanding how this configuration manager persists settings between server restarts is essential for developers building plugins or debugging stateful behavior in the `wonderwhy-er/DesktopCommanderMCP` repository.

## Configuration File Initialization and Location

The persistence layer targets a specific JSON file determined by the `CONFIG_FILE` constant exported from [`src/config.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.js). When the server launches, the `ConfigManager.init()` routine checks this location for an existing [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json).

According to lines 70-92 in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), the initialization logic follows this path:

- If [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json) exists, the manager parses the JSON into `this.config`.
- If the file is missing, the system generates a default configuration via `getDefaultConfig()`, marks the session as a **first-run**, and immediately persists these defaults to disk.

This initialization pattern guarantees that every server instance starts with a valid, readable configuration state, ensuring that settings persist between server restarts from the very first execution.

### Version Stamping on Load

After loading or creating the configuration, the manager injects the current library `VERSION` into the in-memory object. As implemented in lines 92-94 of [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), this version stamping ensures that the persisted file always reflects the running code version, which aids in migration debugging and compatibility checking across restarts.

## Synchronous Updates for Critical Persistence

When tools require immediate persistence to survive unexpected crashes, the code path uses `setValue()`. This method updates `this.config[key]` in memory and then calls `saveConfig()`, which serializes the data through an internal **`writeChain`** mechanism to prevent race conditions.

The implementation in lines 41-50 of [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) handles this process:

1. Updates the in-memory `this.config` object.
2. Chains the write operation via `writeChain` to ensure sequential disk access.
3. Writes the file using `JSON.stringify(this.config, null, 2)` to maintain human-readable formatting with two-space indentation.

Because the promise returned by `setValue()` does not resolve until the file is flushed to disk, this method provides guaranteed persistence for critical settings between server restarts.

## Non-Blocking Updates for High-Frequency Operations

Background processes that generate rapid configuration changes—such as usage statistics or telemetry timestamps—use **`setValueNonBlocking()`**. Found in lines 82-90 of [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), this method queues a coalesced write via `scheduleSave()`.

This architectural choice ensures that multiple rapid updates collapse into a single disk write, protecting the libuv thread pool from I/O congestion while still guaranteeing eventual consistency. The configuration manager persists these settings to disk without blocking the main execution thread, making it ideal for high-frequency data that does not require immediate crash survivability.

## Stable Client Identification Across Restarts

The system generates a persistent UUID for analytics and A/B testing through **`getOrCreateClientId()`**. As implemented in lines 15-24 of [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), this method:

- Checks for an existing `clientId` field in the configuration.
- Generates a new UUID if absent.
- Stores the identifier via `setValue()` to ensure it persists to [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json).

Because this value is written to disk immediately upon creation, the same **client ID** survives across server restarts, system reboots, and container recreations, providing stable telemetry identification.

## Bulk Configuration Operations

For complete configuration resets or bulk updates, the manager provides `resetConfig()` and `updateConfig()`. Both methods replace the in-memory configuration object and immediately trigger `saveConfig()` to synchronize the disk state, as shown in lines 92-100 of [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts).

These operations are atomic from the caller's perspective—the file on disk always matches the in-memory state once the method resolves, ensuring that bulk changes to how the configuration manager persists settings between server restarts remain consistent.

## Practical Implementation Examples

The following examples demonstrate how to interact with the persistent configuration system in `wonderwhy-er/DesktopCommanderMCP`.

Retrieve the current configuration from disk:

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

async function showConfig() {
  const cfg = await configManager.getConfig();
  console.log('Current config:', cfg);
}

showConfig();

```

Update a critical setting with immediate persistence:

```typescript
await configManager.setValue('telemetryEnabled', false);
// The change is written to config.json before the promise resolves.

```

Queue a non-blocking update for high-frequency data:

```typescript
await configManager.setValueNonBlocking('lastSeen', Date.now());
// Returns immediately; the write is queued in the background.

```

Ensure a stable client ID exists for analytics:

```typescript
const clientId = await configManager.getOrCreateClientId();
console.log('Client ID for analytics:', clientId);

```

## Summary

- The **`ConfigManager`** singleton in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) maintains an in-memory copy of settings loaded from [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json) at startup.
- **Synchronous writes** via `setValue()` use a chained `writeChain` mechanism to prevent race conditions and guarantee immediate persistence to disk.
- **Non-blocking writes** via `setValueNonBlocking()` coalesce multiple updates into a single disk operation to protect system resources while ensuring eventual consistency.
- **Version stamping** and **client ID generation** (lines 15-24 and 92-94) ensure the persisted state remains consistent and identifiable across server restarts.
- All configuration changes ultimately serialize to the JSON file defined by `CONFIG_FILE` in [`src/config.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.js), ensuring settings survive process termination, container restarts, and system reboots.

## Frequently Asked Questions

### Where is the configuration file stored in DesktopCommanderMCP?

The configuration file location is determined by the `CONFIG_FILE` constant exported from [`src/config.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.js). This path points to a [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json) file in the user's configuration directory, ensuring that the configuration manager persists settings between server restarts by writing to a stable location outside temporary process memory.

### How does the configuration manager handle concurrent write operations?

The manager prevents race conditions by serializing write operations through an internal `writeChain` promise queue. When `saveConfig()` is called, it chains the new write operation to the previous one, ensuring that rapid successive calls to `setValue()` result in sequential—not parallel—disk access, preventing file corruption.

### What happens if the config.json file is corrupted or deleted?

If [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json) is missing during initialization, the `ConfigManager.init()` routine automatically generates a default configuration using `getDefaultConfig()` and writes it to disk. While the provided source analysis does not explicitly detail corrupted JSON parsing error handling, the initialization logic assumes either valid JSON or a first-run scenario where defaults are applied, ensuring the server can always start successfully.

### Can I use non-blocking updates for critical configuration changes?

No. According to the source code in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts), you should use `setValue()` for critical changes that must survive a crash, as it awaits the write completion before resolving. Reserve `setValueNonBlocking()` for high-frequency, background-only data like timestamps or usage counters where eventual consistency is acceptable and immediate persistence between server restarts is not required.