How Desktop Commander MCP Implements Process Output Pagination with Offset and Length Parameters
Desktop Commander MCP implements process output pagination using a line-based circular buffer that supports three offset modes—negative for tail reads, zero for incremental reads, and positive for absolute positioning—returning paginated results via the readOutputPaginated method in src/terminal-manager.ts.
Desktop Commander MCP is a Model Context Protocol (MCP) server that enables AI assistants to execute and monitor terminal commands. When handling long-running processes or large output streams, the system provides sophisticated process output pagination with offset and length parameters to efficiently retrieve specific portions of stdout and stderr without overwhelming the client or consuming excessive memory.
The Pagination Architecture
At its core, Desktop Commander MCP treats every spawned process as a stream of discrete lines stored in memory. This design supports three distinct pagination APIs that operate uniformly on both active and completed sessions.
Line-Based Circular Buffer
When a process launches, the TerminalManager class splits incoming data from stdout and stderr into individual lines. These lines accumulate in session.outputLines, an array where each element represents one line of output. To prevent unbounded memory growth, the system enforces MAX_BUFFERED_OUTPUT_CHARS, automatically evicting the oldest lines when the buffer reaches capacity.
The manager tracks pagination state through session.lastReadIndex, which stores the position of the last read operation. This enables efficient "read new output" workflows where subsequent calls automatically resume from the previous position.
The Three Offset Modes
The readOutputPaginated(pid, offset?, length?) method supports three distinct offset behaviors, as implemented in src/terminal-manager.ts lines 555-603:
-
Negative offset: Interprets the value as "start N lines from the end" (tail read). The system calculates
startIndex = totalLines - |offset|and returnslengthlines forward. These reads are absolute and do not updatelastReadIndex. -
Zero offset: Represents "read from where I last stopped." The method sets
startIndex = lastReadIndex, returnslengthlines, and automatically advanceslastReadIndexby the count of lines read. -
Positive offset: Treats the value as an absolute line number. The calculation
startIndex = offsetprovides direct access to any position in the buffer without affecting the incremental read pointer.
Implementation Details
Core Methods and File Locations
The pagination logic resides primarily in src/terminal-manager.ts. The public entry point readOutputPaginated (lines 514-527) first locates the target session in either active processes or completedSessions, then delegates to the private helper readFromLineBuffer (lines 555-603).
For backward compatibility, the wrapper method getNewOutput(pid, maxLines?) simply invokes readOutputPaginated(pid, 0, maxLines), defaulting to the incremental read behavior.
The result type PaginatedOutputResult (defined in src/types.ts) returns:
lines: Array of strings containing the requested outputtotalLines: Total count in the current bufferreadCount: Number of lines returned in this requestremainingLines: Count available after the current readevictedLines: Count of lines dropped due to memory limitsisComplete,exitCode,runtimeMs: Process termination metadata
Memory Management and Eviction
When MAX_BUFFERED_OUTPUT_CHARS is exceeded, the buffer truncates from the beginning. The session counters evictedLines and evictedChars increment accordingly, and the PaginatedOutputResult includes these values so callers can adjust absolute offsets for subsequent reads if necessary.
Completed sessions persist in completedSessions, ensuring that pagination remains available after a process terminates. This unified storage model means the same offset and length parameters work identically whether the process is running or finished.
Practical Code Examples
Reading Fresh Output Incrementally
To retrieve only new lines since the last read (the default behavior):
import { TerminalManager } from './src/terminal-manager';
const manager = new TerminalManager();
const pid = await manager.createSession('long-running-task.sh');
// First call reads from position 0
const result = manager.readOutputPaginated(pid, 0, 100);
console.log(`Read ${result.readCount} lines, ${result.remainingLines} remaining`);
// Subsequent calls automatically resume from lastReadIndex
const update = manager.readOutputPaginated(pid, 0, 100);
Tail Reading with Negative Offset
To access the most recent output from a completed process:
// After process termination, retrieve the last 50 lines
const tailResult = manager.readOutputPaginated(pid, -50, 50);
console.log('Recent output:');
tailResult.lines.forEach(line => console.log(line));
Here, offset = -50 instructs the system to start 50 lines from the end of the buffer.
Absolute Offset Reads
For random access to specific line ranges:
// Read lines 500-529 directly
const chunk = manager.readOutputPaginated(pid, 500, 30);
if (chunk.lines.length < 30) {
console.warn('Requested beyond available buffer range');
}
Note that positive offsets do not modify lastReadIndex, leaving the incremental pointer unchanged for subsequent zero-offset reads.
Backward Compatibility Wrapper
Legacy code using getNewOutput receives the same pagination treatment:
// Equivalent to readOutputPaginated(pid, 0, 1000)
const output = manager.getNewOutput(pid);
Summary
- Desktop Commander MCP stores process output in
session.outputLines, a line-based array with automatic eviction when exceedingMAX_BUFFERED_OUTPUT_CHARS. - The
readOutputPaginatedmethod insrc/terminal-manager.ts(lines 514-527) supports negative offsets (tail), zero offsets (incremental), and positive offsets (absolute positioning). session.lastReadIndexpersists the reading position across calls, enabling efficient polling patterns for long-running processes.- The
PaginatedOutputResulttype includesevictedLinesmetadata to help clients adjust absolute offsets when buffer truncation occurs. - Completed sessions remain accessible through
completedSessions, maintaining consistent pagination behavior after process termination.
Frequently Asked Questions
How does the offset parameter work in Desktop Commander MCP?
The offset parameter supports three behaviors: negative values read from the end of the buffer (e.g., -20 returns the last 20 lines), zero resumes from the last read position using lastReadIndex, and positive values specify absolute line numbers starting from the beginning of the buffer. This design accommodates tail monitoring, incremental polling, and random access patterns.
What is the default behavior when calling readOutputPaginated?
When called with default parameters readOutputPaginated(pid, 0, 1000), the method returns up to 1000 lines starting from the lastReadIndex position, then automatically advances the internal pointer by the number of lines read. This ensures subsequent calls with offset = 0 only return new output generated since the previous request.
How does the system handle memory limits for large outputs?
The implementation enforces MAX_BUFFERED_OUTPUT_CHARS by evicting the oldest lines when the buffer grows too large. The PaginatedOutputResult includes an evictedLines counter that tracks how many lines have been dropped, allowing clients to detect when their absolute offsets may have shifted due to buffer truncation.
Can I paginate output from a process that has already terminated?
Yes, Desktop Commander MCP stores completed sessions in a completedSessions map that preserves the outputLines buffer and supports identical pagination semantics. Whether the process is active or finished, readOutputPaginated returns consistent results using the same offset and length parameters.
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 →