Skip to main content
Glama

read_process_output

Read output from running processes with intelligent completion detection. Automatically identifies when processes are ready for input or have finished execution, preventing timeouts on interactive prompts.

Instructions

Read output from a running process with intelligent completion detection. Automatically detects when process is ready for more input instead of timing out. SMART FEATURES: - Early exit when REPL shows prompt (>>>, >, etc.) - Detects process completion vs still running - Prevents hanging on interactive prompts - Clear status messages about process state REPL USAGE: - Stops immediately when REPL prompt detected - Shows clear status: waiting for input vs finished - Shorter timeouts needed due to smart detection - Works with Python, Node.js, R, Julia, etc. DETECTION STATES: Process waiting for input (ready for interact_with_process) Process finished execution Timeout reached (may still be running) PERFORMANCE DEBUGGING (verbose_timing parameter): Set verbose_timing: true to get detailed timing information including: - Exit reason (early_exit_quick_pattern, early_exit_periodic_check, process_finished, timeout) - Total duration and time to first output - Complete timeline of all output events with timestamps - Which detection mechanism triggered early exit Use this to identify when timeouts could be reduced or detection patterns improved. This command can be referenced as "DC: ..." or "use Desktop Commander to ..." in your instructions.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pidYes
timeout_msNo
verbose_timingNo

