# How Search Result Pagination with Offset Works in DesktopCommanderMCP

> Learn how DesktopCommanderMCP pagination with offset prevents overflow. Discover its bounded offset strategy and hasMoreResults flag for efficient data handling.

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

---

**DesktopCommanderMCP prevents search result overflow using bounded offset-based pagination that returns empty arrays for out-of-range requests and provides an explicit `hasMoreResults` flag to signal when no additional data exists.**

DesktopCommanderMCP implements a robust search result pagination system that mirrors its file-reading APIs to handle large result sets safely. The implementation uses an offset-based cursor mechanism centered in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts) with schema validation defined in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts), ensuring clients can navigate results without risking buffer overflows or index out-of-range errors.

## Offset Semantics and Cursor Behavior

The pagination system supports three distinct offset modes that operate on an already-filtered results array (`allResults`). This design ensures that pagination occurs after filtering, preventing the cursor from drifting as the underlying data changes.

### Zero and Positive Offsets (Forward Pagination)

When `offset` is `0` (default) or a positive integer, the system implements standard forward pagination using absolute indexing:

- **`offset = 0`**: Returns the first `length` results from the beginning of the result set
- **`offset = n`**: Skips the first `n` results and returns the next `length` items

The implementation uses `Array.slice(offset, offset + length)` to extract the window. JavaScript's native slice operation safely clamps indices, so requesting beyond the array bounds returns an empty array rather than throwing an error.

### Negative Offsets (Tail Pagination)

Negative offset values enable "tail" behavior to fetch the most recent results regardless of total count:

- **`offset = -n`**: Returns the last `n` results from the current result set

The system transforms negative values using `Math.abs(offset)` and applies `Array.slice(-tailCount)` to extract from the end of the array. This guarantees the request never exceeds the total result count, as slicing from the end is inherently bounded by the array length.

## Overflow Prevention Mechanisms

DesktopCommanderMCP employs four specific safeguards to prevent pagination overflow:

1. **Bounds-checked slicing** – JavaScript's `Array.slice` safely handles out-of-bounds indices by clamping them to valid ranges, ensuring `readSearchResults` never throws index errors even when `offset` exceeds the result count.

2. **Explicit completion signaling** – The `hasMoreResults` boolean flag in the response indicates whether additional pages exist. The system calculates this as `offset + length < allResults.length || !session.isComplete`, allowing clients to detect when they have reached the end of the data stream.

3. **Session-level completion tracking** – The `session.isComplete` property monitors whether the underlying ripgrep process has finished executing. This prevents endless pagination loops after the search process terminates.

4. **Filtered array isolation** – Pagination operates on `allResults`, which excludes internal markers like `__LAST_READ_MARKER__` through `session.results.filter(r => r.file !== '__LAST_READ_MARKER__')`, ensuring the cursor aligns only with actual search results.

## Implementation in search-manager.ts

The core pagination logic resides in the `readSearchResults` method within [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts). This method handles both forward and tail pagination branches while computing the availability of additional results:

```typescript
// src/search-manager.ts
readSearchResults(
  sessionId: string,
  offset: number = 0,
  length: number = 100
) {
  // Filter out internal markers before pagination
  const allResults = session.results.filter(r => r.file !== '__LAST_READ_MARKER__');

  // Tail pagination branch (negative offset)
  if (offset < 0) {
    const tailCount = Math.abs(offset);
    const tailResults = allResults.slice(-tailCount);
    return { 
      results: tailResults, 
      hasMoreResults: false 
    };
  }

  // Forward pagination branch (zero or positive offset)
  const slicedResults = allResults.slice(offset, offset + length);
  const hasMoreResults = offset + length < allResults.length || !session.isComplete;
  
  return { 
    results: slicedResults, 
    hasMoreResults 
  };
}

```

Lines 51-72 in the source file contain the conditional logic that separates tail handling from range slicing, while the `hasMoreResults` calculation on lines 69-72 prevents clients from requesting non-existent pages.

## Schema Definition and API Contract

The pagination parameters are formally defined in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts) using Zod validation, ensuring type safety and consistent defaults across the API:

```typescript
// src/tools/schemas.ts
export const GetMoreSearchResultsArgsSchema = z.object({
  sessionId: z.string(),
  offset: z.number().optional().default(0),
  length: z.number().optional().default(100),
});

```

This schema mirrors the file-reading tool parameters, maintaining API consistency across DesktopCommanderMCP. The default values ensure backward compatibility while the optional typing allows clients to specify precise pagination windows when needed.

## Practical Usage Examples

### Starting a Search Session

Initiate a search without pagination parameters—the system buffers results internally:

```typescript
await searchManager.startSearch({
  rootPath: '/projects',
  pattern: 'TODO',
  searchType: 'content'
});

```

### Fetching the First Page

Retrieve the initial results using default offset and length values:

```typescript
const { results, hasMoreResults, totalResults } =
  searchManager.readSearchResults(sessionId); // offset defaults to 0, length to 100

```

### Advancing to the Next Page

Calculate the next offset by adding the previous offset and length, then request the subsequent window:

```typescript
const nextOffset = 100; // previous offset (0) + previous length (100)
const { results, hasMoreResults } = 
  searchManager.readSearchResults(sessionId, nextOffset, 100);

```

### Retrieving the Last N Results

Use a negative offset to fetch the most recent results regardless of total count:

```typescript
const { results } = 
  searchManager.readSearchResults(sessionId, -20); // returns last 20 results

```

### Detecting Pagination Completion

Check the `hasMoreResults` flag to determine when to stop requesting pages:

```typescript
if (!hasMoreResults) {
  console.log('All results have been retrieved.');
}

```

## Summary

- **DesktopCommanderMCP uses offset-based pagination** with support for forward (positive) and tail (negative) cursor positioning, implemented in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts).
- **Overflow is prevented through bounded Array.slice operations** that safely handle out-of-range indices by returning empty arrays rather than throwing exceptions.
- **The `hasMoreResults` flag** combines array bounds checking with session completion status (`session.isComplete`) to signal when no additional data exists.
- **Schema validation in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts)** ensures consistent default values (offset: 0, length: 100) and type safety across the API.
- **Results are filtered** to exclude internal markers before pagination, ensuring cursor accuracy against actual search results.

## Frequently Asked Questions

### What happens if the offset exceeds the total number of results?

If the `offset` parameter exceeds the length of the results array, `Array.slice` returns an empty array without throwing an error. The `hasMoreResults` flag will be set to `false` (assuming the search is complete), signaling the client that no additional data exists.

### How does negative offset (tail) pagination work?

Negative offsets trigger tail mode, where the system calculates `tailCount = Math.abs(offset)` and returns `allResults.slice(-tailCount)`. This extracts the last N results from the current result set, useful for retrieving the most recent matches without knowing the total result count beforehand.

### How does the system prevent infinite pagination loops?

The `hasMoreResults` calculation combines two conditions: `offset + length < allResults.length` (checking if more results exist in the buffer) and `!session.isComplete` (checking if the underlying ripgrep process is still running). When both conditions are false, the flag returns `false`, indicating the client should stop requesting additional pages.

### Where is the pagination logic defined in the codebase?

The primary implementation resides in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts) within the `readSearchResults` method (lines 51-72). The API schema defining the offset and length parameters is located in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts) (lines 13-15), while [`src/handlers/search-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/handlers/search-handlers.ts) formats the pagination output for client consumption.