# How Desktop Commander MCP's Tool History Tracking System Works: A Technical Deep Dive

> Explore Desktop Commander MCP's tool history tracking system. Learn how it captures tool invocations in memory and JSONL for LLM retrieval via get_recent_tool_calls.

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

---

**Desktop Commander's tool history tracking system captures every tool invocation in a capped in-memory buffer and persistent JSONL file, enabling LLMs to retrieve recent context through the `get_recent_tool_calls` MCP tool.**

The tool history tracking system in Desktop Commander MCP serves as a critical context bridge for Large Language Models, capturing execution metadata to help AI assistants recover from interrupted sessions and debug command sequences. Implemented as a singleton in [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts), this system intercepts each tool call dispatched through [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), persisting arguments, outputs, and timing data to a local JSONL file while maintaining a fast in-memory index for immediate retrieval.

## Core Architecture of the ToolHistory Class

The **`ToolHistory`** class operates as a singleton that balances durability with performance, using a dual-layer storage approach that protects against memory bloat while ensuring data survives process restarts.

### In-Memory Buffer and Entry Limits

At the heart of the system lies a private array that stores the most recent invocations. The `ToolHistory` class maintains **`private history: ToolCallRecord[] = []`**, capped at **1000 entries** via the `MAX_ENTRIES` constant. When this limit is reached, older entries are discarded to prevent unbounded memory growth, ensuring the MCP server remains responsive during long-running sessions.

### Persistent Storage and File Management

Every tool call is appended as a JSON-Lines entry to **`~/.claude-server-commander/tool-history.jsonl`**, created automatically during class construction. To prevent disk exhaustion, the system implements aggressive file-size guards: when the history file exceeds **5 MiB** (`MAX_HISTORY_FILE_SIZE_BYTES`), the `trimHistoryFile()` method truncates it to **4 MiB**, preserving only the newest entries at the end of the file.

### Output Truncation and Size Guards

To avoid storing massive binary outputs or log dumps, individual tool results are truncated to **4 KB** (`MAX_STORED_OUTPUT_BYTES`). When outputs exceed this threshold, the system replaces the content with a placeholder message indicating truncation, significantly reducing storage overhead while maintaining forensic utility.

### Asynchronous Write Batching

Rather than blocking the request dispatcher with synchronous disk I/O, `ToolHistory` implements an async write queue flushed by a `setInterval` timer every **second**. This batching mechanism accumulates pending writes in memory and persists them in bulk, minimizing filesystem overhead during high-frequency tool invocation sequences.

## Integration with the MCP Request Flow

The history system hooks into the request pipeline within [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), where the dispatcher executes tool handlers and subsequently records the invocation. After a tool handler finishes execution, the server checks against an exclusion list before logging:

```typescript
if (!EXCLUDED_TOOLS.includes(name)) {
  toolHistory.addCall(name, args, result, duration);
}

```

The **`EXCLUDED_TOOLS`** array currently contains `get_recent_tool_calls` and `track_ui_event`, preventing recursive history entries when the LLM queries its own recent activity or when telemetry events fire.

## Querying Tool History via get_recent_tool_calls

The system exposes history retrieval through a first-class MCP tool defined in [`src/tools/schemas.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/schemas.ts) and implemented in [`src/handlers/history-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/handlers/history-handlers.ts). The **`handleGetRecentToolCalls`** function parses arguments through `GetRecentToolCallsArgsSchema` and returns formatted records:

```typescript
export async function handleGetRecentToolCalls(args: unknown): Promise<ServerResult> {
  const parsed = GetRecentToolCallsArgsSchema.parse(args);
  const calls = toolHistory.getRecentCallsFormatted({
    maxResults: parsed.maxResults,
    toolName: parsed.toolName,
    since: parsed.since,
  });
  const stats = toolHistory.getStats();
  const summary = `Tool Call History (${calls.length} results, ${stats.totalEntries} total in memory)`;
  return { content: [{ type: "text", text: `${summary}\n\n${JSON.stringify(calls, null, 2)}` }] };
}

```

The `getRecentCallsFormatted()` method converts ISO timestamps to human-readable local time using `formatLocalTimestamp()`, returning a JSON-stringified array that includes the tool name, arguments, truncated output, and execution duration in milliseconds.

## Lifecycle Management and Cleanup

During graceful shutdown or test teardown, the singleton's **`cleanup()`** method stops the background flush interval and ensures any queued writes are persisted to disk. This prevents data loss when the MCP server process terminates, maintaining the integrity of the historical record across restarts.

## Summary

- **Dual-layer storage**: Combines a 1000-entry in-memory buffer with a 5 MiB-capped JSONL file at `~/.claude-server-commander/tool-history.jsonl`.
- **Size protections**: Enforces 4 KB output truncation per entry and automatic file trimming to prevent disk exhaustion.
- **Async performance**: Uses a 1-second batched write queue to avoid blocking the request dispatcher.
- **Selective logging**: Excludes `get_recent_tool_calls` and `track_ui_event` from history to prevent recursion and telemetry pollution.
- **LLM accessibility**: Exposes history through the `get_recent_tool_calls` tool with local timezone formatting and statistical summaries.

## Frequently Asked Questions

### Where does Desktop Commander store the tool history file?

The system persists tool history to **`~/.claude-server-commander/tool-history.jsonl`** in the user's home directory. This JSON-Lines file is created automatically when the `ToolHistory` singleton initializes and is trimmed automatically when it exceeds 5 MiB to maintain storage efficiency.

### What is the maximum number of tool calls retained by the system?

The in-memory buffer retains **1000 entries** (`MAX_ENTRIES`), while the persistent file maintains entries up to **5 MiB** before automatic truncation. Individual tool outputs are capped at **4 KB** to prevent storage of excessively large binary data or log streams.

### How does the history tracking system prevent the file from growing indefinitely?

When the JSONL file exceeds **5 MiB** (`MAX_HISTORY_FILE_SIZE_BYTES`), the `trimHistoryFile()` method removes older entries from the beginning of the file until it reaches a target size of **4 MiB**, ensuring only the most recent activity persists while preventing disk space exhaustion.

### Which tools are excluded from history tracking and why?

The tools **`get_recent_tool_calls`** and **`track_ui_event`** are explicitly excluded from logging. The former is excluded to prevent recursive history entries when the LLM queries its own recent activity, while the latter is excluded because it represents pure telemetry rather than substantive command execution that requires forensic tracking.