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
| Name | Required | Description | Default |
|---|---|---|---|
| startDate | Yes | Start date in ISO 8601 format (YYYY-MM-DD) | |
| endDate | Yes | End date in ISO 8601 format (YYYY-MM-DD) | |
| projectId | No | Optional project ID to filter activities for a specific project |
Implementation Reference
- src/tools/activitiesTools.ts:31-67 (handler)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'}`; } } };
- src/tools/activitiesTools.ts:21-25 (schema)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 ];
- src/services/mocoApi.ts:143-154 (helper)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); }
- src/tools/activitiesTools.ts:73-147 (helper)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) }; }