Skip to main content
Glama
kornbed

Jira MCP Server for Cursor

get_ticket

Retrieve details for a specific Jira ticket by entering its ID to view status, description, and other information directly within your editor.

Instructions

Get details of a specific Jira ticket

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ticketIdYesThe Jira ticket ID (e.g., PROJECT-123)

Implementation Reference

  • The main handler function for the 'get_ticket' tool. It validates configuration, fetches the ticket details from Jira API including summary, status, type, description (parsed from ADF), parent, and linked issues, then formats and returns them.
    async ({ ticketId }: { ticketId: string }) => {
      const configError = validateJiraConfig();
      if (configError) {
        return {
          content: [{ type: "text", text: `Configuration error: ${configError}` }],
        };
      }
    
      try {
        const ticket = await jira.issues.getIssue({
          issueIdOrKey: ticketId,
          fields: ['summary', 'status', 'issuetype', 'description', 'parent', 'issuelinks'],
        });
    
        const formattedTicket = [
          `Key: ${ticket.key}`,
          `Summary: ${ticket.fields?.summary || 'No summary'}`,
          `Status: ${ticket.fields?.status?.name || 'Unknown status'}`,
          `Type: ${ticket.fields?.issuetype?.name || 'Unknown type'}`,
          `Description:\n${extractTextFromADF(ticket.fields?.description) || 'No description'}`,
          `Parent: ${ticket.fields?.parent?.key || 'No parent'}`
        ];
    
        // Linked Issues Section
        const links = ticket.fields?.issuelinks || [];
        if (Array.isArray(links) && links.length > 0) {
          formattedTicket.push('\nLinked Issues:');
          for (const link of links) {
            // Outward (this issue is the source)
            if (link.outwardIssue) {
              const key = link.outwardIssue.key;
              const summary = link.outwardIssue.fields?.summary || 'No summary';
              const type = link.type?.outward || link.type?.name || 'Related';
              formattedTicket.push(`- [${type}] ${key}: ${summary}`);
            }
            // Inward (this issue is the target)
            if (link.inwardIssue) {
              const key = link.inwardIssue.key;
              const summary = link.inwardIssue.fields?.summary || 'No summary';
              const type = link.type?.inward || link.type?.name || 'Related';
              formattedTicket.push(`- [${type}] ${key}: ${summary}`);
            }
          }
        } else {
          formattedTicket.push('\nLinked Issues: None');
        }
    
        return {
          content: [{ type: "text", text: formattedTicket.join('\n') }],
        };
      } catch (error) {
        return {
          content: [{ type: "text", text: `Failed to fetch ticket: ${(error as Error).message}` }],
        };
      }
    }
  • Input schema for the 'get_ticket' tool, using Zod to validate the 'ticketId' parameter.
    {
      ticketId: z.string().describe("The Jira ticket ID (e.g., PROJECT-123)"),
    },
  • src/server.ts:159-221 (registration)
    Registration of the 'get_ticket' tool on the MCP server, specifying name, description, input schema, and handler function.
    server.tool(
      "get_ticket",
      "Get details of a specific Jira ticket",
      {
        ticketId: z.string().describe("The Jira ticket ID (e.g., PROJECT-123)"),
      },
      async ({ ticketId }: { ticketId: string }) => {
        const configError = validateJiraConfig();
        if (configError) {
          return {
            content: [{ type: "text", text: `Configuration error: ${configError}` }],
          };
        }
    
        try {
          const ticket = await jira.issues.getIssue({
            issueIdOrKey: ticketId,
            fields: ['summary', 'status', 'issuetype', 'description', 'parent', 'issuelinks'],
          });
    
          const formattedTicket = [
            `Key: ${ticket.key}`,
            `Summary: ${ticket.fields?.summary || 'No summary'}`,
            `Status: ${ticket.fields?.status?.name || 'Unknown status'}`,
            `Type: ${ticket.fields?.issuetype?.name || 'Unknown type'}`,
            `Description:\n${extractTextFromADF(ticket.fields?.description) || 'No description'}`,
            `Parent: ${ticket.fields?.parent?.key || 'No parent'}`
          ];
    
          // Linked Issues Section
          const links = ticket.fields?.issuelinks || [];
          if (Array.isArray(links) && links.length > 0) {
            formattedTicket.push('\nLinked Issues:');
            for (const link of links) {
              // Outward (this issue is the source)
              if (link.outwardIssue) {
                const key = link.outwardIssue.key;
                const summary = link.outwardIssue.fields?.summary || 'No summary';
                const type = link.type?.outward || link.type?.name || 'Related';
                formattedTicket.push(`- [${type}] ${key}: ${summary}`);
              }
              // Inward (this issue is the target)
              if (link.inwardIssue) {
                const key = link.inwardIssue.key;
                const summary = link.inwardIssue.fields?.summary || 'No summary';
                const type = link.type?.inward || link.type?.name || 'Related';
                formattedTicket.push(`- [${type}] ${key}: ${summary}`);
              }
            }
          } else {
            formattedTicket.push('\nLinked Issues: None');
          }
    
          return {
            content: [{ type: "text", text: formattedTicket.join('\n') }],
          };
        } catch (error) {
          return {
            content: [{ type: "text", text: `Failed to fetch ticket: ${(error as Error).message}` }],
          };
        }
      }
    );
  • Helper function to recursively extract plain text from Jira's ADF (Atlassian Document Format) used in 'get_ticket' to parse ticket description and linked issue summaries.
    function extractTextFromADF(node: any): string {
      if (!node) {
        return '';
      }
    
      // Handle text nodes directly
      if (node.type === 'text' && node.text) {
        return node.text;
      }
    
      let text = '';
      // Handle block nodes like paragraph, heading, etc.
      if (node.content && Array.isArray(node.content)) {
        text = node.content.map(extractTextFromADF).join('');
        // Add a newline after paragraphs for better formatting
        if (node.type === 'paragraph') {
          text += '\n';
        }
      }
    
      return text;
    }
  • Helper function to validate required Jira environment variables, called at the start of the 'get_ticket' handler.
    function validateJiraConfig(): string | null {
      if (!process.env.JIRA_HOST) return "JIRA_HOST environment variable is not set";
      if (!process.env.JIRA_EMAIL) return "JIRA_EMAIL environment variable is not set";
      if (!process.env.JIRA_API_TOKEN) return "JIRA_API_TOKEN environment variable is not set";
      return null;
    }

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/kornbed/jira-mcp-server'

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