How Desktop Commander MCP Process Management Works: Listing and Killing OS Processes

Desktop Commander MCP exposes two tools—list_processes and kill_process—that enable enumeration of running system processes and termination by PID through a schema-validated MCP protocol layer.

The wonderwhy-er/DesktopCommanderMCP repository implements process management functionality as part of its terminal skill set, allowing AI clients to inspect and control operating system processes via the Model Context Protocol (MCP). This capability relies on Node.js native APIs and platform-specific shell commands wrapped in type-safe handlers.

Architecture and Tool Registration

The MCP server registers both tools in src/server.ts (lines 1095–1468) within the central tool table. The registration maps tool names to their argument schemas and handler functions from the @modelcontextprotocol/sdk.

  • list_processes accepts no arguments, defined by ListProcessesArgsSchema as an empty Zod object
  • kill_process requires a numeric pid parameter, enforced by KillProcessArgsSchema

When a client invokes a tool, the server's dispatcher routes the request to the appropriate handler in src/handlers/process-handlers.ts. This thin wrapper layer validates inputs before delegating to the core implementation:

// src/handlers/process-handlers.ts
export async function handleListProcesses(): Promise<ServerResult> {
    return listProcesses();
}

export async function handleKillProcess(args: unknown): Promise<ServerResult> {
    const parsed = KillProcessArgsSchema.parse(args);
    return killProcess(parsed);
}

Listing Processes with Platform-Specific Commands

The listProcesses() function in src/tools/process.ts executes OS-specific commands to retrieve process information. It uses a promisified exec to run tasklist on Windows and ps aux on macOS/Linux, then parses the text output into structured data.

Command Execution and Parsing

The implementation detects the platform via os.platform(), executes the appropriate command, and transforms the raw stdout into an array of ProcessInfo objects:

// src/tools/process.ts
export async function listProcesses(): Promise<ServerResult> {
    const command = os.platform() === 'win32' ? 'tasklist' : 'ps aux';
    const { stdout } = await execAsync(command);
    const processes = stdout.split('\n')
      .slice(1)
      .filter(Boolean)
      .map(line => {
          const parts = line.split(/\s+/);
          return {
              pid: parseInt(parts[1]),
              command: parts[parts.length - 1],
              cpu: parts[2],
              memory: parts[3],
          } as ProcessInfo;
      });
    return { content: [{ type: "text", text: processes.map(p =>
            `PID: ${p.pid}, Command: ${p.command}, CPU: ${p.cpu}, Memory: ${p.memory}`
          ).join('\n') }] };
}

The function strips header lines, splits entries by whitespace, and extracts the PID, command name, CPU usage, and memory consumption. Results return as plain-text content within a ServerResult payload for direct display to users.

Terminating Processes with Node.js Native APIs

The killProcess() function handles process termination through Node's built-in process.kill() method. Before execution, it validates arguments using KillProcessArgsSchema defined in src/tools/schemas.ts (lines 51–53), which requires a numeric pid field.

Validation and Error Handling

The handler uses safe parsing to distinguish between validation failures and runtime execution errors, returning appropriate isError flags and descriptive messages:

// src/tools/process.ts
export async function killProcess(args: unknown): Promise<ServerResult> {
    const parsed = KillProcessArgsSchema.safeParse(args);
    if (!parsed.success) {
        return { content: [{ type: "text",
            text: `Error: Invalid arguments for kill_process: ${parsed.error}` }],
            isError: true };
    }
    try {
        process.kill(parsed.data.pid);
        return { content: [{ type: "text",
            text: `Successfully terminated process ${parsed.data.pid}` }] };
    } catch (error) {
        return { content: [{ type: "text",
            text: `Error: Failed to kill process: ${error instanceof Error ? error.message : String(error)}` }],
            isError: true };
    }
}

If the PID does not exist or the server lacks permissions, the catch block returns the system error message (e.g., EPERM: operation not permitted) wrapped in the MCP error format.

Schema Definitions and Type Safety

Argument schemas in src/tools/schemas.ts enforce type safety at runtime using Zod:

  • ListProcessesArgsSchema = z.object({}) accepts an empty object
  • KillProcessArgsSchema = z.object({ pid: z.number() }) requires a numeric process identifier

These schemas prevent malformed requests from reaching the OS-level execution layer, ensuring that kill_process only receives valid PIDs before invoking system calls.

Example Request Flow

A client interaction flows from the LLM through the MCP protocol to the OS and back:

Listing all processes:

{
  "toolName": "list_processes",
  "args": {}
}

Sample response:


PID: 1245, Command: node, CPU: 0.0, Memory: 42.1
PID: 2310, Command: python3, CPU: 1.2, Memory: 150.3

Killing a specific process:

{
  "toolName": "kill_process",
  "args": { "pid": 2310 }
}

Success response: Successfully terminated process 2310

Permission denied response: Error: Failed to kill process: EPERM: operation not permitted, kill 2310

Summary

  • Tool Registration: src/server.ts registers list_processes and kill_process with the MCP SDK, binding them to schemas and handlers in src/handlers/process-handlers.ts.
  • Process Enumeration: src/tools/process.ts executes platform-specific commands (tasklist or ps aux) and parses output into readable text format.
  • Process Termination: Uses Node.js process.kill() with Zod validation via KillProcessArgsSchema to safely terminate processes by PID.
  • Error Handling: Distinguishes between validation errors (malformed arguments) and runtime errors (permissions, missing PIDs) using the isError flag in ServerResult objects.
  • Type Safety: Zod schemas in src/tools/schemas.ts enforce strict typing before OS-level execution.

Frequently Asked Questions

What permissions are required to kill processes with Desktop Commander MCP?

The MCP server process must run with sufficient privileges to terminate the target process. Killing system processes or processes owned by other users typically requires administrator/root access; otherwise, the kill_process tool returns an EPERM (permission denied) error.

How does the process list differ between Windows and Unix systems?

On Windows, the tool executes tasklist and parses its columnar output. On macOS and Linux, it runs ps aux. While both return PID, command name, CPU, and memory usage, the exact formatting and available metrics differ slightly based on the underlying OS command output structure.

Can I filter processes by name or CPU usage?

The current implementation in src/tools/process.ts returns the complete process list without server-side filtering. Clients receive the full text output and must perform any filtering or searching on the client side after receiving the response from the list_processes tool.

What happens if I attempt to kill a non-existent PID?

If the specified PID does not exist, Node's process.kill() throws an error that the handler catches and returns as a ServerResult with isError: true. The error message indicates that the process was not found, allowing the client to handle the failure gracefully.

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 →