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 MiBsrc/utils/logger.ts– Provides theloggerobject andlogToStderrfallback for critical errorssrc/server.ts– ImportstrackToolCalland forwards metadata from the HTTP server layersrc/index.ts– Sets up global error handlers that route exceptions through the logger
Summary
- DesktopCommander writes JSON audit entries via
trackToolCall()insrc/utils/trackTools.tsevery time a tool executes. - Log files reside at
${HOME}/.desktop-commander/logs/desktop-commander-tools.logand 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-SStimestamp suffix and creating a fresh log. - The rotation logic runs synchronously before each write, preventing data loss during file rollover.
- The separate
loggermodule insrc/utils/logger.tshandles 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →