read_process_output
Read output from running processes with pagination controls to monitor execution results, detect completion states, and manage verbose output efficiently.
Instructions
Read output from a running process with file-like pagination support.
Supports partial output reading with offset and length parameters (like read_file):
- 'offset' (start line, default: 0)
* offset=0: Read NEW output since last read (default, like old behavior)
* Positive: Read from absolute line position
* Negative: Read last N lines from end (tail behavior)
- 'length' (max lines to read, default: configurable via 'fileReadLineLimit' setting)
Examples:
- offset: 0, length: 100 → First 100 NEW lines since last read
- offset: 0 → All new lines (respects config limit)
- offset: 500, length: 50 → Lines 500-549 (absolute position)
- offset: -20 → Last 20 lines (tail)
- offset: -50, length: 10 → Start 50 from end, read 10 lines
OUTPUT PROTECTION:
- Uses same fileReadLineLimit as read_file (default: 1000 lines)
- Returns status like: [Reading 100 lines from line 0 (total: 5000 lines, 4900 remaining)]
- Prevents context overflow from verbose processes
SMART FEATURES:
- For offset=0, waits up to timeout_ms for new output to arrive
- Detects REPL prompts and process completion
- Shows process state (waiting for input, finished, etc.)
DETECTION STATES:
Process waiting for input (ready for interact_with_process)
Process finished execution
Timeout reached (may still be running)
This command can be referenced as "DC: ..." or "use Desktop Commander to ..." in your instructions.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| pid | Yes | ||
| timeout_ms | No | ||
| offset | No | ||
| length | No | ||
| verbose_timing | No |
Implementation Reference
- Core handler function that parses arguments, reads paginated output from process via terminalManager.readOutputPaginated(pid, offset, length), handles waiting for new output, adds status messages, process state detection, and timing info.export async function readProcessOutput(args: unknown): Promise<ServerResult> { const parsed = ReadProcessOutputArgsSchema.safeParse(args); if (!parsed.success) { return { content: [{ type: "text", text: `Error: Invalid arguments for read_process_output: ${parsed.error}` }], isError: true, }; } // Get default line limit from config const config = await configManager.getConfig(); const defaultLength = config.fileReadLineLimit ?? 1000; const { pid, timeout_ms = 5000, offset = 0, // 0 = from last read, positive = absolute, negative = tail length = defaultLength, // Default from config, same as file reading verbose_timing = false } = parsed.data; // Timing telemetry const startTime = Date.now(); // For active sessions with no new output yet, optionally wait for output const session = terminalManager.getSession(pid); if (session && offset === 0) { // Wait for new output to arrive (only for "new output" reads, not absolute/tail) const waitForOutput = (): Promise<void> => { return new Promise((resolve) => { // Check if there's already new output const currentLines = terminalManager.getOutputLineCount(pid) || 0; if (currentLines > session.lastReadIndex) { resolve(); return; } let resolved = false; let interval: NodeJS.Timeout | null = null; let timeout: NodeJS.Timeout | null = null; const cleanup = () => { if (interval) clearInterval(interval); if (timeout) clearTimeout(timeout); }; const resolveOnce = () => { if (resolved) return; resolved = true; cleanup(); resolve(); }; // Poll for new output interval = setInterval(() => { const newLineCount = terminalManager.getOutputLineCount(pid) || 0; if (newLineCount > session.lastReadIndex) { resolveOnce(); } }, 50); // Timeout timeout = setTimeout(() => { resolveOnce(); }, timeout_ms); }); }; await waitForOutput(); } // Read output with pagination const result = terminalManager.readOutputPaginated(pid, offset, length); if (!result) { return { content: [{ type: "text", text: `No session found for PID ${pid}` }], isError: true, }; } // Join lines back into string const output = result.lines.join('\n'); // Generate status message similar to file reading let statusMessage = ''; if (offset < 0) { // Tail read - match file reading format for consistency statusMessage = `[Reading last ${result.readCount} lines (total: ${result.totalLines} lines)]`; } else if (offset === 0) { // "New output" read if (result.remaining > 0) { statusMessage = `[Reading ${result.readCount} new lines from line ${result.readFrom} (total: ${result.totalLines} lines, ${result.remaining} remaining)]`; } else { statusMessage = `[Reading ${result.readCount} new lines (total: ${result.totalLines} lines)]`; } } else { // Absolute position read statusMessage = `[Reading ${result.readCount} lines from line ${result.readFrom} (total: ${result.totalLines} lines, ${result.remaining} remaining)]`; } // Add process state info let processStateMessage = ''; if (result.isComplete) { const runtimeStr = result.runtimeMs !== undefined ? ` (runtime: ${(result.runtimeMs / 1000).toFixed(2)}s)` : ''; processStateMessage = `\n✅ Process completed with exit code ${result.exitCode}${runtimeStr}`; } else if (session) { // Analyze state for running processes const fullOutput = session.outputLines.join('\n'); const processState = analyzeProcessState(fullOutput, pid); if (processState.isWaitingForInput) { processStateMessage = `\n🔄 ${formatProcessStateMessage(processState, pid)}`; } } // Add timing information if requested let timingMessage = ''; if (verbose_timing) { const endTime = Date.now(); timingMessage = `\n\n📊 Timing: ${endTime - startTime}ms`; } const responseText = output || '(No output in requested range)'; return { content: [{ type: "text", text: `${statusMessage}\n\n${responseText}${processStateMessage}${timingMessage}` }], }; }
- src/tools/schemas.ts:28-34 (schema)Zod schema defining input parameters for read_process_output: pid (required), timeout_ms, offset, length, verbose_timing.export const ReadProcessOutputArgsSchema = z.object({ pid: z.number(), timeout_ms: z.number().optional(), offset: z.number().optional(), // Line offset: 0=from last read, positive=absolute, negative=tail length: z.number().optional(), // Max lines to return (default from config.fileReadLineLimit) verbose_timing: z.boolean().optional(), });
- src/server.ts:801-840 (registration)Tool registration in list_tools handler: defines name, description, inputSchema from ReadProcessOutputArgsSchema, and annotations.name: "read_process_output", description: ` Read output from a running process with file-like pagination support. Supports partial output reading with offset and length parameters (like read_file): - 'offset' (start line, default: 0) * offset=0: Read NEW output since last read (default, like old behavior) * Positive: Read from absolute line position * Negative: Read last N lines from end (tail behavior) - 'length' (max lines to read, default: configurable via 'fileReadLineLimit' setting) Examples: - offset: 0, length: 100 → First 100 NEW lines since last read - offset: 0 → All new lines (respects config limit) - offset: 500, length: 50 → Lines 500-549 (absolute position) - offset: -20 → Last 20 lines (tail) - offset: -50, length: 10 → Start 50 from end, read 10 lines OUTPUT PROTECTION: - Uses same fileReadLineLimit as read_file (default: 1000 lines) - Returns status like: [Reading 100 lines from line 0 (total: 5000 lines, 4900 remaining)] - Prevents context overflow from verbose processes SMART FEATURES: - For offset=0, waits up to timeout_ms for new output to arrive - Detects REPL prompts and process completion - Shows process state (waiting for input, finished, etc.) DETECTION STATES: Process waiting for input (ready for interact_with_process) Process finished execution Timeout reached (may still be running) ${CMD_PREFIX_DESCRIPTION}`, inputSchema: zodToJsonSchema(ReadProcessOutputArgsSchema), annotations: { title: "Read Process Output", readOnlyHint: true, }, },
- src/server.ts:1246-1248 (registration)Dispatch in call_tool handler: routes 'read_process_output' calls to handlers.handleReadProcessOutputcase "read_process_output": result = await handlers.handleReadProcessOutput(args); break;
- src/handlers/terminal-handlers.ts:27-33 (handler)Thin wrapper handler that parses args with schema and delegates to readProcessOutput from tools./** * Handle read_process_output command (improved read_output) */ export async function handleReadProcessOutput(args: unknown): Promise<ServerResult> { const parsed = ReadProcessOutputArgsSchema.parse(args); return readProcessOutput(parsed); }