How Process Output Pagination Prevents Context Overflow in Desktop Commander MCP
Desktop Commander MCP uses a file-like pagination API with configurable line limits and buffer caps to ensure that reading process output never returns more data than the LLM's context window can handle.
Process output pagination is a critical safeguard in the Desktop Commander MCP server. Instead of dumping the entire stdout/stderr buffer of a spawned process into a single tool response, the system slices the output into manageable chunks. This design prevents "context overflow," where unbounded process logs could exhaust the model's token limit and degrade performance.
The Pagination API: Offset, Length, and Limits
The read_process_output function in src/tools/improved-process-tools.ts implements a virtual terminal manager that treats process output as a seekable stream. When a client requests output, they receive only the specific slice requested, not the entire buffer.
Requesting Specific Line Ranges
The pagination system supports three offset modes via the offset parameter:
offset = 0– Returns only "new" output generated since the last read- Positive integer – Absolute line number to start reading from
- Negative integer – Tail offset (e.g.,
-5returns the last 5 lines)
The length parameter caps the number of lines returned. According to the source code in src/tools/improved-process-tools.ts (lines 38-81), the function reads only the requested range and appends a concise status line indicating whether more content is available.
Default Line Limits and Configuration
If the caller omits the length parameter, the system falls back to the fileReadLineLimit configuration value, which defaults to 1000 lines (source: src/tools/improved-process-tools.ts, lines 51-60). This guarantees that even a naive request cannot accidentally return megabytes of log data.
Buffer Management and Eviction Warnings
Behind the pagination API, the terminal manager enforces a hard ceiling on memory usage. The constant MAX_BUFFERED_OUTPUT_CHARS caps the total buffered output for any single process.
When this cap is reached, the system evicts the earliest lines from the buffer to make room for new data. Crucially, it appends an explicit warning to the status message indicating that truncation has occurred (source: src/tools/improved-process-tools.ts, lines 44-50). This transparency ensures users know when data has been dropped and can adjust their polling strategy accordingly.
Integration with Interactive Processes
The same safeguards apply to interact_with_process. After sending input to a running process, the function reads the resulting output but truncates it to fileReadLineLimit before returning. If additional content remains, the response includes a hint to use read_process_output to retrieve the full log (source: src/tools/improved-process-tools.ts, lines 68-76).
This pattern ensures that even interactive REPL sessions cannot generate responses that overwhelm the context window.
Practical Usage Examples
The following examples demonstrate how to stream output from a long-running process using offset-based pagination:
// 1️⃣ Start a long-running process
import { startProcess, readProcessOutput } from './dist/tools/improved-process-tools.js';
const { pid } = await startProcess({
command: 'node -e "let i=0; setInterval(()=>console.log(`tick${i++}`),200)"',
timeout_ms: 500 // return early, process keeps running
});
// 2️⃣ Read only the *new* output since the last read (offset = 0)
let result = await readProcessOutput({ pid, timeout_ms: 300 });
console.log(result.content[0].text); // → shows the first few "tick" lines
// 3️⃣ Get the next chunk of new output
await new Promise(r => setTimeout(r, 400));
result = await readProcessOutput({ pid, timeout_ms: 300 });
console.log(result.content[0].text); // → only the lines that appeared after the previous read
// 4️⃣ Read a specific slice (absolute offset = 5, length = 3)
result = await readProcessOutput({ pid, offset: 5, length: 3, timeout_ms: 1000 });
console.log(result.content[0].text); // → lines 5-7
// 5️⃣ Tail-read the last 5 lines of a completed process
result = await readProcessOutput({ pid, offset: -5, timeout_ms: 1000 });
console.log(result.content[0].text); // → the final 5 lines
Summary
- Bounded returns: The
read_process_outputfunction insrc/tools/improved-process-tools.tsnever returns the full buffer, only the requested slice. - Configurable safety: Default line limits (
fileReadLineLimit) prevent accidental overload, defaulting to 1000 lines. - Memory protection:
MAX_BUFFERED_OUTPUT_CHARScaps total buffered data, with early eviction and warnings when limits are hit. - Consistent application: Both direct reads and interactive sessions (
interact_with_process) enforce the same truncation logic.
Frequently Asked Questions
What happens if a process generates more output than MAX_BUFFERED_OUTPUT_CHARS?
When the buffer reaches MAX_BUFFERED_OUTPUT_CHARS, the terminal manager automatically removes the oldest lines to accommodate new data. It appends a warning to the status message indicating that eviction has occurred, allowing you to detect data loss and adjust your polling frequency.
How do I read only new output since my last request?
Pass offset: 0 to read_process_output. The virtual terminal manager tracks your last read position and returns only lines generated after that point, keeping responses minimal and efficient.
Why does interact_with_process truncate output instead of paginating?
After sending input to a process, interact_with_process truncates results to fileReadLineLimit to guarantee the response fits within the context window. If truncation occurs, the response explicitly advises calling read_process_output with specific offsets to retrieve the remaining content. This two-step approach keeps interactive sessions responsive while preserving access to complete logs.
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 →