How Search Result Pagination with Offset Works in DesktopCommanderMCP
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 with schema validation defined in 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 firstlengthresults from the beginning of the result setoffset = n: Skips the firstnresults and returns the nextlengthitems
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 lastnresults 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:
-
Bounds-checked slicing – JavaScript's
Array.slicesafely handles out-of-bounds indices by clamping them to valid ranges, ensuringreadSearchResultsnever throws index errors even whenoffsetexceeds the result count. -
Explicit completion signaling – The
hasMoreResultsboolean flag in the response indicates whether additional pages exist. The system calculates this asoffset + length < allResults.length || !session.isComplete, allowing clients to detect when they have reached the end of the data stream. -
Session-level completion tracking – The
session.isCompleteproperty monitors whether the underlying ripgrep process has finished executing. This prevents endless pagination loops after the search process terminates. -
Filtered array isolation – Pagination operates on
allResults, which excludes internal markers like__LAST_READ_MARKER__throughsession.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. This method handles both forward and tail pagination branches while computing the availability of additional results:
// 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 using Zod validation, ensuring type safety and consistent defaults across the API:
// 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:
await searchManager.startSearch({
rootPath: '/projects',
pattern: 'TODO',
searchType: 'content'
});
Fetching the First Page
Retrieve the initial results using default offset and length values:
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:
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:
const { results } =
searchManager.readSearchResults(sessionId, -20); // returns last 20 results
Detecting Pagination Completion
Check the hasMoreResults flag to determine when to stop requesting pages:
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. - Overflow is prevented through bounded Array.slice operations that safely handle out-of-range indices by returning empty arrays rather than throwing exceptions.
- The
hasMoreResultsflag combines array bounds checking with session completion status (session.isComplete) to signal when no additional data exists. - Schema validation in
src/tools/schemas.tsensures 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 within the readSearchResults method (lines 51-72). The API schema defining the offset and length parameters is located in src/tools/schemas.ts (lines 13-15), while src/handlers/search-handlers.ts formats the pagination output for client consumption.
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 →