UNPKG

@pavi_thran_7/jira-mcp-server

Version:

MCP server for Jira integration with ticket creation and time logging

216 lines (190 loc) 5.89 kB
/** * @fileoverview Work logging tools for Jira MCP server */ /** * @typedef {import('../types/jira.js').LogWorkParams} LogWorkParams * @typedef {import('../types/jira.js').McpToolResult} McpToolResult * @typedef {import('../jira-client.js').JiraClient} JiraClient */ /** * Log work against a Jira ticket * @param {JiraClient} jiraClient - Jira client instance * @param {LogWorkParams} params - Work logging parameters * @returns {Promise<McpToolResult>} Work logging result */ export async function logWork(jiraClient, params) { try { // Validate required parameters if (!params.issueKey || !params.timeSpent) { return { content: [ { type: 'text', text: 'Error: Missing required parameters. Please provide issueKey and timeSpent.', }, ], isError: true, }; } // Validate time format const timeRegex = /^(\d+[wdhm]\s*)+$/i; if (!timeRegex.test(params.timeSpent.trim())) { return { content: [ { type: 'text', text: 'Error: Invalid time format. Use formats like "2h 30m", "1d", "45m", "1w 2d 3h 30m".', }, ], isError: true, }; } // Check if issue exists const issueResult = await jiraClient.getIssue(params.issueKey); if (!issueResult.success) { return { content: [ { type: 'text', text: `Error: Issue '${params.issueKey}' not found or not accessible: ${issueResult.error}`, }, ], isError: true, }; } const issue = issueResult.data; // Build worklog data const worklogData = { timeSpent: params.timeSpent, comment: params.comment || 'Work logged via MCP', }; // Add start time if provided if (params.started) { try { const startDate = new Date(params.started); if (isNaN(startDate.getTime())) { return { content: [ { type: 'text', text: 'Error: Invalid started date format. Please use ISO format (e.g., "2023-12-01T09:00:00.000Z").', }, ], isError: true, }; } worklogData.started = startDate.toISOString(); } catch (error) { return { content: [ { type: 'text', text: `Error: Invalid started date: ${error.message}`, }, ], isError: true, }; } } else { // Default to current time worklogData.started = new Date().toISOString(); } // Add billing account if provided if (params.billingAccountId) { // Note: Billing account handling depends on Jira configuration // This is a placeholder for billing account integration worklogData.attributes = { _BillingAccount_: params.billingAccountId, }; } // Create the worklog const worklogResult = await jiraClient.addWorklog(params.issueKey, worklogData); if (!worklogResult.success) { return { content: [ { type: 'text', text: `Error logging work: ${worklogResult.error}`, }, ], isError: true, }; } const worklog = worklogResult.data; const issueUrl = `${jiraClient.baseUrl}/browse/${params.issueKey}`; // Format the response const startedTime = new Date(worklog.started).toLocaleString(); const billingInfo = params.billingAccountId ? `\n- Billing Account: ${params.billingAccountId}` : ''; return { content: [ { type: 'text', text: `⏰ Successfully logged work! **Work Log Details:** - Issue: ${params.issueKey} - ${issue.fields.summary} - Time Spent: ${params.timeSpent} - Started: ${startedTime} - Comment: ${params.comment || 'Work logged via MCP'}${billingInfo} - Work Log ID: ${worklog.id} **Issue URL:** ${issueUrl} The work has been logged and will be reflected in your time tracking reports.`, }, ], isError: false, }; } catch (error) { return { content: [ { type: 'text', text: `Error logging work: ${error.message}`, }, ], isError: true, }; } } /** * Parse time string into seconds * @param {string} timeString - Time string (e.g., "2h 30m") * @returns {number} Time in seconds */ function parseTimeToSeconds(timeString) { const timeRegex = /(\d+)([wdhm])/gi; let totalSeconds = 0; let match; const multipliers = { w: 7 * 24 * 60 * 60, // week d: 24 * 60 * 60, // day h: 60 * 60, // hour m: 60, // minute }; while ((match = timeRegex.exec(timeString)) !== null) { const value = parseInt(match[1]); const unit = match[2].toLowerCase(); totalSeconds += value * (multipliers[unit] || 0); } return totalSeconds; } /** * Format seconds into human-readable time * @param {number} seconds - Time in seconds * @returns {string} Formatted time string */ function formatSecondsToTime(seconds) { const weeks = Math.floor(seconds / (7 * 24 * 60 * 60)); seconds %= 7 * 24 * 60 * 60; const days = Math.floor(seconds / (24 * 60 * 60)); seconds %= 24 * 60 * 60; const hours = Math.floor(seconds / (60 * 60)); seconds %= 60 * 60; const minutes = Math.floor(seconds / 60); const parts = []; if (weeks > 0) parts.push(`${weeks}w`); if (days > 0) parts.push(`${days}d`); if (hours > 0) parts.push(`${hours}h`); if (minutes > 0) parts.push(`${minutes}m`); return parts.join(' ') || '0m'; }