# How Desktop Commander MCP Implements Process Output Pagination with Offset and Length Parameters

> Learn how Desktop Commander MCP handles process output pagination with offset and length parameters. Explore its line-based circular buffer and absolute positioning for efficient data retrieval via readOutputPaginated.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: internals
- Published: 2026-07-10

---

**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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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):

```typescript
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:

```typescript
// 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:

```typescript
// 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:

```typescript
// 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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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.