UNPKG

@aashari/mcp-server-atlassian-jira

Version:

Node.js/TypeScript MCP server for Atlassian Jira. Equips AI systems (LLMs) with tools to list/get projects, search/get issues (using JQL/ID), and view dev info (commits, PRs). Connects AI capabilities directly into Jira project management and issue tracki

129 lines (122 loc) 6.82 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const logger_util_js_1 = require("../utils/logger.util.js"); const error_util_js_1 = require("../utils/error.util.js"); const atlassian_issues_controller_js_1 = __importDefault(require("../controllers/atlassian.issues.controller.js")); const formatter_util_js_1 = require("../utils/formatter.util.js"); /** * CLI module for managing Jira issues. * Provides commands for listing issues and retrieving issue details. * All commands require valid Atlassian credentials. */ // Create a contextualized logger for this file const cliLogger = logger_util_js_1.Logger.forContext('cli/atlassian.issues.cli.ts'); // Log CLI module initialization cliLogger.debug('Jira issues CLI module initialized'); /** * Register Jira Issues CLI commands with the Commander program * @param program - The Commander program instance to register commands with * @throws Error if command registration fails */ function register(program) { const methodLogger = logger_util_js_1.Logger.forContext('cli/atlassian.issues.cli.ts', 'register'); methodLogger.debug('Registering Jira Issues CLI commands...'); registerListIssuesCommand(program); registerGetIssueCommand(program); methodLogger.debug('CLI commands registered successfully'); } /** * Register the command for listing Jira issues * @param program - The Commander program instance */ function registerListIssuesCommand(program) { program .command('list-issues') .description(`Search for Jira issues using JQL (Jira Query Language), with pagination. PURPOSE: Find and explore issues across projects or within specific criteria using the powerful JQL syntax. Essential for finding issue keys/IDs for 'get-issue'. Use Case: Use this for any issue search, from simple text searches to complex filtering based on project, status, assignee, priority, dates, labels, etc. Output: Formatted list of issues matching the JQL query, including key, summary, type, status, priority, project, assignee, reporter, and dates. Includes pagination info. Sorting: By default, issues are sorted by updated date in descending order (most recently updated first). This behavior can be overridden by including an explicit ORDER BY clause in your JQL query. Examples: $ mcp-atlassian-jira list-issues --jql "project = TEAM AND status = 'In Progress' ORDER BY updated DESC" $ mcp-atlassian-jira list-issues --limit 50 --jql "assignee = currentUser() AND resolution = Unresolved" $ mcp-atlassian-jira list-issues --jql "text ~ 'performance issue'" --cursor "50" $ mcp-atlassian-jira list-issues # Returns all issues, sorted by most recently updated first`) .option('-l, --limit <number>', 'Maximum number of items to return (1-100)', '25') .option('-c, --cursor <string>', 'Pagination cursor for retrieving the next set of results') .option('-q, --jql <jql>', 'Filter issues using Jira Query Language (JQL) syntax (e.g., "project = TEAM AND status = \'In Progress\'")') .action(async (options) => { const actionLogger = logger_util_js_1.Logger.forContext('cli/atlassian.issues.cli.ts', 'list-issues'); try { actionLogger.debug('Processing command options:', options); // Validate limit if provided if (options.limit) { const limit = parseInt(options.limit, 10); if (isNaN(limit) || limit <= 0) { throw new Error('Invalid --limit value: Must be a positive integer.'); } } const filterOptions = { ...(options.jql && { jql: options.jql }), ...(options.limit && { limit: parseInt(options.limit, 10), }), ...(options.cursor && { cursor: options.cursor }), }; actionLogger.debug('Fetching issues with filters:', filterOptions); const result = await atlassian_issues_controller_js_1.default.list(filterOptions); actionLogger.debug('Successfully retrieved issues'); // Print the main content console.log((0, formatter_util_js_1.formatHeading)('Issues', 2)); console.log(result.content); // Print pagination information if available if (result.pagination) { // Use the actual number of items displayed rather than potentially zero count // The count comes from the controller - it should be the number of items in the current batch // We extract this from the controller response. // If the response has no items but has more results, show 0 but indicate more are available const displayCount = result.pagination.count ?? 0; console.log('\n' + (0, formatter_util_js_1.formatPagination)(displayCount, result.pagination.hasMore, result.pagination.nextCursor)); } } catch (error) { actionLogger.error('Operation failed:', error); (0, error_util_js_1.handleCliError)(error); } }); } /** * Register the command for retrieving a specific Jira issue * @param program - The Commander program instance */ function registerGetIssueCommand(program) { program .command('get-issue') .description(`Get detailed information about a specific Jira issue using its ID or key. PURPOSE: Retrieve comprehensive details for a *known* issue, including its summary, description, status, priority, assignee, reporter, comments, attachments, and all standard fields.`) .requiredOption('--issue-id-or-key <idOrKey>', 'ID or key of the issue to retrieve (e.g., "TEAM-123" or "10001")') .action(async (options) => { const actionLogger = logger_util_js_1.Logger.forContext('cli/atlassian.issues.cli.ts', 'get-issue'); try { actionLogger.debug('Processing command options:', options); // Validate issue ID/key if (!options.issueIdOrKey || options.issueIdOrKey.trim() === '') { throw new Error('Issue ID or key must not be empty.'); } actionLogger.debug(`Fetching issue: ${options.issueIdOrKey}`); const result = await atlassian_issues_controller_js_1.default.get({ issueIdOrKey: options.issueIdOrKey, }); console.log(result.content); } catch (error) { (0, error_util_js_1.handleCliError)(error); } }); } exports.default = { register };