How Desktop Commander MCP Implements Audit Logging for Tool Calls: Inside `claude_tool_call.log`
Desktop Commander MCP records every AI tool invocation as structured JSON in ~/.claude-server-commander/claude_tool_call.log, automatically rotating the file at 10 MiB to prevent unbounded growth.
Desktop Commander MCP is a Model Context Protocol server that exposes system-level tools to Claude Desktop and other MCP clients. To provide transparency and security, the server implements comprehensive audit logging for tool calls, writing a detailed record of every operation to a local log file. Understanding this mechanism helps administrators monitor AI activity, debug issues, and maintain compliance.
Where Audit Logs Are Configured and Stored
The log destination is defined in src/config.ts. The constant CONFIG_DIR resolves to ~/.claude-server-commander, and TOOL_CALL_FILE concatenates this path with the filename claude_tool_call.log.
// src/config.ts
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');
This ensures the audit trail is stored in a consistent, user-specific location across macOS, Linux, and Windows systems.
How Tool Calls Are Recorded
The actual write operation is handled by src/utils/toolHistory.ts. Every request handler in src/handlers/* invokes the logToolCall function before executing the requested operation, creating an append-only stream of JSON Lines.
Anatomy of a Log Entry
Each line is a self-contained JSON object containing:
- ts: ISO 8601 timestamp of the invocation
- client: Identifier for the MCP client (e.g.,
claude-desktop,claude-code) - tool: The exact tool name being invoked (e.g.,
read_file,execute_command) - args: The deserialized arguments object passed to the tool
- result: Status flag (
okorerror) - error: Optional error message when result is
error
A successful call looks like this:
{"ts":"2024-07-19T14:23:11.842Z","client":"claude-desktop","tool":"read_file","args":{"path":"~/projects/app/src/index.ts"},"result":"ok"}
A failed permission denial appears as:
{"ts":"2024-07-19T14:25:04.113Z","client":"claude-code","tool":"write_file","args":{"path":"/etc/passwd","content":"..."},"result":"error","error":"EACCES: permission denied"}
This structured format allows precise filtering using standard UNIX tools or JSON processors like jq.
Automatic Log Rotation
To prevent the log from consuming excessive disk space, src/utils/toolHistory.ts implements automatic rotation. The logic, shared with src/utils/fuzzySearchLogger.ts, checks the file size before each write.
When claude_tool_call.log exceeds 10 MiB, the current file is renamed to claude_tool_call.log.old, and a fresh log file is started. Only one generation of backup is retained, meaning the .old file is overwritten during the next rotation cycle.
Reading and Filtering Audit Logs
Because the log uses the JSON Lines format (one JSON object per line), you can stream-process it without loading the entire file into memory.
Viewing Recent Activity
# Print the last 10 tool calls with pretty formatting
tail -n 10 ~/.claude-server-commander/claude_tool_call.log | jq .
Filtering by Tool Name
# Show only file read operations
jq 'select(.tool == "read_file")' ~/.claude-server-commander/claude_tool_call.log
Reconstructing Full History
If rotation has occurred, concatenate the current and archived logs to analyze a complete session:
cat ~/.claude-server-commander/claude_tool_call.log* | jq -s 'sort_by(.ts)' > full_audit.json
Programmatic Access
You can also read the log from within Node.js scripts:
import { readFileSync } from 'fs';
import { TOOL_CALL_FILE } from './src/config.js';
const entries = readFileSync(TOOL_CALL_FILE, 'utf8')
.trim()
.split('\n')
.map(line => JSON.parse(line));
console.table(entries.slice(-5));
Disabling Audit Logging
For privacy-sensitive environments or temporary debugging sessions, Desktop Commander respects the DISABLE_AUDIT_LOG environment variable. When set to any non-empty value, the server bypasses the logToolCall write entirely:
DISABLE_AUDIT_LOG=1 npx @wonderwhy-er/desktop-commander@latest
Note that disabling logs removes the audit trail and should only be used when the session activity does not require compliance tracking.
Summary
- Audit logging is implemented in
src/utils/toolHistory.ts, which writes structured JSON to~/.claude-server-commander/claude_tool_call.logas defined insrc/config.ts. - Each log entry captures the timestamp, client identifier, tool name, arguments, and outcome of the invocation.
- Log rotation occurs automatically when the file reaches 10 MiB, archiving the previous content to
.old. - The JSON Lines format enables efficient streaming analysis using tools like
jq,grep, or custom Node.js scripts. - Logging can be disabled via the
DISABLE_AUDIT_LOGenvironment variable for privacy-sensitive use cases.
Frequently Asked Questions
What data is stored in each claude_tool_call.log entry?
Each entry is a JSON object containing an ISO timestamp (ts), the MCP client name (client), the specific tool invoked (tool), the full arguments object (args), and a result status (result). If the tool fails, an error field containing the error message is included. This provides a complete, tamper-evident record of what the AI requested and whether it succeeded.
Where is the audit log file located on my system?
The log resides in your user's home directory under .claude-server-commander/claude_tool_call.log. The exact path is constructed in src/config.ts using path.join(os.homedir(), '.claude-server-commander', 'claude_tool_call.log'). On Windows, this resolves to %USERPROFILE%\.claude-server-commander\claude_tool_call.log.
How do I prevent the log file from growing too large?
The server handles this automatically. When the log exceeds 10 MiB, src/utils/toolHistory.ts renames the current file to claude_tool_call.log.old and starts a fresh log. Only one backup generation is kept. If you need longer retention, you should configure a system-level log rotation daemon (like logrotate on Linux) to archive the .old file before it is overwritten.
Can I disable audit logging entirely?
Yes. Set the environment variable DISABLE_AUDIT_LOG to any non-empty value before starting the server. This causes the logToolCall function in src/utils/toolHistory.ts to skip writing entries, effectively running the server without an audit trail. Use this option with caution in production environments where tool call accountability is required.
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 →