Skip to main content
Glama
handlers.ts6.76 kB
/** * Issue tool handlers. * @module tools/issues/handlers */ import type { z } from 'zod'; import * as issues from '../../jira/endpoints/issues.js'; import { simplifyIssue, simplifyIssues } from '../../formatters/toon.js'; import { createSuccessResult, createErrorResult, createPaginatedResult, createActionResult, } from '../../formatters/response.js'; import { formatErrorForMcp } from '../../utils/errors.js'; import { createLogger } from '../../utils/logger.js'; import type { ToolResult } from '../../types/mcp.js'; import type { getIssueSchema, createIssueSchema, updateIssueSchema, deleteIssueSchema, searchIssuesSchema, transitionIssueSchema, getTransitionsSchema, assignIssueSchema, } from './schemas.js'; const logger = createLogger('issue-handlers'); /** * Handles getting an issue. */ export async function handleGetIssue( args: z.infer<typeof getIssueSchema> ): Promise<ToolResult> { try { logger.debug('Getting issue', { issueKey: args.issueKey }); const issue = await issues.getIssue(args.issueKey, args.fields); const simplified = simplifyIssue(issue); return createSuccessResult(simplified); } catch (error) { logger.error('Failed to get issue', error as Error); return formatErrorForMcp(error); } } /** * Handles creating an issue. */ export async function handleCreateIssue( args: z.infer<typeof createIssueSchema> ): Promise<ToolResult> { try { logger.debug('Creating issue', { projectKey: args.projectKey }); const issue = await issues.createIssue({ projectKey: args.projectKey, summary: args.summary, issueType: args.issueType, description: args.description, priority: args.priority, assignee: args.assignee, labels: args.labels, components: args.components, parentKey: args.parentKey, }); const simplified = simplifyIssue(issue); return createSuccessResult({ message: `Issue ${issue.key} created successfully`, issue: simplified, }); } catch (error) { logger.error('Failed to create issue', error as Error); return formatErrorForMcp(error); } } /** * Handles updating an issue. */ export async function handleUpdateIssue( args: z.infer<typeof updateIssueSchema> ): Promise<ToolResult> { try { logger.debug('Updating issue', { issueKey: args.issueKey }); await issues.updateIssue(args.issueKey, { summary: args.summary, description: args.description, priority: args.priority, assignee: args.assignee, labels: args.labels, }); // Fetch updated issue const updated = await issues.getIssue(args.issueKey); const simplified = simplifyIssue(updated); return createSuccessResult({ message: `Issue ${args.issueKey} updated successfully`, issue: simplified, }); } catch (error) { logger.error('Failed to update issue', error as Error); return formatErrorForMcp(error); } } /** * Handles deleting an issue. */ export async function handleDeleteIssue( args: z.infer<typeof deleteIssueSchema> ): Promise<ToolResult> { try { logger.debug('Deleting issue', { issueKey: args.issueKey }); await issues.deleteIssue(args.issueKey, args.deleteSubtasks); return createActionResult('deleted issue', args.issueKey, { subtasksDeleted: args.deleteSubtasks, }); } catch (error) { logger.error('Failed to delete issue', error as Error); return formatErrorForMcp(error); } } /** * Handles searching issues. */ export async function handleSearchIssues( args: z.infer<typeof searchIssuesSchema> ): Promise<ToolResult> { try { logger.debug('Searching issues', { jql: args.jql }); const result = await issues.searchIssues({ jql: args.jql, maxResults: args.maxResults, }); const simplified = simplifyIssues(result.issues); return createPaginatedResult( simplified, result.total ?? result.issues.length, 0, 'issues' ); } catch (error) { logger.error('Failed to search issues', error as Error); return formatErrorForMcp(error); } } /** * Handles getting transitions. */ export async function handleGetTransitions( args: z.infer<typeof getTransitionsSchema> ): Promise<ToolResult> { try { logger.debug('Getting transitions', { issueKey: args.issueKey }); const transitions = await issues.getTransitions(args.issueKey); const simplified = transitions.map((t) => ({ id: t.id, name: t.name, to: t.to.name, })); return createSuccessResult({ issueKey: args.issueKey, transitions: simplified, }); } catch (error) { logger.error('Failed to get transitions', error as Error); return formatErrorForMcp(error); } } /** * Handles transitioning an issue. */ export async function handleTransitionIssue( args: z.infer<typeof transitionIssueSchema> ): Promise<ToolResult> { try { logger.debug('Transitioning issue', { issueKey: args.issueKey }); let transitionId = args.transitionId; // If transition name provided, find the ID if (!transitionId && args.transitionName) { const transitions = await issues.getTransitions(args.issueKey); const transition = transitions.find( (t) => t.name.toLowerCase() === args.transitionName?.toLowerCase() ); if (!transition) { return createErrorResult( `Transition "${args.transitionName}" not found`, { availableTransitions: transitions.map((t) => t.name), } ); } transitionId = transition.id; } if (!transitionId) { return createErrorResult( 'Either transitionId or transitionName must be provided' ); } await issues.transitionIssue(args.issueKey, transitionId, args.comment); // Fetch updated issue const updated = await issues.getIssue(args.issueKey); return createActionResult('transitioned issue', args.issueKey, { newStatus: updated.fields.status.name, }); } catch (error) { logger.error('Failed to transition issue', error as Error); return formatErrorForMcp(error); } } /** * Handles assigning an issue. */ export async function handleAssignIssue( args: z.infer<typeof assignIssueSchema> ): Promise<ToolResult> { try { logger.debug('Assigning issue', { issueKey: args.issueKey }); await issues.assignIssue(args.issueKey, args.accountId); const action = args.accountId ? 'assigned issue' : 'unassigned issue'; return createActionResult(action, args.issueKey, { assignee: args.accountId ?? 'Unassigned', }); } catch (error) { logger.error('Failed to assign issue', error as Error); return formatErrorForMcp(error); } }

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/icy-r/jira-mcp'

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