How Desktop Commander Implements Audit Logging for Tool Calls: A Two-Layer Approach
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, 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. 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.
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 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.
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, which immediately invokes trackToolCall(name, args) for every tool invocation. The centralized configuration in src/config.ts ensures consistent file paths and size limits across both logging layers, while 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:
// 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.tsand structured JSON-Lines history insrc/utils/toolHistory.ts. - Plain-text logs rotate automatically at 10 MiB (
TOOL_CALL_FILE_MAX_SIZEinsrc/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, which triggerstrackToolCall()for immediate audit capture. - Both layers store data in the
.claude-server-commanderdirectory 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. 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 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 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.
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 →