# How Desktop Commander MCP Handles Tool Registration and Routing

> Discover how Desktop Commander MCP handles tool registration and routing using a declarative JSON-RPC pipeline. Learn about Zod schemas and argument validation for efficient tool dispatch.

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

---

**Desktop Commander MCP implements a declarative JSON-RPC pipeline where tools are registered in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) as a master `allTools` array with Zod schemas, then routed through a `CallToolRequestSchema` handler that validates arguments and dispatches to concrete implementations in `src/tools/`.**

Desktop Commander MCP is a Model Context Protocol server that exposes filesystem and process management capabilities to AI clients. Understanding how it handles **tool registration and routing** reveals the architecture behind its robust JSON-RPC interface. This guide examines the actual source code from `wonderwhy-er/DesktopCommanderMCP` to explain the three-stage lifecycle from schema definition to execution.

## Tool Registration and Schema Definition

### Defining Tools with Zod Schemas

Every tool in Desktop Commander MCP starts with a strict **Zod schema** that defines its arguments. These schemas live in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts) and provide runtime validation alongside TypeScript type safety. For example, a directory listing tool defines its parameters as follows:

```typescript
// src/tools/schemas.ts
export const ListDirectoryArgsSchema = z.object({
  path: z.string(),
  depth: z.number().int().min(1).max(5).optional(),
});

```

The server converts these Zod schemas to JSON-Schema using `zodToJsonSchema` when exposing tools to clients. This ensures that any MCP-compatible client can understand the expected parameters without executing TypeScript.

### Building the Master Tool Registry

When the server initializes, it constructs an **`allTools`** array in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (approximately lines 3005–3120) that aggregates every available tool. Each entry includes the tool name, description, input schema, UI metadata, and annotations:

```typescript
// src/server.ts
{
  name: "list_directory",
  description: `Get a detailed listing of all files and directories in a specified path…`,
  inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
  _meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true, showMcpUiPreviews),
  annotations: { title: "List Directory Contents", readOnlyHint: true },
},

```

The server exposes this registry through the **`list_tools`** JSON-RPC method. The list is dynamically filtered based on the current client context—for instance, the Desktop Commander app hides meta-tools from the standard view while keeping them available for internal use.

## JSON-RPC Routing and Request Handling

### Handling the tools/call Method

Incoming execution requests arrive via the **`tools/call`** JSON-RPC method. The server registers a handler for `CallToolRequestSchema` in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (approximately lines 1500–1700) that extracts the tool `name` and `arguments` from the request params:

```typescript
// src/server.ts
server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const { name, arguments: args } = req.params;
  await trackToolCall(name, args);                // Logging
  setCurrentCallIsRemote(!!req._meta?.remote);    // Context tracking
  
  // Dispatch to concrete implementation
  switch (name) {
    case "read_file":   return server_read_file(args);
    case "write_file":  return server_write_file(args);
    case "list_directory": return server_list_directory(args);
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

```

The handler validates the payload against the tool's Zod schema before dispatching. This centralizes error handling and ensures that only type-safe arguments reach the implementation functions.

### Remote Context and Call Tracking

Before execution, the router captures telemetry through **`trackToolCall`** in [`src/utils/trackTools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/trackTools.ts). This function persists a log line for every invocation, enabling audit trails and usage analytics. The handler also determines execution context via **`setCurrentCallIsRemote`**, which checks `req._meta?.remote` to distinguish between local UI calls and remote device requests.

## Concrete Tool Implementations

Each tool's business logic resides in modular files under `src/tools/`. For example, [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts) exports `server_read_file` and `server_write_file`, while [`src/tools/improved-process-tools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/improved-process-tools.ts) handles `server_start_process` and `interact_with_process`.

These implementation functions receive validated arguments and return results that the JSON-RPC handler wraps into the response payload. Error handling occurs through `capture_call_tool` in [`src/utils/capture.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/capture.ts), which centralizes telemetry emission and error reporting without polluting the business logic.

## UI Fallback Bridge

When the primary MCP host cannot execute a tool directly, Desktop Commander MCP provides a **JSON-RPC bridge** in [`src/ui/shared/tool-bridge.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/shared/tool-bridge.ts) (lines 69–95). This fallback mechanism posts a `tools/call` message to the parent frame and awaits the response:

```typescript
// src/ui/shared/tool-bridge.ts
export function createToolBridge(options = {}) {
  const requestId = generateId();
  
  parent.postMessage(
    { 
      jsonrpc: "2.0", 
      id: requestId, 
      method: "tools/call", 
      params: { name, arguments: args } 
    },
    targetOrigin
  );
  
  // Wait for response or timeout...
}

```

This bridge ensures that tools remain accessible even when running in sandboxed UI contexts or across iframe boundaries.

## Summary

- **Schema-first registration**: Tools are defined via Zod schemas in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts) and registered in the `allTools` array within [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), exposing them through the `list_tools` JSON-RPC method.
- **Centralized routing**: The `CallToolRequestSchema` handler in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (lines 1500–1700) validates incoming `tools/call` requests and dispatches to concrete implementations based on the tool name.
- **Execution context**: Each call is logged via `trackToolCall` and tagged with remote status through `setCurrentCallIsRemote` before reaching the implementation in `src/tools/` modules.
- **Fallback support**: The [`src/ui/shared/tool-bridge.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/shared/tool-bridge.ts) provides a JSON-RPC bridge for environments where direct tool execution is unavailable.

## Frequently Asked Questions

### How does Desktop Commander MCP validate tool arguments?

The server validates tool arguments using **Zod schemas** defined in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts). When a `tools/call` request arrives, the `CallToolRequestSchema` handler validates the payload against the specific tool's schema before dispatching to the implementation function. This ensures type safety and provides clear error messages for malformed requests.

### What happens if a tool is called that doesn't exist?

If the `CallToolRequestSchema` handler in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) receives a request for an unknown tool name, it throws an `Error` with the message `Unknown tool: ${name}`. This error propagates through the JSON-RPC response, allowing the client to handle the failure gracefully without crashing the server process.

### Where are tool usage statistics stored?

Tool usage statistics are persisted through **`trackToolCall`** in [`src/utils/trackTools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/trackTools.ts), which writes a log line for every invocation. Additionally, historical tool calls are stored in [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts) for recent history display in the UI. These utilities enable both telemetry analysis and debugging capabilities.

### Can tools be called remotely, and how does the server distinguish remote from local calls?

Yes, tools support remote execution. The server distinguishes contexts by checking `req._meta?.remote` in the `CallToolRequestSchema` handler and setting the state via **`setCurrentCallIsRemote`**. This flag allows implementations to adjust behavior—for example, applying stricter security policies or different path resolution rules—when executing commands from remote devices versus the local Desktop Commander UI.