How the Audit Logging System in DesktopCommander Tracks and Rotates Tool Usage Logs

DesktopCommander records every tool invocation via the trackTools utility in src/utils/trackTools.ts, automatically rotating logs when they reach 10 MiB by renaming them with timestamps and creating fresh log files.

The audit logging system in DesktopCommander provides a complete forensic trail of tool executions for debugging and security auditing. Implemented in the wonderwhy-er/DesktopCommanderMCP repository, this lightweight mechanism captures metadata for every search, file edit, and PDF generation while preventing disk space exhaustion through automatic size-based rotation.

How DesktopCommander Records Tool Invocations

The trackTools Utility

The core implementation lives in src/utils/trackTools.ts, which exports the trackToolCall() function. This utility is invoked from src/server.ts and other entry points immediately when a tool begins execution. Before writing any entry, the module checks log file size against the LOG_MAX_SIZE constant (10 MiB) to determine if rotation is required.

Audit Entry Structure

Each log entry is a JSON object containing:

  • Tool name – identifies which command was invoked (e.g., search, edit)
  • Request ID – a unique UUID generated via crypto.randomUUID()
  • Start timestamp – ISO 8601 timestamp from new Date().toISOString()
  • Invoking user – user context when available
  • Description – human-readable summary of the operation

These entries append to a plain-text log file located at ${HOME}/.desktop-commander/logs/desktop-commander-tools.log.

Automatic Log Rotation at 10 MiB

Size-Based Rotation Logic

The rotation algorithm runs synchronously before every write operation. Inside src/utils/trackTools.ts, the system calls fs.promises.stat() to check the current file size. When the log exceeds 10 MiB (10 * 1024 * 1024 bytes), the module executes maybeRotateLog():

// Simplified logic from src/utils/trackTools.ts
const LOG_MAX_SIZE = 10 * 1024 * 1024; // 10 MiB

async function maybeRotateLog(filePath: string) {
  const { size } = await fs.promises.stat(filePath);
  if (size < LOG_MAX_SIZE) return;

  const date = new Date();
  const rotateTimestamp = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}_${String(date.getHours()).padStart(2, '0')}-${String(date.getMinutes()).padStart(2, '0')}-${String(date.getSeconds()).padStart(2, '0')}`;
  
  const { dir, name, ext } = path.parse(filePath);
  const newFileName = path.join(dir, `${name}_${rotateTimestamp}${ext}`);
  
  await fs.promises.rename(filePath, newFileName);
  await fs.promises.writeFile(filePath, ''); // start fresh
}

This synchronous-before-write approach guarantees that no audit entry is lost during rotation.

Rotation File Naming Convention

Rotated archives retain the original base name with an appended timestamp suffix. For example, a rotation occurring on August 15, 2024 at 14:30:02 produces desktop-commander-tools_2024-08-15_14-30-02.log. The path.parse() method ensures the extension and directory structure remain consistent.

Implementation Details and Code Examples

Logging a tool call from your implementation:

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

async function runSearch(query: string) {
  const requestId = crypto.randomUUID();
  await trackToolCall({
    requestId,
    tool: 'search',
    description: `User search for "${query}"`,
    startTime: new Date().toISOString(),
  });
  // … perform the actual search …
}

Inspecting logs manually:


# Show the most recent 20 entries

tail -n 20 ~/.desktop-commander/logs/desktop-commander-tools.log

# List rotated archives (older than the active file)

ls -1 ~/.desktop-commander/logs/desktop-commander-tools_*.log

Triggering rotation for testing:

import { writeFile } from 'fs/promises';

const LOG_PATH = `${process.env.HOME}/.desktop-commander/logs/desktop-commander-tools.log`;

// Fill the log file to exceed 10 MiB threshold
await writeFile(LOG_PATH, 'x'.repeat(10 * 1024 * 1024 + 1));

Integration with the Logger Module

The audit system complements the logger module defined in src/utils/logger.ts. While trackTools handles structured tool-invocation audits, the logger provides general application logging and a logToStderr fallback for critical errors. The src/index.ts file initializes global error handlers that route uncaught exceptions through this logger, ensuring that catastrophic failures are captured even if the audit stream is unavailable.

Key files in the audit pipeline:

  • src/utils/trackTools.ts – Core audit-logging implementation; writes entries and rotates logs at 10 MiB
  • src/utils/logger.ts – Provides the logger object and logToStderr fallback for critical errors
  • src/server.ts – Imports trackToolCall and forwards metadata from the HTTP server layer
  • src/index.ts – Sets up global error handlers that route exceptions through the logger

Summary

  • DesktopCommander writes JSON audit entries via trackToolCall() in src/utils/trackTools.ts every time a tool executes.
  • Log files reside at ${HOME}/.desktop-commander/logs/desktop-commander-tools.log and store tool names, request IDs, timestamps, and operation descriptions.
  • Automatic rotation occurs when the log reaches 10 MiB, renaming the file with a YYYY-MM-DD_HH-MM-SS timestamp suffix and creating a fresh log.
  • The rotation logic runs synchronously before each write, preventing data loss during file rollover.
  • The separate logger module in src/utils/logger.ts handles general application errors and stderr fallback.

Frequently Asked Questions

Where are audit logs stored in DesktopCommander?

Audit logs are stored in the user's home directory at ${HOME}/.desktop-commander/logs/desktop-commander-tools.log. Rotated archives remain in the same directory with timestamped suffixes (e.g., desktop-commander-tools_2024-08-15_14-30-02.log).

What triggers automatic log rotation?

Rotation triggers when the active log file reaches 10 MiB (10,485,760 bytes). The system checks file size before every write operation using fs.promises.stat(), ensuring that logs rotate immediately upon hitting the threshold without losing the triggering entry.

How can I parse audit logs programmatically?

Since entries are plain JSON lines, you can parse them using standard streaming JSON parsers. Each line represents a separate tool invocation object containing tool, requestId, startTime, and description fields. For example, use readline in Node.js to stream the file line-by-line and JSON.parse() each entry.

Is the audit logging system configurable?

The current implementation in wonderwhy-er/DesktopCommanderMCP uses hardcoded constants for the log path (desktop-commander-tools.log) and rotation size (10 MiB). To modify these behaviors, you must edit the LOG_MAX_SIZE constant and path variables directly in src/utils/trackTools.ts before building the application.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →