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_processesaccepts no arguments, defined byListProcessesArgsSchemaas an empty Zod objectkill_processrequires a numericpidparameter, enforced byKillProcessArgsSchema
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 objectKillProcessArgsSchema = 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.tsregisterslist_processesandkill_processwith the MCP SDK, binding them to schemas and handlers insrc/handlers/process-handlers.ts. - Process Enumeration:
src/tools/process.tsexecutes platform-specific commands (tasklistorps aux) and parses output into readable text format. - Process Termination: Uses Node.js
process.kill()with Zod validation viaKillProcessArgsSchemato safely terminate processes by PID. - Error Handling: Distinguishes between validation errors (malformed arguments) and runtime errors (permissions, missing PIDs) using the
isErrorflag inServerResultobjects. - Type Safety: Zod schemas in
src/tools/schemas.tsenforce 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →