# How Desktop Commander Implements Audit Logging for Tool Calls: A Two-Layer Approach

> Desktop Commander uses a two-layer audit logging approach combining plain-text rotation logs and JSON-Lines history. Track every tool invocation with human-readable trails and queryable data.

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

---

**Desktop Commander implements audit logging for tool calls through a dual-layer system that combines lightweight plain-text rotation logs with structured JSON-Lines history, ensuring both human-readable audit trails and queryable data for every tool invocation.**

The wonderwhy-er/DesktopCommanderMCP repository provides a Model Context Protocol (MCP) server that records every tool request through a sophisticated audit logging system. By splitting responsibilities between high-performance text logging and rich structured storage, Desktop Commander maintains comprehensive observability without blocking the main execution loop.

## The Two-Layer Audit Architecture

Desktop Commander employs complementary logging strategies to serve different observability needs. The **lightweight logging layer** captures essential metadata in a rotating plain-text file for quick inspection and external log aggregation, while the **rich history layer** maintains structured JSON records suitable for UI components and programmatic analysis.

## Lightweight Logging with trackToolCall

### Log Format and Storage Location

Located in [`src/utils/trackTools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/trackTools.ts), the `trackToolCall(name, args)` function appends a single-line entry to `claude_tool call.log` within the user's home configuration directory at `.claude-server-commander`. Each entry contains an ISO timestamp, the tool name padded to 20 characters, and a JSON-encoded copy of the arguments.

### Automatic Rotation at 10 MiB

To prevent unbounded disk usage, the system monitors file size against `TOOL_CALL_FILE_MAX_SIZE` defined in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts). When the log exceeds **10 MiB**, the current file is automatically renamed with a timestamp suffix and a fresh log file is initiated. This rotation happens transparently during the write operation.

```typescript
import { promises as fs } from 'fs';
import path from 'path';
import { TOOL_CALL_FILE } from './config.js';

// Executed automatically when file size exceeds 10 MiB
await fs.rename(
  TOOL_CALL_FILE,
  `${path.basename(TOOL_CALL_FILE, '.log')}_${new Date().toISOString().replace(/[:.]/g, '-')}.log`
);

```

## Rich History Storage

### JSON-Lines Structure and Content

The [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts) module maintains `tools-history.jsonl`, storing structured records via `toolHistory.addCall()`. Each entry preserves the timestamp, tool name, arguments, server response, and optional execution duration. This format enables complex querying and analytics while remaining line-parseable.

### Memory Capping and Async Batching

The history implementation caps the in-memory list to the latest **1,000 entries** to prevent memory leaks. Writes to disk occur asynchronously in batches once per second, ensuring the main event loop remains unblocked during high-volume tool execution. When the on-disk file exceeds **5 MiB**, the system trims older entries while preserving the most recent history.

```typescript
import { trackToolCall } from './utils/trackTools.js';
import { toolHistory } from './utils/toolHistory.js';

async function handleToolRequest(name: string, args: unknown) {
  const startTime = Date.now();
  
  // Lightweight audit logging to plain-text file
  trackToolCall(name, args);
  
  // Execute the tool operation
  const result = await runTool(name, args);
  
  // Rich structured history with duration tracking
  toolHistory.addCall(name, args, result, Date.now() - startTime);
}

```

## Server Integration and Configuration

Incoming JSON-RPC requests hit the handler in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), which immediately invokes `trackToolCall(name, args)` for every tool invocation. The centralized configuration in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts) ensures consistent file paths and size limits across both logging layers, while [`src/utils/logger.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/logger.ts) provides the underlying transport infrastructure used by other system components.

### Querying the Audit Trail

Developers can retrieve formatted history entries for UI components or debugging interfaces:

```typescript
// Retrieve recent calls with formatting for display
const recent = toolHistory.getRecentCallsFormatted({ maxResults: 20 });
console.log(recent);

```

## Summary

- Desktop Commander uses **two complementary layers**: lightweight text logs in [`src/utils/trackTools.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/trackTools.ts) and structured JSON-Lines history in [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts).
- Plain-text logs rotate automatically at **10 MiB** (`TOOL_CALL_FILE_MAX_SIZE` in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts)) to prevent disk overflow.
- Structured history retains the latest **1,000 entries** in memory and trims the on-disk file at **5 MiB**, writing asynchronously in **one-second batches**.
- Every tool invocation flows through [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), which triggers `trackToolCall()` for immediate audit capture.
- Both layers store data in the `.claude-server-commander` directory under the user's home folder.

## Frequently Asked Questions

### Where are the audit log files stored?

Desktop Commander stores audit data in the `.claude-server-commander` directory within the user's home folder. The lightweight log writes to `claude_tool call.log`, while the structured history maintains `tools-history.jsonl` in the same location.

### What triggers the rotation of audit log files?

The plain-text audit log rotates when it exceeds **10 MiB**, as defined by the `TOOL_CALL_FILE_MAX_SIZE` constant in [`src/config.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config.ts). The system renames the current file with a timestamp suffix and creates a fresh log automatically.

### How does Desktop Commander prevent audit logging from consuming excessive memory?

The [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts) implementation caps the in-memory history to **1,000 entries** and writes to disk asynchronously in batches every second. Additionally, the on-disk JSON-Lines file is trimmed when it exceeds **5 MiB**, retaining only the most recent tool calls.

### Can I query the audit logs programmatically?

Yes, the [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts) module provides `getRecentCallsFormatted()` to retrieve structured data for UI components or analytics. The function accepts parameters like `maxResults` to limit the returned entries, making it suitable for building monitoring dashboards or debugging interfaces.