Skip to main content
Glama
niondigital

MoCo MCP Server

by niondigital

get_activities

Retrieve and summarize time tracking activities by date, project, and task within a specified date range. Filter results by project ID to focus on specific work data.

Instructions

Get all activities within a date range with automatic summation by date, project, and task. Optionally filter by project ID.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
startDateYesStart date in ISO 8601 format (YYYY-MM-DD)
endDateYesEnd date in ISO 8601 format (YYYY-MM-DD)
projectIdNoOptional project ID to filter activities for a specific project

Implementation Reference

  • The main handler for the get_activities tool. Validates input dates, fetches activities from Moco API, aggregates them by date/project/task, formats the summary, and returns a formatted string response.
    export const getActivitiesTool = {
      name: 'get_activities',
      description: 'Get all activities within a date range with automatic summation by date, project, and task. Optionally filter by project ID.',
      inputSchema: zodToJsonSchema(GetActivitiesSchema),
      handler: async (params: z.infer<typeof GetActivitiesSchema>): Promise<string> => {
        const { startDate, endDate, projectId } = params;
    
        // Validate date format and range
        if (!validateDateRange(startDate, endDate)) {
          return createValidationErrorMessage({
            field: 'dateRange',
            value: `${startDate} to ${endDate}`,
            reason: 'invalid_date_range'
          });
        }
    
        try {
          const apiService = new MocoApiService();
          const activities = await apiService.getActivities(startDate, endDate, projectId);
    
          if (activities.length === 0) {
            return createEmptyResultMessage({
              type: 'activities',
              startDate,
              endDate,
              projectId
            });
          }
    
          const summary = aggregateActivities(activities, startDate, endDate);
          return formatActivitiesSummary(summary, projectId);
    
        } catch (error) {
          return `Error retrieving activities: ${error instanceof Error ? error.message : 'Unknown error'}`;
        }
      }
    };
  • Zod schema defining the input parameters for the get_activities tool: required startDate and endDate strings, optional positive projectId number.
    const GetActivitiesSchema = z.object({
      startDate: z.string().describe('Start date in ISO 8601 format (YYYY-MM-DD)'),
      endDate: z.string().describe('End date in ISO 8601 format (YYYY-MM-DD)'),
      projectId: z.number().positive().optional().describe('Optional project ID to filter activities for a specific project')
    });
  • src/index.ts:34-42 (registration)
    The getActivitiesTool is imported and included in the AVAILABLE_TOOLS array, which is used by the MCP server to list and dispatch tool calls.
    const AVAILABLE_TOOLS = [
      getActivitiesTool,
      getUserProjectsTool,
      getUserProjectTasksTool,
      getUserHolidaysTool,
      getUserPresencesTool,
      getUserSickDaysTool,
      getPublicHolidaysTool
    ];
  • MocoApiService method that fetches activities from the MoCo API endpoint '/activities' with date range and optional project filter, handling pagination automatically.
    async getActivities(startDate: string, endDate: string, projectId?: number): Promise<Activity[]> {
      const params: Record<string, string | number> = {
        from: startDate,
        to: endDate
      };
      
      if (projectId) {
        params.project_id = projectId;
      }
      
      return this.fetchAllPages<Activity>('/activities', params);
    }
  • Helper function that aggregates raw activities into structured summaries: daily breakdowns, project totals across days, task breakdowns, and grand total.
    function aggregateActivities(activities: Activity[], startDate: string, endDate: string): ActivityRangeSummary {
      // Group activities by date
      const activitiesByDate = new Map<string, Activity[]>();
      
      activities.forEach(activity => {
        if (!activitiesByDate.has(activity.date)) {
          activitiesByDate.set(activity.date, []);
        }
        activitiesByDate.get(activity.date)!.push(activity);
      });
    
      // Create daily summaries
      const dailySummaries: DailyActivitySummary[] = [];
      const projectTotalsMap = new Map<number, {
        projectName: string;
        totalHours: number;
        tasks: Map<number, { taskName: string; totalHours: number }>;
      }>();
    
      // Sort dates for consistent output
      const sortedDates = Array.from(activitiesByDate.keys()).sort();
      
      sortedDates.forEach(date => {
        const dayActivities = activitiesByDate.get(date)!;
        const dailySummary = createDailySummary(date, dayActivities);
        dailySummaries.push(dailySummary);
    
        // Accumulate project totals across all days
        dailySummary.projects.forEach(project => {
          if (!projectTotalsMap.has(project.projectId)) {
            projectTotalsMap.set(project.projectId, {
              projectName: project.projectName,
              totalHours: 0,
              tasks: new Map()
            });
          }
    
          const projectTotal = projectTotalsMap.get(project.projectId)!;
          projectTotal.totalHours += project.projectTotal.hours;
    
          project.tasks.forEach(task => {
            if (!projectTotal.tasks.has(task.taskId)) {
              projectTotal.tasks.set(task.taskId, {
                taskName: task.taskName,
                totalHours: 0
              });
            }
            projectTotal.tasks.get(task.taskId)!.totalHours += task.hours;
          });
        });
      });
    
      // Convert project totals map to array format
      const projectTotals = Array.from(projectTotalsMap.entries()).map(([projectId, data]) => ({
        projectId,
        projectName: data.projectName,
        total: createTimeFormat(data.totalHours),
        tasks: Array.from(data.tasks.entries()).map(([taskId, taskData]) => ({
          taskId,
          taskName: taskData.taskName,
          total: createTimeFormat(taskData.totalHours)
        }))
      }));
    
      // Calculate grand total
      const grandTotalHours = sumHours(dailySummaries.map(day => day.dailyTotal.hours));
    
      return {
        startDate,
        endDate,
        dailySummaries,
        projectTotals,
        grandTotal: createTimeFormat(grandTotalHours)
      };
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses key behavioral traits: it retrieves activities, applies automatic summation by date/project/task, and allows optional project filtering. However, it doesn't cover aspects like pagination, rate limits, authentication needs, or error handling, which are important for a read operation with potential large datasets.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with two sentences: the first states the core purpose and key feature (automatic summation), and the second adds optional filtering. It's front-loaded with essential information and avoids redundancy, though it could be slightly more structured (e.g., clarifying the summation output).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (read operation with summation and filtering), no annotations, and no output schema, the description is moderately complete. It covers the main action and parameters but lacks details on output format (e.g., how summation is presented), error cases, or performance considerations, which would help an agent use it effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents the parameters (startDate, endDate, projectId). The description adds minimal value beyond the schema by mentioning 'date range' and 'filter by project ID,' but doesn't provide additional semantics like format nuances or usage tips. This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get all activities within a date range with automatic summation by date, project, and task.' It specifies the verb ('Get'), resource ('activities'), and key behavior ('automatic summation'). However, it doesn't explicitly differentiate from sibling tools like 'get_user_projects' or 'get_user_project_tasks', which appear related but serve different purposes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by mentioning 'within a date range' and 'Optionally filter by project ID,' suggesting it's for retrieving summarized activity data. However, it lacks explicit guidance on when to use this tool versus alternatives (e.g., vs. 'get_user_projects' for project details) or any exclusions (e.g., not for real-time data). This leaves some ambiguity in context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/niondigital/moco-mcp'

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