# How to Implement Search Sessions Using the SearchManager Class in Desktop Commander

> Implement search sessions with the SearchManager class in Desktop Commander. Spawn persistent ripgrep processes for incremental results, pagination, session monitoring, and on-demand termination.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: how-to-guide
- Published: 2026-07-25

---

**Use the `SearchManager` class in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts) to spawn persistent `ripgrep` processes that stream results incrementally, allowing you to paginate through matches, monitor active sessions, and terminate searches on demand.**

Desktop Commander (wonderwhy-er/DesktopCommanderMCP) provides a robust **search session** API that wraps `ripgrep` execution into manageable, stateful objects. When you implement search sessions using the `SearchManager` class, you gain fine-grained control over long-running file searches, including the ability to fetch partial results while the process continues running in the background.

## Understanding the SearchManager Architecture

The `SearchManager` class maintains an internal map of **SearchSession** objects, each representing a running `ripgrep` child process. According to the source code in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts), a session tracks the process handle, accumulated results, stdout buffers, timestamps, and search metadata.

Key components include:

- **`SearchSession` interface** (lines 17-30): Defines the state structure for active searches, including `totalMatches`, `totalContextLines`, and `buffer` for partial JSON lines.
- **`startSearch` method**: Validates paths, constructs `ripgrep` arguments, spawns the process, and returns a session ID with initial results captured within the first ~40ms.
- **`readSearchResults` method**: Implements offset-based pagination supporting both range queries (positive offset) and tail behavior (negative offset).
- **`terminateSearch` method**: Sends `SIGTERM` to the child process while preserving results for subsequent reads.

## Starting a Search Session

To implement a search session, invoke `searchManager.startSearch()` with a configuration object specifying the search parameters. The method is located at lines 55-20 in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts) and handles argument construction for literal searches, case sensitivity, hidden files, and context lines.

```typescript
import { searchManager } from './src/search-manager.js';

async function startNewSearch() {
  const { sessionId, results, isComplete, runtime } = await searchManager.startSearch({
    rootPath: '/path/to/project',
    pattern: 'TODO',
    searchType: 'content',          // Use 'files' for filename-only search
    ignoreCase: true,
    maxResults: 200,
    contextLines: 2,
    earlyTermination: true,         // Stops after finding exact filename matches
    literalSearch: false,
  });

  console.log(`Session ${sessionId} started (${runtime}ms)`);
  console.log(`Received ${results.length} initial results (complete: ${isComplete})`);
}

```

The method returns a unique `sessionId` (formatted as `search_{type}_{timestamp}`), an array of initial results, and an `isComplete` boolean indicating whether the `ripgrep` process has already finished.

## Retrieving Paginated Results

Once a session is active, use `readSearchResults` to fetch slices of the accumulated results without blocking on the process completion. This method supports two pagination modes as implemented in lines 22-40 of [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts):

- **Positive offset**: Range-based pagination (e.g., `offset=0, length=100` returns results 0-99).
- **Negative offset**: Tail behavior (e.g., `offset=-10` returns the last 10 results).

```typescript
// Fetch first page of 100 results
const page1 = searchManager.readSearchResults('search_1_1698345678901', 0, 100);
console.log(page1.results);
console.log(`Has more: ${page1.hasMoreResults}`);

// Fetch last 20 results (tail behavior)
const tail = searchManager.readSearchResults('search_1_1698345678901', -20);
console.log('Recent matches:', tail.results);

```

The return object includes `hasMoreResults` (indicating if the buffer contains additional unpaginated results), `wasIncomplete` (true when `ripgrep` exits with code 2 due to permission-restricted files), and `runtime` (milliseconds since session start).

## Managing Session Lifecycle

### Terminating Active Searches

To stop a running search prematurely, call `terminateSearch` with the session ID. Located at lines 90-07 in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts), this method sends `SIGTERM` to the underlying process:

