Desktop Commander MCP Error Handling Strategy: 5 Layers of Safe, Consistent Failure Management

Desktop Commander MCP uses a five-layer error handling strategy that captures errors centrally, sanitizes sensitive data for telemetry, and returns uniform ServerResult objects to all consumers.

The wonderwhy-er/DesktopCommanderMCP repository implements a defensive, privacy-first error handling strategy designed for an AI-powered desktop automation server. Every failure follows a predictable path: capture for diagnostics, sanitize for privacy, and standardize for API consistency. This article breaks down exactly how the codebase handles runtime errors without leaking sensitive information.


Layer 1: Central Capture and Sanitization in capture.ts

All errors flow through src/utils/capture.ts, the telemetry gateway. The capture() function records failures for monitoring while sanitizeError() strips personally identifiable information before data leaves the process.

// src/utils/capture.ts – simplified sanitization logic
export function sanitizeError(error: any): { message: string; code?: string } {
  if (typeof error === 'string') {
    return { message: error };
  }
  if (error && typeof error.message === 'string') {
    // Strip file paths and credential fragments
    const clean = error.message.replace(/([A-Za-z]:\\|\/)[^\s]*/g, '[REDACTED]');
    return { message: clean, code: error.code };
  }
  return { message: 'Unknown error' };
}

The sanitization applies only to telemetry payloads. The original error remains intact for internal debugging, ensuring accurate diagnostics without privacy violations. As noted in the source: "Don't sanitize operation name for logs – only telemetry will sanitize if needed."


Layer 2: Uniform ServerResult API Contract

Every public function returns a ServerResult object. Errors set isError: true and provide a plain-text description in the content array. This structure guarantees that UI components, CLI clients, and remote MCP consumers parse responses identically.

The createErrorResponse() helper in src/error-handlers.ts constructs these objects:

// src/error-handlers.ts – central error response builder
export function createErrorResponse(message: string): ServerResult {
  // Record for telemetry (sanitized inside capture)
  capture('server_request_error', { error: message });

  // Return standardized shape
  return {
    content: [{ type: 'text', text: `Error: ${message}` }],
    isError: true,
  };
}

This single function eliminates inconsistent error shapes across the codebase.


Layer 3: Defensive Try/Catch Wrappers

Core tools wrap operations in try { … } catch (e) { … } blocks. The catch handler forwards errors to capture() for telemetry, then transforms them into safe user-facing messages via createErrorResponse().

Real-world example from file system operations:

import { capture } from './utils/capture.js';
import { createErrorResponse } from './error-handlers.js';
import { readFile } from 'fs/promises';

export async function readUserFile(path: string): Promise<ServerResult> {
  try {
    const data = await readFile(path, 'utf8');
    return { content: [{ type: 'text', text: data }], isError: false };
  } catch (e) {
    // Telemetry: recorded and sanitized automatically
    capture('file_read_error', { error: e });

    // User response: safe, uniform message
    const safeMessage = typeof e === 'object' && e?.message
      ? e.message
      : String(e);
    return createErrorResponse(safeMessage);
  }
}

This pattern appears throughout src/tools/filesystem.ts, src/utils/withTimeout.ts, and src/utils/trackTools.ts.


Layer 4: Controlled Logging with logger.ts

The lightweight logger in src/utils/logger.ts writes concise console messages when DEBUG mode is enabled. It never prints raw error objects, preventing accidental data exposure in production logs.

Usage pattern:

import { logger } from './utils/logger.js';

logger.error('File read operation failed');
// Output (DEBUG=1): "ERROR: File read operation failed"
// Output (DEBUG=0): silent

Layer 5: Telemetry-Only Data Flow

The Desktop Commander MCP error handling strategy maintains separation between diagnostics and user feedback:

Path Data Treatment Destination
Telemetry Sanitized via sanitizeError() BigQuery / analytics backends
User response Original message (safe context) ServerResult.content
Debug logs Conditionally enabled, no raw objects Console (development only)

This design prevents the common failure mode of "over-sanitization" that strips useful context from user-facing errors.


Complete Error Flow: Step-by-Step

  1. Operation initiates – async I/O or tool execution begins
  2. Exception caughttry/catch block intercepts the failure
  3. Telemetry recordedcapture() receives raw error; sanitizeError() cleans PII before external transmission
  4. Response constructedcreateErrorResponse() builds ServerResult with isError: true
  5. Result returned – calling REPL, UI, or MCP client receives uniform object

Key Files in the Error Handling Architecture

File Responsibility
src/utils/capture.ts Central telemetry hook with sanitizeError()
src/error-handlers.ts createErrorResponse() and related helpers
src/utils/logger.ts Conditional debug logging
src/utils/withTimeout.ts Timeout wrapper demonstrating error forwarding
src/tools/filesystem.ts Production tool implementation with full error pipeline

Summary

Desktop Commander MCP's error handling strategy delivers three core guarantees:

  • Consistent API contracts via the ServerResult type and createErrorResponse() helper
  • Privacy-preserving telemetry through capture.ts and sanitizeError()
  • Clear user feedback with defensive try/catch patterns in every tool function

The layered approach separates concerns cleanly: diagnostics happen invisibly, sensitive data stays protected, and every client receives predictable, parseable responses.


Frequently Asked Questions

How does Desktop Commander MCP prevent sensitive data leaks in error messages?

The sanitizeError() function in src/utils/capture.ts uses regex patterns to detect and redact file paths, credentials, and other PII from telemetry payloads. This sanitization applies only to external analytics; user-facing error messages retain original context for debugging assistance.

What is the ServerResult type and why is it important?

ServerResult is a standardized response object with content (text array), isError (boolean), and optional metadata. It ensures that UI layers, CLI tools, and remote MCP clients all parse success and failure states identically, eliminating integration bugs from inconsistent error shapes.

Where should I add error handling for new tools in this codebase?

Wrap your tool's main operation in try/catch, call capture('your_tool_error', { error: e }) for telemetry, and return createErrorResponse(message) on failure. Follow the pattern in src/tools/filesystem.ts for production-ready implementation.

Does the DEBUG flag affect error reporting behavior?

Yes. When DEBUG=1, logger.error() and related methods write to console. However, raw error objects are never printed—only safe, stringified messages—to maintain privacy even in development environments.

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 →