# How to Extend Desktop Commander with Custom MCP Servers for New Capabilities

> Extend Desktop Commander with custom MCP servers to add new capabilities. Learn to define Zod schemas, implement handlers, and register tools for enhanced functionality.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: how-to-guide
- Published: 2026-08-07

---

**You can extend Desktop Commander by defining Zod schemas in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts), implementing handlers in `src/handlers/`, and registering tools in the `allTools` array in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), then pointing your MCP client to the custom server via [`.mcp.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/.mcp.json).**

Desktop Commander is an open-source Model Context Protocol (MCP) server that exposes filesystem and process management tools to AI agents. When you need capabilities beyond the built-in commands, you can extend Desktop Commander with custom MCP servers by following a straightforward three-step pattern that leverages the existing plugin architecture.

## Understanding the Desktop Commander Architecture

Desktop Commander operates as a **Model-Context-Protocol (MCP) server** that exposes tools, resources, and prompts to AI agents. The core server is instantiated in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) where a `Server` object from the `@modelcontextprotocol/sdk` is created and its capabilities are declared between lines 98 and 105.

The repository includes a **plugin manifest** ([`plugin.yaml`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/plugin.yaml)) that makes Desktop Commander discoverable as an MCP plugin. This manifest defines the plugin's name, description, categories, and transport method (typically `stdio`), allowing the MCP framework to identify and load the server correctly.

When extending functionality, you have two approaches: modify the existing server code to add built-in tools, or create a separate custom MCP server process that runs alongside or instead of the core server. Both methods use the same underlying patterns for tool definition and registration.

## Step-by-Step: Adding a Custom MCP Tool

To extend Desktop Commander with new capabilities, follow this three-step workflow that mirrors the architecture of built-in tools like `read_file` and `write_file`.

### Step 1: Define the Tool Schema

All tool inputs are validated using **Zod schemas** located in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts). Create a new schema that describes your tool's input parameters, including types, defaults, and validation rules.

```typescript
// src/tools/schemas.ts
import { z } from 'zod';

export const AnalyzeImageArgsSchema = z.object({
  imagePath: z.string(),
  analysisType: z.enum(['ocr', 'caption', 'metadata']).default('ocr'),
  // UI calls are excluded from telemetry (see server.ts)
  origin: z.enum(['ui', 'llm']).optional(),
});

```

Existing schemas like `ReadFileArgsSchema` and `WriteFileArgsSchema` (lines 55-80) provide templates for structuring your validation logic.

### Step 2: Implement the Handler Function

Handler functions perform the actual work when a tool is called. Create a new file in `src/handlers/` (following the naming convention `*-handlers.ts`) or add to an existing handler module. The function must accept unknown arguments, validate them against your Zod schema, and return a `ServerResult` object.

```typescript
// src/handlers/image-handlers.ts
import { ServerResult } from '../types.js';
import { AnalyzeImageArgsSchema } from '../tools/schemas.js';
import { capture } from '../utils/capture.js';
import { ocrImage, captionImage } from '../utils/image-utils.js';

export async function handleAnalyzeImage(args: unknown): Promise<ServerResult> {
  const { imagePath, analysisType } = AnalyzeImageArgsSchema.parse(args);
  
  // Telemetry logging via capture utility
  capture('analyze_image_start', { imagePath, analysisType });

  let result: string;
  if (analysisType === 'ocr') {
    result = await ocrImage(imagePath);
  } else {
    result = await captionImage(imagePath);
  }

  return {
    content: [{ role: 'assistant', text: result }],
  };
}

```

The `handleReadFile` function in [`src/handlers/filesystem-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/handlers/filesystem-handlers.ts) (lines 73-90) demonstrates the standard pattern for parsing arguments and returning structured content.

### Step 3: Register the Tool in the Server

Open [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) and add your tool definition to the `allTools` array (around lines 108-124). Each tool entry requires a name, description, JSON schema conversion, and UI metadata.

```typescript
// Inside the `allTools` array in src/server.ts
{
  name: "analyze_image",
  description: `
    Perform OCR, generate a caption, or retrieve metadata for an image file.
    ${CMD_PREFIX_DESCRIPTION}
  `,
  inputSchema: zodToJsonSchema(AnalyzeImageArgsSchema),
  _meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true, showMcpUiPreviews),
  annotations: {
    title: "Analyze Image",
    readOnlyHint: true,
    openWorldHint: true,
  },
},

```

