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 returns length lines forward. These reads are absolute and do not update lastReadIndex.

  • Zero offset: Represents "read from where I last stopped." The method sets startIndex = lastReadIndex, returns length lines, and automatically advances lastReadIndex by the count of lines read.

  • Positive offset: Treats the value as an absolute line number. The calculation startIndex = offset provides 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 output
  • totalLines: Total count in the current buffer
  • readCount: Number of lines returned in this request
  • remainingLines: Count available after the current read
  • evictedLines: Count of lines dropped due to memory limits
  • isComplete, 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 exceeding MAX_BUFFERED_OUTPUT_CHARS.
  • The readOutputPaginated method in src/terminal-manager.ts (lines 514-527) supports negative offsets (tail), zero offsets (incremental), and positive offsets (absolute positioning).
  • session.lastReadIndex persists the reading position across calls, enabling efficient polling patterns for long-running processes.
  • The PaginatedOutputResult type includes evictedLines metadata 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →