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

You can extend Desktop Commander by defining Zod schemas in src/tools/schemas.ts, implementing handlers in src/handlers/, and registering tools in the allTools array in src/server.ts, then pointing your MCP client to the custom server via .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 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) 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. Create a new schema that describes your tool's input parameters, including types, defaults, and validation rules.

// 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.

// 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 (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 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.

// 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, add a request handler that maps the tool name to your implementation:

// 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. This file tells the MCP client where to reach the server endpoint, whether via local stdio, TCP, or HTTP.

// .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 – Core MCP server creation, capability definitions, tool registration in allTools, and request handler wiring.
  • plugin.yaml – Plugin manifest declaring the server name, description, and transport configuration for MCP discovery.
  • 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 – Custom stdio transport wrapper that formats console output as JSON-RPC messages for UI integration.
  • 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, exposing tools through a structured plugin architecture defined in plugin.yaml.
  • To add custom tools, define a Zod schema in src/tools/schemas.ts, implement a handler function in src/handlers/, and register the tool in the allTools array in 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, which supports stdio, TCP, or HTTP transports to local or remote custom servers.
  • The capture utility in 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. 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 and the plugin manifest (plugin.yaml lines 54-61), Desktop Commander primarily uses stdio transport for local communication. However, the MCP client configuration in .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 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 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 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.

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 →