```typescript
const wasKilled = searchManager.terminateSearch('search_1_1698345678901');
console.log(wasKilled ? 'Process terminated' : 'Session not found');

```

The session remains in the manager's internal map after termination, allowing you to continue reading accumulated results until the global cleanup interval removes it (default idle timeout is 5 minutes).

### Listing Active Sessions

Monitor all search operations using `listSearchSessions`, which returns a concise summary of every session in the manager's map:

```typescript
const sessions = searchManager.listSearchSessions();
sessions.forEach(session => {
  console.log(
    `[${session.id}] ${session.searchType} "${session.pattern}" – ` +
    `${session.isComplete ? 'DONE' : 'RUNNING'} (${session.totalResults} matches)`
  );
});

```

## Working with High-Level Handlers

For RPC-style integration, Desktop Commander exposes wrapper functions in [`src/handlers/search-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/handlers/search-handlers.ts). These handlers validate inputs using Zod schemas (defined in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts)) and delegate to the `SearchManager`:

```typescript
import { 
  handleStartSearch, 
  handleGetMoreSearchResults, 
  handleStopSearch, 
  handleListSearches 
} from './src/handlers/search-handlers.js';

// Initiate search
const start = await handleStartSearch({ 
  path: '.', 
  pattern: 'error', 
  searchType: 'content' 
});

// Paginate results
const more = await handleGetMoreSearchResults({ 
  sessionId: 'search_1_1698345678901', 
  offset: 0, 
  length: 50 
});

// Terminate
const stop = await handleStopSearch({ sessionId: 'search_1_1698345678901' });

// Monitor
const list = await handleListSearches();

```

These handlers provide the same functionality as direct `SearchManager` calls but include argument validation and standardized response formatting for the Model Context Protocol (MCP) server interface.

## Summary

- **Import `searchManager`** from [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts) to access the singleton instance managing all search sessions.
- **Start sessions** with `startSearch()`, which spawns `ripgrep` and returns a session ID plus initial results captured during the first 40ms.
- **Paginate results** using `readSearchResults()` with positive offsets for ranges or negative offsets for tail behavior.
- **Terminate processes** gracefully with `terminateSearch()`, which sends `SIGTERM` but preserves results for subsequent reads.
- **Monitor activity** via `listSearchSessions()` or use the high-level handlers in [`src/handlers/search-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/handlers/search-handlers.ts) for validated RPC interfaces.

## Frequently Asked Questions

### What is the difference between `searchType: 'content'` and `'files'`?

**Content search** scans file contents for pattern matches using `ripgrep`'s standard regex engine, while **files search** uses the `-g` glob pattern to match filenames directly. When using `searchType: 'files'`, enable `earlyTermination: true` to stop the search immediately after finding matching filenames, improving performance for large repositories.

### How does the negative offset pagination work in `readSearchResults`?

Passing a negative `offset` value (e.g., `-20`) to `readSearchResults` activates **tail behavior**, returning the last N results from the accumulated buffer. This is useful for real-time monitoring scenarios where you need the most recent matches without fetching the entire result set. Positive offsets behave like standard array slicing (`offset` to `offset + length`).

### What happens when `ripgrep` encounters permission errors during a search?

If `ripgrep` exits with code 2 (indicating permission-restricted files were encountered), the `SearchManager` marks the session with `wasIncomplete: true` but preserves all successfully read results. You can still paginate through the partial results, and the `totalMatches` counter reflects only the accessible files. The session is considered complete despite the error condition.

### Can I implement custom cleanup intervals for terminated sessions?

The `SearchManager` runs a global cleanup interval that removes completed sessions after a configurable idle period (default 5 minutes). While the current implementation in [`src/search-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/search-manager.ts) uses a fixed timer, you can force immediate cleanup by calling `terminateSearch()` followed by manual deletion of the session from the internal map, though this is not recommended as it prevents post-termination result retrieval.