Skip to main content
Glama
nocoo

MCP Work History Server

by nocoo

log_activity

Records AI tool usage with metrics like tokens, duration, and cost to a daily worklog file for tracking and analysis.

Instructions

Log AI tool activity to a daily worklog file with comprehensive metrics

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
tool_nameYesName of the AI tool that performed the activity (e.g., 'Warp', 'Claude Code', 'GitHub Copilot')
log_messageYesDetailed log message describing what was accomplished
ai_modelNoAI model used (e.g., 'gemini-2.5-pro', 'claude-3-sonnet', 'gpt-4')
tokens_usedNoTotal tokens consumed in the request (optional)
input_tokensNoInput tokens used (optional)
output_tokensNoOutput tokens generated (optional)
context_lengthNoContext window length used (optional)
duration_msNoDuration of the operation in milliseconds (optional)
cost_usdNoEstimated cost in USD (optional)
successNoWhether the operation was successful (optional, defaults to true)
error_messageNoError message if operation failed (optional)
tagsNoTags to categorize the activity (e.g., ['coding', 'debugging', 'refactoring']) (optional)

Implementation Reference

  • The `handleLogActivity` function implements the core logic of the 'log_activity' tool. It destructures input arguments, validates required fields, generates timestamps, 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 }; } }
  • The input schema defines the structure and validation for the 'log_activity' tool parameters, including required fields (tool_name, log_message) and optional metrics like tokens, duration, cost, and tags.
    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:37-97 (registration)
    The 'log_activity' tool is registered in the ListToolsRequestSchema handler by including it in the tools array returned, defining its name, description, and input schema.
    { 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"] } }
  • src/index.js:103-105 (registration)
    In the CallToolRequestSchema handler, the tool is dispatched by checking the name and calling the handleLogActivity method.
    if (request.params.name === "log_activity") { return await this.handleLogActivity(request.params.arguments); }

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nocoo/mcp-work-history'

If you have feedback or need assistance with the MCP directory API, please join our Discord server