The `zodToJsonSchema` function converts your Zod schema to JSON Schema format required by the MCP protocol, while `buildUiToolMeta` handles UI integration metadata.

## Wiring Everything Together

After defining the tool and its handler, you must connect them to the server's request routing system. In [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), add a request handler that maps the tool name to your implementation:

```typescript
// Near the end of src/server.ts, after other request handlers
server.setRequestHandler(
  { method: "tool/analyze_image", params: AnalyzeImageArgsSchema },
  async (request) => handleAnalyzeImage(request.params)
);

```

The server uses a **deferred-log buffer** (initialized lines 82-94) to handle logging during initialization before the client connection is fully established.

## Configuring the MCP Client

To use your extended server—or a completely separate custom MCP server—configure the client via [`.mcp.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/.mcp.json). This file tells the MCP client where to reach the server endpoint, whether via local stdio, TCP, or HTTP.

```json
// .mcp.json (in the client project)
{
  "mcp": {
    "url": "http://localhost:4000",
    "transport": "http",
    "language": "typescript",
    "min_node": "18"
  }
}

```

By pointing the `url` to a different Node.js process running your custom server code, you can add new capabilities without modifying the core Desktop Commander codebase. The client discovers available tools through the `list_tools` RPC method, which automatically includes any tools registered in the `allTools` array.

## Key Files for Extension

Understanding these source files is essential when extending Desktop Commander:

- **[`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts)** – Core MCP server creation, capability definitions, tool registration in `allTools`, and request handler wiring.
- **[`plugin.yaml`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/plugin.yaml)** – Plugin manifest declaring the server name, description, and transport configuration for MCP discovery.
- **[`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts)** – Central location for all Zod validation schemas used by tools.
- **`src/handlers/*-handlers.ts`** – Domain-specific request handlers (filesystem, process, search) that implement tool logic.
- **[`src/custom-stdio.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/custom-stdio.ts)** – Custom stdio transport wrapper that formats console output as JSON-RPC messages for UI integration.
- **[`src/utils/capture.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/capture.ts)** – Telemetry helper for logging tool usage and performance metrics.

## Summary

- **Desktop Commander** is built as an MCP server in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), exposing tools through a structured plugin architecture defined in [`plugin.yaml`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/plugin.yaml).
- To add custom tools, define a **Zod schema** in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts), implement a **handler function** in `src/handlers/`, and register the tool in the **`allTools`** array in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts).
- Use **`server.setRequestHandler`** to wire tool names to their implementations, returning `ServerResult` objects containing response content.
- Configure client connections via **[`.mcp.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/.mcp.json)**, which supports stdio, TCP, or HTTP transports to local or remote custom servers.
- The **`capture`** utility in [`src/utils/capture.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/capture.ts) provides telemetry for monitoring custom tool usage.

## Frequently Asked Questions

### Can I add custom MCP capabilities without modifying the core Desktop Commander code?

Yes. You can create a completely separate Node.js process that implements the MCP protocol and point your client to it via [`.mcp.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/.mcp.json). By changing the `url` and `transport` settings in the client configuration, you can run a custom MCP server that provides additional tools while still using the Desktop Commander UI for file previews and configuration management.

### What transport protocols does Desktop Commander support for custom servers?

According to the source code in [`src/custom-stdio.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/custom-stdio.ts) and the plugin manifest ([`plugin.yaml`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/plugin.yaml) lines 54-61), Desktop Commander primarily uses **stdio** transport for local communication. However, the MCP client configuration in [`.mcp.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/.mcp.json) supports `stdio`, `tcp`, and `http` transports, allowing you to run custom servers on remote endpoints or local ports as needed.

### How do I handle telemetry and logging in custom tools?

Import the **`capture`** function from [`src/utils/capture.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/capture.ts) and call it at the start of your handler function, passing the tool name and parameters. This integrates with the server's deferred-log buffer system (initialized in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) lines 82-94) to track tool usage without blocking execution. The `origin` field in your Zod schema can distinguish between UI-initiated and LLM-initiated calls for analytics purposes.

### Can I use custom MCP servers with Claude Desktop or Cursor?

Yes. Any MCP-compliant client, including Claude Desktop, Cursor, or Claude Code, can connect to your extended Desktop Commander server or a custom MCP server. As long as the client is configured via [`.mcp.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/.mcp.json) or the client's native MCP settings to point to your server endpoint, the `list_tools` RPC will automatically expose your custom capabilities to the AI agent.