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);
    }
Install Server

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