# Process Lifecycle for start_process, interact_with_process, and force_terminate in Desktop Commander MCP

> Understand the Desktop Commander MCP process lifecycle. Learn how start_process, interact_with_process, and force_terminate manage shell sessions and execute commands efficiently.

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

---

**The process lifecycle in Desktop Commander MCP follows a three-stage state machine: `start_process` creates a persistent shell session and returns a PID, `interact_with_process` sends input and polls for output until a prompt or completion is detected, and `force_terminate` ends the session with SIGINT followed by SIGKILL.**

Desktop Commander MCP manages long-lived shell and REPL sessions that persist across tool invocations, enabling interactive workflows like Python data analysis or multi-step DevOps automation. Understanding the process lifecycle for the `start_process`, `interact_with_process`, and `force_terminate` tools is essential for building reliable MCP server integrations that maintain state across multiple AI agent turns.

## The Three-Stage Process Lifecycle

The lifecycle operates as a managed state machine within the **TerminalManager** class, which stores active sessions in a `Map<number, TerminalSession>` and tracks completed sessions separately for pagination.

### Stage 1: Creating Sessions with start_process

The `start_process` tool in [`src/tools/improved-process-tools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/improved-process-tools.ts) spawns a new shell or REPL and prepares it for interaction.

The implementation follows these steps:

1. **Argument validation** – `StartProcessArgsSchema` parses `{ command, timeout_ms?, shell?, verbose_timing? }`.
2. **Shell selection** – If `shell` is omitted, the default is retrieved from `configManager.getConfig()` or falls back to `cmd.exe` on Windows and `/bin/sh` on Unix. See the shell-selection logic in [`src/terminal-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/terminal-manager.ts) (lines 77-89).
3. **Process spawn** – `TerminalManager.executeCommand` builds a platform-specific spawn configuration using `getShellSpawnArgs` and calls `spawn`.
4. **Early-output buffering** – While the child runs, the wait buffer (`MAX_WAIT_OUTPUT_CHARS`) accumulates the first chunk of output.
5. **Prompt detection** – The system uses `quickPromptPatterns` regex and `analyzeProcessState` heuristics to determine if the process is blocked waiting for input. If a prompt appears before timeout, the tool resolves with `{ pid, output, isBlocked: true }`.
6. **Return** – The tool returns a message containing the PID, initial output, and status emoji (`🔄` if waiting, `✅` if finished, `⏳` otherwise).

The response construction in [`src/tools/improved-process-tools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/improved-process-tools.ts) (lines 102-108) formats the result:

```typescript
return {
  content: [{
    type: "text",
    text: `Process started with PID ${result.pid} (shell: ${shellUsed})\nInitial output:\n${result.output}${statusMessage}${timingMessage}`
  }],
};

```

### Stage 2: Interacting with Running Sessions

Once a session exists, `interact_with_process` sends commands and captures output without restarting the shell, preserving environment variables and working directory state.

The interaction flow in [`src/terminal-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/terminal-manager.ts) and [`src/tools/improved-process-tools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/improved-process-tools.ts) works as follows:

1. **Snapshot** – `terminalManager.captureOutputSnapshot(pid)` records the current character and line count before sending input.
2. **Send input** – `terminalManager.sendInputToProcess(pid, input)` appends a newline (if missing) and writes to the child’s stdin.
3. **Waiting loop** – Every 50ms, the system fetches new output since the snapshot via `getOutputSinceSnapshot`. It checks `quickPromptPatterns` or runs `analyzeProcessState` to detect if the process is waiting for input or has finished. The loop exits after `maxAttempts = timeout_ms / pollIntervalMs` iterations.
4. **Output cleaning** – `cleanProcessOutput` strips echoed input and trims whitespace. If output exceeds `config.fileReadLineLimit`, it truncates with a warning.
5. **Result** – Returns a status emoji (✅ or 🔄) with state message, cleaned output, and optional timing telemetry.

The final response construction (lines 640-645 in [`improved-process-tools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/improved-process-tools.ts)) returns:

```typescript
return {
  content: [{
    type: "text",
    text: responseText
  }],
};

```

### Stage 3: Terminating Sessions with force_terminate

The `force_terminate` tool ends sessions reliably using a two-stage kill sequence.