Implementation Reference

  • Core handler function executing the read_process_output tool logic: validates args, reads from terminal session, polls for output with smart prompt detection, analyzes process state, supports verbose timing telemetry.
    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, }; } const { pid, timeout_ms = 5000, verbose_timing = false } = parsed.data; const session = terminalManager.getSession(pid); if (!session) { // Check if this is a completed session const completedOutput = terminalManager.getNewOutput(pid); if (completedOutput) { return { content: [{ type: "text", text: completedOutput }], }; } // Neither active nor completed session found return { content: [{ type: "text", text: `No session found for PID ${pid}` }], isError: true, }; } let output = ""; let timeoutReached = false; let earlyExit = false; let processState: ProcessState | undefined; // Timing telemetry const startTime = Date.now(); let firstOutputTime: number | undefined; let lastOutputTime: number | undefined; const outputEvents: any[] = []; let exitReason: 'early_exit_quick_pattern' | 'early_exit_periodic_check' | 'process_finished' | 'timeout' = 'timeout'; try { const outputPromise: Promise<string> = new Promise<string>((resolve) => { const initialOutput = terminalManager.getNewOutput(pid); if (initialOutput && initialOutput.length > 0) { const now = Date.now(); if (!firstOutputTime) firstOutputTime = now; lastOutputTime = now; if (verbose_timing) { outputEvents.push({ timestamp: now, deltaMs: now - startTime, source: 'initial_poll', length: initialOutput.length, snippet: initialOutput.slice(0, 50).replace(/\n/g, '\\n') }); } // Immediate check on existing output const state = analyzeProcessState(initialOutput, pid); if (state.isWaitingForInput) { earlyExit = true; processState = state; exitReason = 'early_exit_periodic_check'; } resolve(initialOutput); return; } let resolved = false; let interval: NodeJS.Timeout | null = null; let timeout: NodeJS.Timeout | null = null; // Quick prompt patterns for immediate detection const quickPromptPatterns = />>>\s*$|>\s*$|\$\s*$|#\s*$/; const cleanup = () => { if (interval) clearInterval(interval); if (timeout) clearTimeout(timeout); }; let resolveOnce = (value: string, isTimeout = false) => { if (resolved) return; resolved = true; cleanup(); timeoutReached = isTimeout; if (isTimeout) exitReason = 'timeout'; resolve(value); }; // Monitor for new output with immediate detection const session = terminalManager.getSession(pid); if (session && session.process && session.process.stdout && session.process.stderr) { const immediateDetector = (data: Buffer, source: 'stdout' | 'stderr') => { const text = data.toString(); const now = Date.now(); if (!firstOutputTime) firstOutputTime = now; lastOutputTime = now; if (verbose_timing) { outputEvents.push({ timestamp: now, deltaMs: now - startTime, source, length: text.length, snippet: text.slice(0, 50).replace(/\n/g, '\\n') }); } // Immediate check for obvious prompts if (quickPromptPatterns.test(text)) { const newOutput = terminalManager.getNewOutput(pid) || text; const state = analyzeProcessState(output + newOutput, pid); if (state.isWaitingForInput) { earlyExit = true; processState = state; exitReason = 'early_exit_quick_pattern'; if (verbose_timing && outputEvents.length > 0) { outputEvents[outputEvents.length - 1].matchedPattern = 'quick_pattern'; } resolveOnce(newOutput); return; } } }; const stdoutDetector = (data: Buffer) => immediateDetector(data, 'stdout'); const stderrDetector = (data: Buffer) => immediateDetector(data, 'stderr'); session.process.stdout.on('data', stdoutDetector); session.process.stderr.on('data', stderrDetector); // Cleanup immediate detectors when done const originalResolveOnce = resolveOnce; const cleanupDetectors = () => { if (session.process.stdout) { session.process.stdout.off('data', stdoutDetector); } if (session.process.stderr) { session.process.stderr.off('data', stderrDetector); } }; // Override resolveOnce to include cleanup const resolveOnceWithCleanup = (value: string, isTimeout = false) => { cleanupDetectors(); originalResolveOnce(value, isTimeout); }; // Replace the local resolveOnce reference resolveOnce = resolveOnceWithCleanup; } interval = setInterval(() => { const newOutput = terminalManager.getNewOutput(pid); if (newOutput && newOutput.length > 0) { const now = Date.now(); if (!firstOutputTime) firstOutputTime = now; lastOutputTime = now; if (verbose_timing) { outputEvents.push({ timestamp: now, deltaMs: now - startTime, source: 'periodic_poll', length: newOutput.length, snippet: newOutput.slice(0, 50).replace(/\n/g, '\\n') }); } const currentOutput = output + newOutput; const state = analyzeProcessState(currentOutput, pid); // Early exit if process is clearly waiting for input if (state.isWaitingForInput) { earlyExit = true; processState = state; exitReason = 'early_exit_periodic_check'; if (verbose_timing && outputEvents.length > 0) { outputEvents[outputEvents.length - 1].matchedPattern = 'periodic_check'; } resolveOnce(newOutput); return; } output = currentOutput; // Continue collecting if still running if (!state.isFinished) { return; } // Process finished processState = state; exitReason = 'process_finished'; resolveOnce(newOutput); } }, 50); // Check every 50ms for faster response timeout = setTimeout(() => { const finalOutput = terminalManager.getNewOutput(pid) || ""; resolveOnce(finalOutput, true); }, timeout_ms); }); const newOutput = await outputPromise; output += newOutput; // Analyze final state if not already done if (!processState) { processState = analyzeProcessState(output, pid); } } catch (error) { return { content: [{ type: "text", text: `Error reading output: ${error}` }], isError: true, }; } // Format response based on what we detected let statusMessage = ''; if (earlyExit && processState?.isWaitingForInput) { statusMessage = `\n🔄 ${formatProcessStateMessage(processState, pid)}`; } else if (processState?.isFinished) { statusMessage = `\n✅ ${formatProcessStateMessage(processState, pid)}`; } else if (timeoutReached) { statusMessage = '\n⏱️ Timeout reached - process may still be running'; } // Add timing information if requested let timingMessage = ''; if (verbose_timing) { const endTime = Date.now(); const timingInfo = { startTime, endTime, totalDurationMs: endTime - startTime, exitReason, firstOutputTime, lastOutputTime, timeToFirstOutputMs: firstOutputTime ? firstOutputTime - startTime : undefined, outputEvents: outputEvents.length > 0 ? outputEvents : undefined }; timingMessage = formatTimingInfo(timingInfo); } const responseText = output || 'No new output available'; return { content: [{ type: "text", text: `${responseText}${statusMessage}${timingMessage}` }], }; }
  • Zod input schema defining parameters for read_process_output: required pid (number), optional timeout_ms and verbose_timing.
    export const ReadProcessOutputArgsSchema = z.object({ pid: z.number(), timeout_ms: z.number().optional(), verbose_timing: z.boolean().optional(), });
  • Thin wrapper handler that parses input schema and delegates to the core readProcessOutput implementation. Exported for registration.
    /** * Handle read_process_output command (improved read_output) */ export async function handleReadProcessOutput(args: unknown): Promise<ServerResult> { const parsed = ReadProcessOutputArgsSchema.parse(args); return readProcessOutput(parsed); }
  • MCP server switch-case registration dispatching "read_process_output" tool calls to handleReadProcessOutput.
    case "read_process_output": result = await handlers.handleReadProcessOutput(args); break;
  • src/server.ts:762-799 (registration)
    Tool specification registration in list_tools handler, defining name, description, input schema, and annotations for read_process_output.
    name: "read_process_output", description: ` Read output from a running process with intelligent completion detection. Automatically detects when process is ready for more input instead of timing out. SMART FEATURES: - Early exit when REPL shows prompt (>>>, >, etc.) - Detects process completion vs still running - Prevents hanging on interactive prompts - Clear status messages about process state REPL USAGE: - Stops immediately when REPL prompt detected - Shows clear status: waiting for input vs finished - Shorter timeouts needed due to smart detection - Works with Python, Node.js, R, Julia, etc. DETECTION STATES: Process waiting for input (ready for interact_with_process) Process finished execution Timeout reached (may still be running) PERFORMANCE DEBUGGING (verbose_timing parameter): Set verbose_timing: true to get detailed timing information including: - Exit reason (early_exit_quick_pattern, early_exit_periodic_check, process_finished, timeout) - Total duration and time to first output - Complete timeline of all output events with timestamps - Which detection mechanism triggered early exit Use this to identify when timeouts could be reduced or detection patterns improved. ${CMD_PREFIX_DESCRIPTION}`, inputSchema: zodToJsonSchema(ReadProcessOutputArgsSchema), annotations: { title: "Read Process Output", readOnlyHint: true, }, },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wonderwhy-er/ClaudeComputerCommander'

If you have feedback or need assistance with the MCP directory API, please join our Discord server