How DesktopCommanderMCP Stores and Retrieves Recent Tool Calls: JSON-Lines Architecture Explained

DesktopCommanderMCP maintains a per-user, append-only JSON-Lines file at ~/.claude-server-commander/tool-history.jsonl that caps memory at 1000 entries and disk usage at approximately 5 MiB while providing filtered retrieval via the ToolHistory class.

DesktopCommanderMCP, an open-source Model Context Protocol (MCP) server for desktop automation, implements a robust tool history system to track every invocation for debugging and auditing. The system uses a bounded, append-only storage mechanism that balances durability with strict resource constraints, ensuring that long-running sessions do not exhaust disk or memory.

Storage Architecture and File Location

The history system persists data in a hidden directory within the user's home folder, using a format that supports atomic appends and easy truncation.

The JSON-Lines Persistence Layer

All tool invocations are recorded in tool-history.jsonl, a JSON-Lines file where each line represents a single ToolCallRecord. This format allows the system to append new entries without rewriting the entire file, and enables efficient tail-reading when trimming old data. The file resides in:

~/.claude-server-commander/

Bounded Resource Guarantees

The implementation enforces hard limits to prevent resource exhaustion:

  • Memory bound: Only the most recent 1000 entries are retained in RAM
  • Disk bound: The JSON-Lines file never exceeds approximately 5 MiB; older lines are discarded when the threshold is crossed
  • Output capping: Individual entry outputs are truncated to 4 KiB to prevent runaway JSON size

Core Implementation in src/utils/toolHistory.ts

The ToolHistory class in src/utils/toolHistory.ts manages the entire lifecycle of history records, from initial load to batched disk writes.

Initialization and Memory Management

When the application starts, the constructor performs three critical operations:

  1. Creates the ~/.claude-server-commander/ directory if it does not exist
  2. Loads existing records from tool-history.jsonl into memory
  3. Starts an asynchronous write processor to batch disk operations

This initialization ensures that history persists across server restarts while maintaining the 1000-entry memory ceiling.

Recording Tool Calls with addCall

The addCall(toolName, args, output, duration?) method creates a ToolCallRecord and stages it for persistence. The method implements several safety mechanisms:

import { toolHistory } from './utils/toolHistory';

async function executeTool() {
  const result = await performOperation();
  // Records the call with automatic output capping
  toolHistory.addCall('file_search', { pattern: '*.ts' }, result, 145);
}

Before queuing, the method invokes capOutput to truncate any output exceeding 4 KiB, replacing oversized content with a placeholder message. The record is then pushed onto the in-memory array and queued for the next batch write.

Asynchronous Disk Persistence

Rather than writing on every call, the system uses startWriteProcessor to manage a periodic timer that flushes queued records via flushToDisk. This batching strategy reduces I/O overhead during high-frequency tool usage.

Automatic File Trimming

When the write processor detects that the file has grown past the 5 MiB threshold, trimHistoryFileIfTooLarge rewrites only the most recent tail of the file before appending new entries. This operation preserves the newest history while reclaiming disk space from obsolete records.

Retrieving Recent Tool Calls

The history system provides two primary APIs for accessing stored invocations, both supporting optional filtering by tool name or timestamp.

Querying with getRecentCalls

The getRecentCalls({ maxResults, toolName, since }) method returns the last N entries (default 50, maximum 1000) from the in-memory buffer. The method signature supports precise filtering:

const recent = toolHistory.getRecentCalls({
  maxResults: 20,                    // Return up to 20 entries
  toolName: 'execute_command',       // Filter by specific tool
  since: '2026-08-01T00:00:00Z'     // Only entries after this timestamp
});

This approach returns raw ToolCallRecord objects containing the tool name, arguments, truncated output, duration, and ISO timestamp.

Formatted Output via getRecentCallsFormatted

For display purposes, getRecentCallsFormatted maps raw timestamps to localized strings using formatLocalTimestamp, converting UTC storage times to the user's local timezone:

const formatted = toolHistory.getRecentCallsFormatted({ maxResults: 10 });

formatted.forEach(record => {
  console.log(`${record.timestamp} – ${record.toolName}`);
  console.log(`Duration: ${record.duration}ms`);
  console.log(record.output);
});

RPC Exposure Through history-handlers.ts

The history functionality is exposed to clients via an HTTP/WebSocket handler defined in src/handlers/history-handlers.ts. This handler registers the get_recent_tool_calls RPC endpoint, which maps incoming requests to the ToolHistory retrieval methods.

All server instances share the same ~/.claude-server-commander/tool-history.jsonl file, ensuring a consistent view of recent activity even when multiple clients connect to different server processes. The handler serializes the filtered results and transmits them over the established transport connection.

Safety Bounds and Performance Characteristics

The DesktopCommanderMCP tool history system is designed for production reliability with three critical safeguards:

  • Atomic appends: JSON-Lines format allows safe concurrent writes from multiple processes
  • Graceful degradation: When output exceeds 4 KiB, the system stores a truncation notice rather than failing
  • Automatic cleanup: The 5 MiB disk limit ensures that logging never fills the user's home directory, even during extended debugging sessions

These constraints make the history system suitable for long-running desktop automation workflows without manual maintenance.

Summary

  • DesktopCommanderMCP stores tool history in ~/.claude-server-commander/tool-history.jsonl using an append-only JSON-Lines format
  • The ToolHistory class in src/utils/toolHistory.ts caps memory usage at 1000 entries and disk usage at ~5 MiB
  • The addCall method automatically truncates outputs exceeding 4 KiB before staging for disk writes
  • Batched writes via flushToDisk minimize I/O overhead while trimHistoryFileIfTooLarge prevents unbounded growth
  • Retrieval methods getRecentCalls and getRecentCallsFormatted support filtering by tool name, timestamp, and result count
  • The get_recent_tool_calls RPC in src/handlers/history-handlers.ts exposes history to remote clients over shared storage

Frequently Asked Questions

How does DesktopCommanderMCP prevent the tool history file from growing indefinitely?

The system implements a 5 MiB size cap enforced by the trimHistoryFileIfTooLarge method in src/utils/toolHistory.ts. When the JSON-Lines file exceeds this threshold during a write operation, the system reads only the most recent tail of the file, discards older entries, and rewrites the truncated content before appending new records. This ensures that long-running sessions cannot exhaust disk space.

What happens when a tool returns output larger than 4 KiB?

The capOutput helper function truncates any output exceeding 4096 bytes and replaces the excess with a placeholder message indicating truncation. This prevents individual ToolCallRecord entries from becoming bloated, ensuring that the in-memory array of 1000 entries and the JSON-Lines file remain within their bounded size limits.

Can I query tool history for a specific time range or tool name?

Yes. The getRecentCalls method accepts optional parameters for toolName and since (ISO timestamp), allowing you to filter results to specific tools or time periods. The method returns entries from the in-memory buffer, which contains the most recent 1000 calls, defaulting to 50 results if no limit is specified.

Is the tool history shared between multiple DesktopCommanderMCP server instances?

Yes. Because all instances read from and write to the same ~/.claude-server-commander/tool-history.jsonl file, multiple server processes maintain a consistent view of recent activity. The JSON-Lines format supports atomic appends, making it safe for concurrent access from different server instances or client connections.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →