UNPKG

@ivelin-web/tempo-mcp-server

Version:

MCP server for managing Tempo worklogs in Jira

107 lines (106 loc) 3.8 kB
import axios from 'axios'; import { issueIdSchema, idOrKeySchema } from './types.js'; import config from './config.js'; // Build authorization header based on auth type function getAuthHeader() { if (config.jiraApi.authType === 'bearer') { return `Bearer ${config.jiraApi.token}`; } // Basic auth (default) return `Basic ${Buffer.from(`${config.jiraApi.email}:${config.jiraApi.token}`).toString('base64')}`; } // Jira API client with authentication const jiraApi = axios.create({ baseURL: config.jiraApi.baseUrl, headers: { Authorization: getAuthHeader(), Accept: 'application/json', 'Content-Type': 'application/json', }, }); // Standardized error handling for Jira API function formatJiraError(error, context) { if (axios.isAxiosError(error)) { const statusCode = error.response?.status; const message = error.response?.data?.message || error.response?.data?.errorMessages?.join(', ') || error.message; return new Error(`${context}: ${statusCode} - ${message}`); } return new Error(`${context}: ${error.message}`); } /** * Get user's account ID. * - For Bearer auth: uses /myself endpoint * - For Basic auth: searches by configured email */ export async function getCurrentUserAccountId() { try { if (config.jiraApi.authType === 'bearer') { // Bearer auth: get current user directly const response = await jiraApi.get('/rest/api/3/myself'); return response.data.accountId; } // Basic auth: search by email const response = await jiraApi.get('/rest/api/3/user/search', { params: { query: config.jiraApi.email }, }); const users = response.data; if (!users || users.length === 0) { throw new Error(`No user found with email: ${config.jiraApi.email}`); } // Find exact email match const user = users.find((u) => u.emailAddress === config.jiraApi.email); if (!user) { throw new Error(`No exact match for email: ${config.jiraApi.email}`); } return user.accountId; } catch (error) { throw formatJiraError(error, 'Failed to get user account ID'); } } /** * Get Jira issue key from issue ID */ export async function getIssueKeyById(issueId) { try { // Validate issue ID using the schema const result = issueIdSchema().safeParse(issueId); if (!result.success) { throw new Error(result.error.errors[0].message || 'Issue ID validation failed'); } const response = await jiraApi.get(`/rest/api/3/issue/${issueId}`); return response.data.key; } catch (error) { throw formatJiraError(error, `Failed to get issue key for ID ${issueId}`); } } /** * Get Jira issue from issue ID or key */ export async function getIssue(idOrKey) { try { // Validate issue ID using the schema const result = idOrKeySchema().safeParse(idOrKey); if (!result.success) { throw new Error(result.error.errors[0].message || 'Issue identifier validation failed'); } const response = await jiraApi.get(`/rest/api/3/issue/${idOrKey}`); // Find the Tempo account key const tempoAccountId = config.jiraApi.tempoAccountCustomFieldId ? response.data.fields[`customfield_${config.jiraApi.tempoAccountCustomFieldId}`].id : undefined; const id = response.data.id; const key = response.data.key; return { id, key, ...(tempoAccountId ? { tempoAccountId } : {}), }; } catch (error) { throw formatJiraError(error, `Failed to get issue for ${idOrKey}`); } }