get_worklogs
Retrieve time tracking worklogs from JIRA Tempo for a specified date range, optionally filtered by issue key, to review logged hours and activities.
Instructions
Retrieve worklogs for authenticated user and date range
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format (optional, defaults to startDate) | |
| issueKey | No | Optional filter by specific issue key (e.g., PROJ-1234) | |
| startDate | Yes | Start date in YYYY-MM-DD format |
Input Schema (JSON Schema)
{
"properties": {
"endDate": {
"description": "End date in YYYY-MM-DD format (optional, defaults to startDate)",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": "string"
},
"issueKey": {
"description": "Optional filter by specific issue key (e.g., PROJ-1234)",
"type": "string"
},
"startDate": {
"description": "Start date in YYYY-MM-DD format",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": "string"
}
},
"required": [
"startDate"
],
"type": "object"
}
Implementation Reference
- src/tools/get-worklogs.ts:15-149 (handler)Main handler function that fetches, processes, and formats worklogs from Tempo API for the given date range and optional issue key. Generates a concise markdown summary with totals, issue groups, and recent entries.export async function getWorklogs( tempoClient: TempoClient, input: GetWorklogsInput ): Promise<CallToolResult> { try { const { startDate, endDate, issueKey } = input; // Use endDate or default to startDate const actualEndDate = endDate || startDate; // Fetch worklogs from Tempo API (automatically filters by authenticated user) const worklogResponses = await tempoClient.getWorklogs({ from: startDate, to: actualEndDate, issueKey: issueKey }); // Process and format the worklogs const worklogs: TempoWorklog[] = worklogResponses.map((response: TempoWorklogResponse) => ({ id: response.tempoWorklogId?.toString() || response.id || 'unknown', issueKey: response.issue.key, issueSummary: response.issue.summary, timeSpentSeconds: response.timeSpentSeconds, billableSeconds: response.billableSeconds, started: response.started, worker: response.worker, attributes: response.attributes || {}, timeSpent: response.timeSpent, comment: response.comment || '' })); // Calculate total hours const totalSeconds = worklogs.reduce((sum, worklog) => sum + worklog.timeSpentSeconds, 0); const totalHours = Math.round((totalSeconds / 3600) * 100) / 100; // Round to 2 decimal places const result: GetWorklogsResponse = { worklogs, totalHours }; // Format response for display - CONCISE VERSION let displayText = `## Your Worklogs (${startDate}`; if (endDate && endDate !== startDate) { displayText += ` to ${endDate}`; } displayText += `)\n\n`; if (issueKey) { displayText += `**Issue:** ${issueKey}\n\n`; } if (worklogs.length === 0) { displayText += "No worklogs found.\n"; } else { displayText += `**Total:** ${totalHours}h (${worklogs.length} entries)\n\n`; // Group by issue for summary (more concise) const issueGroups = worklogs.reduce((acc, worklog) => { if (!acc[worklog.issueKey]) { acc[worklog.issueKey] = { totalSeconds: 0, entries: 0 }; } acc[worklog.issueKey].totalSeconds += worklog.timeSpentSeconds; acc[worklog.issueKey].entries += 1; return acc; }, {} as Record<string, { totalSeconds: number; entries: number }>); // Show summary by issue with issue summary for (const [key, data] of Object.entries(issueGroups)) { const hours = (data.totalSeconds / 3600).toFixed(1); // Get issue summary from first worklog with this key const sampleWorklog = worklogs.find(w => w.issueKey === key); const issueSummary = sampleWorklog?.issueSummary || ''; displayText += `β’ **${key}** (${issueSummary}): ${hours}h (${data.entries} entries)\n`; } // Show recent entries (limit to 100 most recent) const recentWorklogs = worklogs .sort((a, b) => b.started.localeCompare(a.started)) .slice(0, 100); if (recentWorklogs.length > 0) { displayText += `\n**Recent Entries:**\n`; for (const worklog of recentWorklogs) { // Extract date part from datetime string (handles both "2025-09-12 00:00:00.000" and "2025-09-12T00:00:00.000") const datePart = worklog.started.split(/[T\s]/)[0]; // Parse and format the human-readable date const parsedDate = parseISO(datePart); const humanReadableDate = format(parsedDate, "EEEE, MMMM do, yyyy"); // Create hybrid format: ISO date + human-readable in parentheses const formattedDate = `${datePart} (${humanReadableDate})`; const hours = (worklog.timeSpentSeconds / 3600).toFixed(1); let entryText = `β’ ${formattedDate}: **${worklog.issueKey}** (${worklog.issueSummary}) - ${hours}h - [ID: ${worklog.id}]`; if (worklog.comment && worklog.comment.trim()) { entryText += ` - "${worklog.comment}"`; } displayText += `${entryText}\n`; } } if (worklogs.length > 100) { displayText += `\n*Showing 100 most recent of ${worklogs.length} total entries*\n`; displayText += `*π‘ Tip: Use a shorter date range or specific issueKey filter for more targeted results*\n`; } } return { content: [ { type: "text", text: displayText } ], isError: false }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error retrieving worklogs: ${errorMessage}` } ], isError: true }; } }
- src/types/mcp.ts:8-12 (schema)Zod schema for validating GetWorklogs tool input parameters: startDate (required YYYY-MM-DD), optional endDate and issueKey.export const GetWorklogsInputSchema = z.object({ startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Start date must be in YYYY-MM-DD format"), endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "End date must be in YYYY-MM-DD format").optional(), issueKey: z.string().optional(), });
- src/index.ts:72-95 (registration)Tool registration in MCP ListTools handler: defines name, description, and input schema for get_worklogs.{ name: TOOL_NAMES.GET_WORKLOGS, description: "Retrieve worklogs for authenticated user and date range", inputSchema: { type: "object", properties: { startDate: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$", description: "Start date in YYYY-MM-DD format", }, endDate: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$", description: "End date in YYYY-MM-DD format (optional, defaults to startDate)", }, issueKey: { type: "string", description: "Optional filter by specific issue key (e.g., PROJ-1234)", }, }, required: ["startDate"], }, },
- src/index.ts:221-224 (registration)Tool execution handler in MCP CallToolRequestSchema switch case: parses input with schema and calls the getWorklogs handler.case TOOL_NAMES.GET_WORKLOGS: { const input = GetWorklogsInputSchema.parse(args); return await getWorklogs(tempoClient, input); }
- src/types/tempo.ts:100-111 (schema)TypeScript interfaces defining input parameters and response structure for getWorklogs, used in TempoClient and handler.export interface GetWorklogsParams { user: string; startDate: string; // ISO date (YYYY-MM-DD) endDate?: string; // ISO date, defaults to startDate issueKey?: string; // Optional filter by specific issue } // Get worklogs response export interface GetWorklogsResponse { worklogs: TempoWorklog[]; totalHours: number; }