log_activity
Track AI tool usage by logging activity details like model, tokens, cost, and success status into daily worklog files for comprehensive metrics and analysis.
Instructions
Log AI tool activity to a daily worklog file with comprehensive metrics
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ai_model | No | AI model used (e.g., 'gemini-2.5-pro', 'claude-3-sonnet', 'gpt-4') | |
| context_length | No | Context window length used (optional) | |
| cost_usd | No | Estimated cost in USD (optional) | |
| duration_ms | No | Duration of the operation in milliseconds (optional) | |
| error_message | No | Error message if operation failed (optional) | |
| input_tokens | No | Input tokens used (optional) | |
| log_message | Yes | Detailed log message describing what was accomplished | |
| output_tokens | No | Output tokens generated (optional) | |
| success | No | Whether the operation was successful (optional, defaults to true) | |
| tags | No | Tags to categorize the activity (e.g., ['coding', 'debugging', 'refactoring']) (optional) | |
| tokens_used | No | Total tokens consumed in the request (optional) | |
| tool_name | Yes | Name of the AI tool that performed the activity (e.g., 'Warp', 'Claude Code', 'GitHub Copilot') |
Implementation Reference
- src/index.js:111-237 (handler)The handler function that executes the log_activity tool. It parses arguments, constructs a formatted log entry with optional metadata (tokens, duration, cost, etc.), appends it to a daily Markdown worklog file in the 'logs' directory, and returns a success or error message.async handleLogActivity(args) { try { const { tool_name, log_message, ai_model, tokens_used, input_tokens, output_tokens, context_length, duration_ms, cost_usd, success = true, error_message, tags } = args; if (!tool_name || !log_message) { throw new Error("tool_name and log_message are required"); } const now = new Date(); const dateStr = now.toISOString().split('T')[0]; // YYYY-MM-DD format const timeStr = now.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit' }); const logsDir = path.join(__dirname, '..', 'logs'); const logFileName = `worklog-${dateStr}.md`; const logFilePath = path.join(logsDir, logFileName); // Ensure logs directory exists await fs.mkdir(logsDir, { recursive: true }); let logContent = ''; let fileExists = false; try { await fs.access(logFilePath); fileExists = true; logContent = await fs.readFile(logFilePath, 'utf-8'); } catch (error) { // File doesn't exist, create new content logContent = `# 📝 Work Log - ${dateStr}\n\n`; } // Build the log entry with optional metadata let logEntry = `${timeStr} - ${tool_name}`; if (ai_model) { logEntry += ` (${ai_model})`; } logEntry += `: ${log_message}`; // Add optional metadata const metadata = []; if (tokens_used !== undefined) { metadata.push(`${tokens_used} tokens`); } else if (input_tokens !== undefined || output_tokens !== undefined) { const inTokens = input_tokens || 0; const outTokens = output_tokens || 0; metadata.push(`${inTokens + outTokens} tokens (${inTokens}→${outTokens})`); } if (context_length !== undefined) { metadata.push(`${context_length}k ctx`); } if (duration_ms !== undefined) { const seconds = duration_ms >= 1000 ? `${(duration_ms / 1000).toFixed(1)}s` : `${duration_ms}ms`; metadata.push(seconds); } if (cost_usd !== undefined) { metadata.push(`$${cost_usd.toFixed(4)}`); } if (!success && error_message) { metadata.push(`❌ ${error_message}`); } if (tags && tags.length > 0) { metadata.push(`[${tags.join(', ')}]`); } if (metadata.length > 0) { logEntry += ` (${metadata.join(' | ')})`; } const statusIcon = success ? '✅' : '❌'; const newEntry = `- ${statusIcon} ${logEntry}\n`; // If it's a new file, add it directly. Otherwise, append to existing content if (!fileExists) { logContent += newEntry; } else { // Insert at the end of the file logContent += newEntry; } await fs.writeFile(logFilePath, logContent, 'utf-8'); return { content: [ { type: "text", text: `✅ Activity logged: ${logEntry}${fileExists ? '' : ' (new file created)'}` } ] }; } catch (error) { return { content: [ { type: "text", text: `❌ Error logging activity: ${error.message}` } ], isError: true }; } }
- src/index.js:40-96 (schema)Input schema for the log_activity tool, defining required (tool_name, log_message) and optional parameters for logging AI activity metrics.inputSchema: { type: "object", properties: { tool_name: { type: "string", description: "Name of the AI tool that performed the activity (e.g., 'Warp', 'Claude Code', 'GitHub Copilot')" }, log_message: { type: "string", description: "Detailed log message describing what was accomplished" }, ai_model: { type: "string", description: "AI model used (e.g., 'gemini-2.5-pro', 'claude-3-sonnet', 'gpt-4')" }, tokens_used: { type: "number", description: "Total tokens consumed in the request (optional)" }, input_tokens: { type: "number", description: "Input tokens used (optional)" }, output_tokens: { type: "number", description: "Output tokens generated (optional)" }, context_length: { type: "number", description: "Context window length used (optional)" }, duration_ms: { type: "number", description: "Duration of the operation in milliseconds (optional)" }, cost_usd: { type: "number", description: "Estimated cost in USD (optional)" }, success: { type: "boolean", description: "Whether the operation was successful (optional, defaults to true)" }, error_message: { type: "string", description: "Error message if operation failed (optional)" }, tags: { type: "array", items: { type: "string" }, description: "Tags to categorize the activity (e.g., ['coding', 'debugging', 'refactoring']) (optional)" } }, required: ["tool_name", "log_message"] }
- src/index.js:102-108 (registration)Registration of the CallToolRequest handler that routes 'log_activity' calls to the handleLogActivity method.this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "log_activity") { return await this.handleLogActivity(request.params.arguments); } throw new Error(`Unknown tool: ${request.params.name}`); });
- src/index.js:35-99 (registration)Tool registration in the ListToolsRequest response, including name, description, and inputSchema for log_activity.return { tools: [ { name: "log_activity", description: "Log AI tool activity to a daily worklog file with comprehensive metrics", inputSchema: { type: "object", properties: { tool_name: { type: "string", description: "Name of the AI tool that performed the activity (e.g., 'Warp', 'Claude Code', 'GitHub Copilot')" }, log_message: { type: "string", description: "Detailed log message describing what was accomplished" }, ai_model: { type: "string", description: "AI model used (e.g., 'gemini-2.5-pro', 'claude-3-sonnet', 'gpt-4')" }, tokens_used: { type: "number", description: "Total tokens consumed in the request (optional)" }, input_tokens: { type: "number", description: "Input tokens used (optional)" }, output_tokens: { type: "number", description: "Output tokens generated (optional)" }, context_length: { type: "number", description: "Context window length used (optional)" }, duration_ms: { type: "number", description: "Duration of the operation in milliseconds (optional)" }, cost_usd: { type: "number", description: "Estimated cost in USD (optional)" }, success: { type: "boolean", description: "Whether the operation was successful (optional, defaults to true)" }, error_message: { type: "string", description: "Error message if operation failed (optional)" }, tags: { type: "array", items: { type: "string" }, description: "Tags to categorize the activity (e.g., ['coding', 'debugging', 'refactoring']) (optional)" } }, required: ["tool_name", "log_message"] } } ] };