Desktop Commander MCP Audit Logging: How Tool Call Tracking Works
Desktop Commander MCP records every tool invocation to ~/.claude-server-commander/claude_tool_call.log with automatic 10 MB rotation, plus an optional bounded JSON-Lines history for UI inspection.
The audit logging mechanism in Desktop Commander MCP provides forensic visibility into every tool interaction. According to the wonderwhy-er/DesktopCommanderMCP source code, the system uses a dual-track approach: a raw append-only text log for debugging and a structured history file for quick UI access.
Log File Location and Configuration
All audit logs reside in a dedicated configuration directory under the user home folder.
In src/config.ts, the following constants define the primary audit log:
export const TOOL_CALL_FILE = path.join(CONFIG_DIR, 'claude_tool_call.log');
export const TOOL_CALL_FILE_MAX_SIZE = 1024 * 1024 * 10; // 10 MB
The CONFIG_DIR expands to ~/.claude-server-commander on Linux/macOS or the equivalent path on Windows. Therefore, the canonical audit log location is:
$HOME/.claude-server-commander/claude_tool_call.log
How the Audit Logger Writes Entries
The core audit function lives in src/utils/trackTools.ts. The trackToolCall(toolName, args?) function handles every logged invocation:
export async function trackToolCall(toolName: string, args?: unknown): Promise<void> {
const timestamp = new Date().toISOString();
const logEntry = `${timestamp} | ${toolName.padEnd(20, ' ')}${args ? `\t| Arguments: ${JSON.stringify(args)}` : ''}\n`;
// …size check & rotation…
await fs.promises.appendFile(TOOL_CALL_FILE, logEntry, 'utf8');
}
Each entry contains:
- ISO-8601 timestamp for precise temporal ordering
- Tool name padded to 20 characters for columnar alignment
- JSON-encoded arguments (optional, when provided)
Log Rotation Behavior
Before writing, trackToolCall checks if the current log exceeds 10 MB. If so, it performs rotation:
- Renames
claude_tool_call.logtoclaude_tool_call_YYYY-MM-DD_HH-MM-SS.log - Creates a fresh empty log file
- Continues appending new entries
This prevents unbounded growth while preserving historical audit data.
Failure-Resilient Design
Errors during logging never block primary operations. If appendFile throws, the error is reported via telemetry and silently ignored—tool execution continues uninterrupted.
Tool Call History for UI Inspection
A separate system in src/utils/toolHistory.ts maintains tool-history.jsonl in the same directory. This JSON-Lines store keeps approximately 1,000 recent entries with full call metadata including results.
| File | Purpose | Format |
|---|---|---|
claude_tool_call.log |
Authoritative audit trail for debugging | Plain text, append-only |
tool-history.jsonl |
Bounded history for UI display | JSON-Lines, ~1000 entries |
The history file serves the Desktop Commander UI; the raw audit log remains the source of truth for forensic analysis.
Working with Audit Logs Programmatically
Logging a Tool Call from Custom Code
import { trackToolCall } from './utils/trackTools.js';
await trackToolCall('read_file', { path: '/tmp/example.txt' });
Reading the Current Audit Log
import { TOOL_CALL_FILE } from './config.js';
import { readFile } from 'fs/promises';
async function showAuditLog() {
const content = await readFile(TOOL_CALL_FILE, 'utf-8');
console.log(content);
}
Forcing Log Rotation for Testing
import { TOOL_CALL_FILE_MAX_SIZE, TOOL_CALL_FILE } from './config.js';
import { rename, stat, appendFile } from 'fs/promises';
import path from 'path';
async function forceRotation() {
const dir = path.dirname(TOOL_CALL_FILE);
const base = path.basename(TOOL_CALL_FILE, '.log');
const now = new Date();
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}_${String(now.getHours()).padStart(2, '0')}-${String(now.getMinutes()).padStart(2, '0')}-${String(now.getSeconds()).padStart(2, '0')}`;
const rotated = path.join(dir, `${base}_${ts}.log`);
await rename(TOOL_CALL_FILE, rotated);
await appendFile(TOOL_CALL_FILE, '', 'utf8');
}
Summary
- Primary audit log:
~/.claude-server-commander/claude_tool_call.log(10 MB rotation) - Core implementation:
trackToolCall()insrc/utils/trackTools.ts - Configuration:
src/config.tsdefines paths and size limits - Secondary history:
tool-history.jsonlfor UI access to ~1,000 recent calls - Design priorities: Non-blocking writes, automatic rotation, failure resilience
Frequently Asked Questions
Where exactly are Desktop Commander MCP tool call logs stored?
The audit log resides at $HOME/.claude-server-commander/claude_tool_call.log. On Windows, this maps to %USERPROFILE%\.claude-server-commander\claude_tool_call.log. A separate UI-oriented history file exists at tool-history.jsonl in the same directory.
What happens when the audit log reaches 10 MB?
The trackToolCall function automatically rotates the file. It renames the current log with a timestamp suffix (e.g., claude_tool_call_2024-11-03_14-27-55.log) and starts a fresh empty log. This prevents disk exhaustion while preserving prior audit data.
Can audit logging be disabled?
The source code in wonderwhy-er/DesktopCommanderMCP does not expose a configuration flag to disable trackToolCall. Since errors are caught and ignored, logging fails gracefully without affecting operations. To effectively disable, you would need to modify src/utils/trackTools.ts or redirect TOOL_CALL_FILE to /dev/null.
How does the tool history differ from the audit log?
The tool history (tool-history.jsonl) is a structured JSON-Lines file with a ~1,000 entry limit used by the Desktop Commander UI. The audit log (claude_tool_call.log) is an unlimited, human-readable text file designed for forensic debugging and compliance tracing.
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 →