Skip to main content
Glama

jira_get_issue

Retrieve specific Jira issue details by key to access ticket information, status, and project data for tracking and management.

Instructions

Retrieves details of a specific Jira issue by key

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
jiraHostNoThe Jira host URL (e.g., 'your-domain.atlassian.net')
emailNoEmail address associated with the Jira account
apiTokenNoAPI token for Jira authentication
issueKeyYesThe Jira issue key (e.g., 'PROJECT-123')

Implementation Reference

  • Core handler function that validates inputs, authenticates with Jira, fetches issue details via REST API, formats comprehensive markdown response including tables for status, comments, attachments, worklogs, and changelog, and handles errors.
    export async function getIssue(args: any) { const validatedArgs = await JiraIssueRequestSchema.validate(args); const jiraHost = validatedArgs.jiraHost || process.env.JIRA_HOST; const email = validatedArgs.email || process.env.JIRA_EMAIL; const apiToken = validatedArgs.apiToken || process.env.JIRA_API_TOKEN; const issueKey = validatedArgs.issueKey; if (!jiraHost || !email || !apiToken) { throw new Error('Missing required authentication credentials. Please provide jiraHost, email, and apiToken.'); } validateCredentials(jiraHost, email, apiToken); const authHeader = createAuthHeader(email, apiToken); try { const response = await axios.get(`https://${jiraHost}/rest/api/3/issue/${issueKey}`, { params: { expand: 'renderedFields,names,changelog,operations', fields: 'summary,status,assignee,issuetype,priority,created,creator,reporter,description,comment,attachment,worklog,updated,labels,fixVersions,components,duedate' }, headers: { 'Authorization': authHeader, 'Accept': 'application/json', }, }); const issue = response.data; const formattedDate = new Date(issue.fields.created).toLocaleString(); const updatedDate = issue.fields.updated ? new Date(issue.fields.updated).toLocaleString() : 'Not updated'; let formattedResponse = `# Issue: ${issue.key} - ${issue.fields.summary}\n\n`; formattedResponse += `## Basic Information\n\n`; formattedResponse += `| Field | Value |\n`; formattedResponse += `|-------|-------|\n`; formattedResponse += `| Status | ${issue.fields.status?.name || 'Unknown'} |\n`; formattedResponse += `| Type | ${issue.fields.issuetype?.name || 'Unknown'} |\n`; formattedResponse += `| Priority | ${issue.fields.priority?.name || 'Not set'} |\n`; formattedResponse += `| Assignee | ${issue.fields.assignee?.displayName || 'Unassigned'} |\n`; formattedResponse += `| Reporter | ${issue.fields.reporter?.displayName || 'Unknown'} |\n`; formattedResponse += `| Created | ${formattedDate} |\n`; formattedResponse += `| Updated | ${updatedDate} |\n`; if (issue.fields.duedate) { const dueDate = new Date(issue.fields.duedate).toLocaleString(); formattedResponse += `| Due Date | ${dueDate} |\n`; } if (issue.fields.labels && issue.fields.labels.length > 0) { formattedResponse += `| Labels | ${issue.fields.labels.join(', ')} |\n`; } if (issue.fields.components && issue.fields.components.length > 0) { const componentNames = issue.fields.components.map((comp: any) => comp.name).join(', '); formattedResponse += `| Components | ${componentNames} |\n`; } if (issue.fields.fixVersions && issue.fields.fixVersions.length > 0) { const fixVersionNames = issue.fields.fixVersions.map((ver: any) => ver.name).join(', '); formattedResponse += `| Fix Versions | ${fixVersionNames} |\n`; } if (issue.fields.description) { formattedResponse += `\n## Description\n\n${issue.renderedFields?.description || issue.fields.description}\n`; } if (issue.fields.comment && issue.fields.comment.comments && issue.fields.comment.comments.length > 0) { formattedResponse += `\n## Comments (${issue.fields.comment.comments.length})\n\n`; issue.fields.comment.comments.forEach((comment: any, index: number) => { const commentDate = new Date(comment.created).toLocaleString(); formattedResponse += `### Comment ${index + 1} - ${comment.author.displayName} (${commentDate})\n\n`; formattedResponse += `${comment.body}\n\n`; }); } if (issue.fields.attachment && issue.fields.attachment.length > 0) { formattedResponse += `\n## Attachments (${issue.fields.attachment.length})\n\n`; formattedResponse += `| Filename | Size | Uploaded by | Date |\n`; formattedResponse += `|----------|------|-------------|------|\n`; issue.fields.attachment.forEach((attachment: any) => { const attachmentDate = new Date(attachment.created).toLocaleString(); const sizeInKb = Math.round(attachment.size / 1024); formattedResponse += `| ${attachment.filename} | ${sizeInKb} KB | ${attachment.author.displayName} | ${attachmentDate} |\n`; }); } if (issue.fields.worklog && issue.fields.worklog.worklogs && issue.fields.worklog.worklogs.length > 0) { formattedResponse += `\n## Work Log (${issue.fields.worklog.worklogs.length})\n\n`; formattedResponse += `| User | Time Spent | Date | Comment |\n`; formattedResponse += `|------|------------|------|--------|\n`; issue.fields.worklog.worklogs.forEach((worklog: any) => { const worklogDate = new Date(worklog.started).toLocaleString(); formattedResponse += `| ${worklog.author.displayName} | ${worklog.timeSpent} | ${worklogDate} | ${worklog.comment || 'No comment'} |\n`; }); } if (issue.changelog && issue.changelog.histories && issue.changelog.histories.length > 0) { formattedResponse += `\n## Change History\n\n`; formattedResponse += `| Date | User | Changes |\n`; formattedResponse += `|------|------|--------|\n`; issue.changelog.histories.forEach((history: any) => { const historyDate = new Date(history.created).toLocaleString(); const changes = history.items.map((item: any) => { return `${item.field} changed from "${item.fromString || 'none'}" to "${item.toString || 'none'}"`; }).join('; '); formattedResponse += `| ${historyDate} | ${history.author.displayName} | ${changes} |\n`; }); } return { content: [{ type: "text", text: formattedResponse }], isError: false, }; } catch (error: any) { let errorMsg = "An error occurred while retrieving the issue."; if (error.response) { if (error.response.status === 404) { errorMsg = `Issue ${issueKey} not found or you don't have permission to view it.`; } else { errorMsg = `Error ${error.response.status}: ${error.response.data?.errorMessages?.join(', ') || error.message}`; } } else if (error.message) { errorMsg = error.message; } return { content: [{ type: "text", text: `# Error\n\n${errorMsg}` }], isError: true, }; } }
  • Yup validation schema for jira_get_issue inputs, extending base JiraApiRequestSchema with required issueKey validation (format PROJECT-123).
    export const JiraIssueRequestSchema = JiraApiRequestSchema.shape({ issueKey: yup.string() .required("Issue key is required") .matches(/^[A-Z][A-Z0-9_]+-[1-9][0-9]*$/, "Invalid issue key format. The correct format is PROJECT-123"), });
  • Registers the 'jira_get_issue' tool by mapping its name to the validation schema and handler function in the toolConfigs object used by handleCallTool.
    jira_get_issue: { schema: JiraIssueRequestSchema, handler: getIssue },
  • Registers the tool description, including name, description, and inputSchema, for the MCP listTools endpoint.
    { name: "jira_get_issue", description: "Retrieves details of a specific Jira issue by key", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), issueKey: { type: "string", description: "The Jira issue key (e.g., 'PROJECT-123')", }, }, required: ["issueKey"], }, },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/samuelrizzo/jira-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server