Jira-Context-MCP

by rahulthedevil
Verified
import axios, { AxiosError } from "axios"; import { JiraError, JiraIssue, JiraProject, JiraProjectListResponse, JiraSearchParams, JiraSearchResponse, JiraIssueTypeResponse } from "~/types/jira"; import fs from "fs"; export class JiraService { private readonly baseUrl: string; private readonly auth: { username: string; password: string; }; constructor(baseUrl: string, username: string, apiToken: string) { // Ensure the base URL doesn't end with a trailing slash this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; this.auth = { username, password: apiToken, }; } private async request<T>(endpoint: string, method: 'GET' | 'POST' = 'GET', data?: any): Promise<T> { try { console.log(`Calling ${this.baseUrl}${endpoint}`); const config = { method, url: `${this.baseUrl}${endpoint}`, auth: this.auth, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, data: method === 'POST' ? data : undefined, }; const response = await axios(config); return response.data; } catch (error) { if (error instanceof AxiosError && error.response) { throw { status: error.response.status, err: (error.response.data as { errorMessages?: string[] })?.errorMessages?.[0] || "Unknown error", } as JiraError; } throw new Error(`Failed to make request to Jira API: ${error instanceof Error ? error.message : String(error)}`); } } /** * Get a Jira issue by key */ async getIssue(issueKey: string): Promise<JiraIssue> { const endpoint = `/rest/api/3/issue/${issueKey}`; const response = await this.request<JiraIssue>(endpoint); writeLogs(`jira-issue-${issueKey}.json`, response); return response; } /** * Search for Jira issues using JQL */ async searchIssues(params: JiraSearchParams): Promise<JiraSearchResponse> { const endpoint = `/rest/api/3/search`; const response = await this.request<JiraSearchResponse>(endpoint, 'POST', params); writeLogs(`jira-search-${new Date().toISOString()}.json`, response); return response; } /** * Get issues assigned to the current user */ async getAssignedIssues(projectKey?: string, maxResults: number = 50): Promise<JiraSearchResponse> { const jql = projectKey ? `assignee = currentUser() AND project = ${projectKey} ORDER BY updated DESC` : 'assignee = currentUser() ORDER BY updated DESC'; return this.searchIssues({ jql, maxResults, fields: ['summary', 'description', 'status', 'issuetype', 'priority', 'assignee', 'project'], }); } /** * Get issues by type */ async getIssuesByType(issueType: string, projectKey?: string, maxResults: number = 50): Promise<JiraSearchResponse> { const jql = projectKey ? `issuetype = "${issueType}" AND project = ${projectKey} ORDER BY updated DESC` : `issuetype = "${issueType}" ORDER BY updated DESC`; return this.searchIssues({ jql, maxResults, fields: ['summary', 'description', 'status', 'issuetype', 'priority', 'assignee', 'project'], }); } /** * Get list of projects */ async getProjects(): Promise<JiraProject[]> { const endpoint = `/rest/api/3/project`; const response = await this.request<JiraProject[]>(endpoint); return response; } /** * Get list of issue types */ async getIssueTypes(): Promise<JiraIssueTypeResponse> { const endpoint = `/rest/api/3/issuetype`; const response = await this.request<JiraIssueTypeResponse>(endpoint); return response; } } function writeLogs(name: string, value: any) { const logsDir = "logs"; if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir); } fs.writeFileSync(`${logsDir}/${name}`, JSON.stringify(value, null, 2)); }