The termination logic in [`src/terminal-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/terminal-manager.ts) (lines 172-182) handles two cases:

- **Virtual Node sessions** – For `node:local` fallbacks (negative PIDs), the entry is simply deleted from the sessions map.
- **Real processes** – The system sends `SIGINT`, waits 1 second, then sends `SIGKILL` if the process remains in `this.sessions`.

```typescript
session.process.kill('SIGINT');
setTimeout(() => {
  if (this.sessions.has(pid)) {
    session.process.kill('SIGKILL');
  }
}, 1000);

```

## How the Tools Work Together

The three tools form a cohesive workflow for persistent shell management:

1. **Create** – `start_process` returns **PID 1234** and stores the session in TerminalManager’s active sessions map.
2. **Interact** – `interact_with_process` sends commands like `ls -l\n`, waits for prompt detection, and updates `lastReadIndex` automatically.
3. **Read More** – `read_process_output` (optional) fetches additional lines using offset-based pagination without sending new input.
4. **Finish** – `force_terminate` clears the session from memory and kills the underlying process.

Because `TerminalSession` objects persist in memory, environment state (variables, current directory, loaded modules) survives across multiple `interact_with_process` calls, enabling true REPL-style workflows.

## Practical Usage Examples

The following examples demonstrate the complete lifecycle using MCP tool invocations:

```javascript
// 1. Start a Python REPL session
const start = await callTool('start_process', {
  command: 'python3 -i',
  timeout_ms: 12000,
  origin: 'ui'
});
// Returns: "Process started with PID 5678 ... 🔄 ..."

// 2. Execute Python code and wait for the REPL prompt
const interact = await callTool('interact_with_process', {
  pid: 5678,
  input: 'import pandas as pd; df = pd.read_csv("/abs/data.csv")\n',
  timeout_ms: 8000,
  wait_for_prompt: true,
  verbose_timing: false
});
// Returns: "✅ Input executed in process 5678:\n\n📤 Output:\n[...]"

// 3. Retrieve additional output after long-running operations
const more = await callTool('read_process_output', {
  pid: 5678,
  offset: 0,
  length: 200,
  timeout_ms: 3000
});
// Returns: "[Reading 42 new lines ...]"

// 4. Clean up when finished
await callTool('force_terminate', { pid: 5678 });
// Returns: "Successfully initiated termination of session 5678"

```

## Key Source Files and Implementation

| File | Purpose | Key Functions |
|------|---------|---------------|
| [`src/tools/improved-process-tools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/improved-process-tools.ts) | Public tool implementations | `startProcess`, `interactWithProcess`, `forceTerminate` |
| [`src/terminal-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/terminal-manager.ts) | Core session management | `executeCommand`, `sendInputToProcess`, `captureOutputSnapshot`, `getOutputSinceSnapshot`, `forceTerminate` |
| [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts) | Zod validation schemas | `StartProcessArgsSchema`, `InteractWithProcessArgsSchema`, `ForceTerminateArgsSchema` |
| [`src/utils/process-detection.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/process-detection.ts) | State heuristics | `analyzeProcessState`, `quickPromptPatterns` |
| [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) | Configuration defaults | `getConfig()`, default shell resolution |

## Summary

- **`start_process`** creates persistent shell sessions using `TerminalManager.executeCommand`, detects initial prompts via `analyzeProcessState`, and returns a stable PID for future interactions.
- **`interact_with_process`** manages the running state by capturing output snapshots, sending stdin input, and polling every 50ms until detecting a prompt or completion, preserving all shell state between calls.
- **`force_terminate`** implements reliable cleanup with a graceful SIGINT followed by SIGKILL after 1 second, or simple deletion for virtual `node:local` sessions.
- The **TerminalManager** maintains all state in memory using `Map<number, TerminalSession>`, enabling long-running REPL workflows that survive across multiple MCP tool invocations.

## Frequently Asked Questions

### How does prompt detection work in Desktop Commander MCP?

Prompt detection uses a combination of quick regex patterns (`quickPromptPatterns`) and deeper process state analysis (`analyzeProcessState` in [`src/utils/process-detection.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/process-detection.ts)). The system checks output every 50ms during the waiting loop to determine if the process is blocked awaiting input, has finished execution, or is still processing.

### What happens if a process hangs during interaction?

If `interact_with_process` exceeds the `timeout_ms` limit (calculated as `maxAttempts = timeout_ms / pollIntervalMs`), the tool returns with a timeout status. The session remains active in the TerminalManager, allowing subsequent interaction attempts or `force_terminate` to recover or kill the process.

### Can I run multiple shell processes simultaneously?

Yes. Each call to `start_process` creates a distinct `TerminalSession` stored in the `sessions` Map with a unique PID. You can interact with multiple PIDs concurrently, though each interaction is synchronous and blocks until the prompt is detected or timeout occurs.

### How is output buffering handled across interactions?

The `captureOutputSnapshot` method records the current character count before sending input, and `getOutputSinceSnapshot` retrieves only new output generated after that point. This incremental approach prevents duplicate data while respecting the `fileReadLineLimit` configuration for large outputs.