How Desktop Commander MCP Audit Logging Works: Inside `claude_tool_call.log`
Desktop Commander MCP writes a JSON audit record for every tool call to ~/.claude-server-commander/claude_tool_call.log, capturing timestamps, client identity, tool names, arguments, and outcomes—with automatic 10 MiB log rotation.
This audit system lets developers trace exactly what AI agents requested, when, and what happened. The implementation spans configuration constants, a dedicated logging utility, and integration points across all tool handlers.
Core Architecture: Three Components
Configuration Layer (src/config.ts)
The log file location is defined centrally in src/config.ts via TOOL_CALL_FILE. This constant joins CONFIG_DIR (set to ~/.claude-server-commander) with the filename claude_tool_call.log.
// src/config.ts – simplified excerpt
import path from 'path';
import os from 'os';
export const CONFIG_DIR = path.join(os.homedir(), '.claude-server-commander');
export const TOOL_CALL_FILE = path.join(CONFIG_DIR, 'claude_tool_call.log');
See src/config.ts#L6-L10 for the full definition.
Audit Recorder (src/utils/toolHistory.ts)
The logToolCall function in src/utils/toolHistory.ts is the heart of the audit system. Every tool handler imports and calls this function before executing its logic.
Captured fields per record:
- ts – ISO-8601 timestamp from
new Date().toISOString() - client – identifying string for the calling client (e.g.,
claude-desktop,claude-code) - tool – the
toolIdbeing invoked - args – serialized arguments object
- result –
"ok"for success, or"error"with accompanying error message
Source: src/utils/toolHistory.ts
Log Rotation (src/utils/fuzzySearchLogger.ts)
To prevent unbounded growth, the logger enforces a 10 MiB size limit. When TOOL_CALL_FILE exceeds this threshold, it is renamed to claude_tool_call.log.old and a fresh file begins. This rotation logic is shared with other logging utilities in the codebase.
// src/utils/fuzzySearchLogger.ts – rotation logic excerpt
const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10 MiB
if (currentSize > MAX_LOG_SIZE) {
const oldPath = `${logPath}.old`;
await rename(logPath, oldPath);
// Continue writing to fresh file
}
Source: src/utils/fuzzySearchLogger.ts#L30-L36
Log Entry Format and Examples
Each tool call produces a single JSON line (newline-delimited JSON), enabling efficient streaming and command-line processing.
Successful tool call
{"ts":"2024-07-19T14:23:11.842Z","client":"claude-desktop","tool":"read_file","args":{"path":"~/projects/app/src/index.ts"},"result":"ok"}
Failed tool call with error details
{"ts":"2024-07-19T14:25:04.113Z","client":"claude-code","tool":"write_file","args":{"path":"/etc/passwd","content":"..."},"result":"error","error":"EACCES: permission denied"}
The newline-delimited format ensures compatibility with standard Unix tools: grep, jq, awk, and log aggregation pipelines.
Working with Audit Logs: Practical Examples
Read recent entries programmatically
import { readFileSync } from 'fs';
import { join } from 'path';
import os from 'os';
const logPath = join(os.homedir(), '.claude-server-commander', 'claude_tool_call.log');
const recent = readFileSync(logPath, 'utf8')
.trim()
.split('\n')
.slice(-10)
.map(line => JSON.parse(line));
console.table(recent.map(r => ({ tool: r.tool, client: r.client, result: r.result })));
Filter for specific tool usage with jq
# All read_file calls, pretty-printed
jq 'select(.tool == "read_file")' ~/.claude-server-commander/claude_tool_call.log
# Count errors by tool
jq -r 'select(.result == "error") | .tool' ~/.claude-server-commander/claude_tool_call.log | sort | uniq -c
Combine current and rotated logs for full history
cat ~/.claude-server-commander/claude_tool_call.log* > full_audit.jsonl
jq '.' full_audit.jsonl | less
Disable audit logging for sensitive sessions
Set DISABLE_AUDIT_LOG to any non-empty value to skip logToolCall writes entirely:
DISABLE_AUDIT_LOG=1 npx @wonderwhy-er/desktop-commander@latest setup
Integration Across Tool Handlers
Every handler in src/handlers/ imports logToolCall and invokes it at entry. For example, terminal-related handlers in terminal-handlers.ts call the logger before spawning processes or executing shell commands.
See src/handlers/terminal-handlers.ts for representative usage patterns.
Summary
- Log location:
~/.claude-server-commander/claude_tool_call.log(defined insrc/config.ts) - Log format: Newline-delimited JSON with
ts,client,tool,args, andresultfields - Rotation trigger: 10 MiB file size, archived to
.oldsuffix - Core implementation:
src/utils/toolHistory.tswith shared rotation fromsrc/utils/fuzzySearchLogger.ts - Opt-out: Set
DISABLE_AUDIT_LOGenvironment variable
Frequently Asked Questions
What happens when claude_tool_call.log reaches 10 MiB?
The current file is renamed to claude_tool_call.log.old and a new empty log begins. Only one archived generation is kept—older .old files are overwritten on subsequent rotations.
Can I change the audit log directory or filename?
Not without modifying the source. The path is hardcoded in src/config.ts as TOOL_CALL_FILE. To relocate logs, fork the repository and adjust the CONFIG_DIR or TOOL_CALL_FILE constants.
Does the audit log capture the full output of tool executions?
No. The audit log records arguments provided and success/failure status, not the complete stdout/stderr or return values. For full execution transcripts, inspect individual tool implementations or enable additional debugging at the client level.
How do I search audit logs for a specific time range?
Use jq with date filtering on the ts field:
jq 'select(.ts >= "2024-07-19T10:00:00Z" and .ts < "2024-07-19T15:00:00Z")' \
~/.claude-server-commander/claude_tool_call.log
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 →