How to Dynamically Update MCP Server Configuration Without Restarting: DesktopCommanderMCP Implementation

DesktopCommanderMCP enables dynamic configuration updates by watching configuration files with Node's fs.watch and atomically swapping in-memory config objects, eliminating the need for server restarts.

The DesktopCommanderMCP server implements a live configuration reloading system that allows administrators to modify operational parameters without interrupting active sessions. This architecture centers on a Config Manager module that monitors config.json or server.yaml for changes and propagates updates throughout the server stack. Understanding how to dynamically update MCP server configuration without restarting ensures zero-downtime adjustments to logging levels, port numbers, and feature flags.

How the Hot-Reload Mechanism Works

The dynamic update system relies on persistent file watching and atomic state management to ensure consistency across concurrent operations.

File Watching Implementation

In src/config-manager.ts, the server initializes a file watcher using Node.js native fs.watch or the robust chokidar package. This watcher maintains a persistent listener on the configuration file path, triggering reload logic whenever the operating system reports a file change event.

Atomic Configuration Swapping

When a change event fires, the manager re-parses the configuration file and performs an atomic assignment to the shared in-memory config object. This design ensures that all concurrent request handlers observe a consistent state without race conditions or partial updates during the transition.

Implementing the File Watcher in src/config-manager.ts

The core implementation uses an EventEmitter pattern to broadcast configuration changes while safeguarding against parse errors.

import { watch } from 'fs';
import { EventEmitter } from 'events';
import { readConfigFile, validateConfig } from './config';

const configUpdated = new EventEmitter();
let currentConfig = readConfigFile();   // initial load

// Watch for changes and reload
watch('config.json', { persistent: true }, (event) => {
  if (event === 'change') {
    try {
      const newConfig = readConfigFile();
      validateConfig(newConfig);           // schema validation
      currentConfig = newConfig;            // atomic swap
      configUpdated.emit('updated', newConfig);
      console.log('🔄 MCP configuration reloaded');
    } catch (e) {
      console.error('❌ Failed to reload config:', e);
    }
  }
});

export const getConfig = () => currentConfig;
export const onConfigUpdate = (listener) => configUpdated.on('updated', listener);

The persistent: true option ensures the watcher remains active as long as the server process runs, while the atomic assignment currentConfig = newConfig guarantees that subsequent calls to getConfig() return the complete new configuration.

Consuming Live Configuration in Dependent Modules

Server components such as the logging subsystem subscribe to configuration updates through the event emitter exported by the config manager.

import { getConfig, onConfigUpdate } from '../config-manager';
import { createLogger } from './logger';

let logger = createLogger(getConfig().logLevel);

onConfigUpdate((newConfig) => {
  // Re‑create the logger with the new level
  logger = createLogger(newConfig.logLevel);
  console.log('🛠️ Logger reconfigured to level:', newConfig.logLevel);
});

This pattern allows any module to reinitialize its internal state when specific configuration values change, such as adjusting log verbosity or toggling feature flags.

Configuration File Locations and Formats

DesktopCommanderMCP supports multiple configuration formats for different deployment scenarios:

  • config.json: Primary JSON configuration consumed by standard deployments
  • server.yaml: Alternative YAML configuration used for Docker and orchestrator setups
  • src/tools/config.ts: Provides helper functions readConfigFile() and validateConfig() for parsing and schema validation
  • src/logger.ts: Example implementation showing how dependent modules subscribe to config updates

Error Handling and Validation

The hot-reload system includes safeguards against configuration corruption. When the file watcher detects a change, the new configuration undergoes schema validation before the atomic swap occurs. If parsing fails due to malformed JSON or YAML syntax, the manager logs the error to stderr and retains the previous valid configuration. This prevents the server from entering an invalid state while alerting administrators to syntax issues through console output.

Summary

  • DesktopCommanderMCP implements dynamic configuration through src/config-manager.ts using persistent file watchers
  • Configuration updates are applied via atomic assignment to a shared in-memory object visible to all request handlers
  • Dependent modules receive updates via the configUpdated EventEmitter and can reinitialize without restarting the server
  • Invalid configuration changes are rejected, preserving the last known good state and preventing downtime
  • Both JSON and YAML formats are supported via config.json and server.yaml for different operational contexts

Frequently Asked Questions

What happens if the configuration file contains syntax errors?

The Config Manager validates all changes against the schema before applying them. If parsing fails due to malformed JSON or YAML, the error is logged and the server continues operating with the previous valid configuration, preventing downtime from invalid updates.

Can I switch between JSON and YAML configuration formats?

Yes. The server accepts both config.json and server.yaml files, with the Config Manager handling format detection and parsing appropriately. Use JSON for standard deployments and YAML for Docker orchestration environments.

How do server components know when settings change?

Modules import onConfigUpdate from src/config-manager.ts and register listener functions. When the configuration reloads, the EventEmitter broadcasts the new configuration object to all subscribers, allowing components like loggers to reinitialize with updated parameters.

Is the configuration update process thread-safe?

Yes. The Config Manager performs an atomic swap when replacing the currentConfig variable, ensuring that all concurrent request handlers see a consistent configuration state. This prevents race conditions where some handlers might use partial or mixed configuration values.

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 →