Skip to main content
Glama

Jira MCP Server

by sespinosa
MIT License
15
2
  • Apple
  • Linux
issues.ts7.83 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { Version3Client } from 'jira.js'; import { z } from 'zod'; import { validateIssueFields, safeFieldSets, FieldValidationError, } from '../utils/fieldValidation.js'; import { rateLimiters, withRateLimit } from '../utils/rateLimiter.js'; import { auditLogger } from '../utils/auditLogger.js'; import { SecurityError } from '../utils/security.js'; export function registerIssueTools(server: McpServer, jiraClient: Version3Client) { // Create Issue server.registerTool( 'create_issue', { title: 'Create Issue', description: 'Create a new Jira issue', inputSchema: { projectKey: z.string().describe('Project key'), summary: z.string().describe('Issue summary'), description: z.string().optional().describe('Issue description'), issueType: z.string().default('Task').describe('Issue type'), priority: z.string().optional().describe('Priority'), assignee: z.string().optional().describe('Assignee account ID'), labels: z.array(z.string()).optional().describe('Labels'), }, }, async (args) => { try { // Rate limiting and audit logging await withRateLimit( async () => { auditLogger.logOperation('create_issue', 'issue', { projectKey: args.projectKey, summary: args.summary, }); return true; }, rateLimiters.standard, 'create_issue' ); // Validate args using Zod for runtime validation const createSchema = z.object({ projectKey: z.string(), summary: z.string(), description: z.string().optional(), issueType: z.string().default('Task'), priority: z.string().optional(), assignee: z.string().optional(), labels: z.array(z.string()).optional(), }); const validatedArgs = createSchema.parse(args); // Prepare fields for validation const issueFields: Record<string, any> = { project: { key: validatedArgs.projectKey }, summary: validatedArgs.summary, issuetype: { name: validatedArgs.issueType }, }; if (validatedArgs.description) { issueFields.description = { type: 'doc', version: 1, content: [ { type: 'paragraph', content: [{ type: 'text', text: validatedArgs.description }], }, ], }; } if (validatedArgs.priority) issueFields.priority = { name: validatedArgs.priority }; if (validatedArgs.assignee) issueFields.assignee = { accountId: validatedArgs.assignee }; if (validatedArgs.labels) issueFields.labels = validatedArgs.labels; // Validate fields for security const validatedFields = validateIssueFields(issueFields, { allowedFields: safeFieldSets.basic, blockDangerousValues: true, }); const issue = await jiraClient.issues.createIssue({ fields: validatedFields as any, }); // Log successful creation auditLogger.logSuccess('create_issue', 'issue', { issueKey: issue.key, projectKey: validatedArgs.projectKey, }); return { content: [ { type: 'text', text: JSON.stringify( { success: true, data: { key: issue.key, id: issue.id, self: issue.self, }, }, null, 2 ), }, ], }; } catch (error) { // Audit failure auditLogger.logFailure( 'create_issue', 'issue', error instanceof Error ? error.message : 'Unknown error', { projectKey: args.projectKey } ); if (error instanceof FieldValidationError) { throw new Error(`Field validation failed: ${error.message}`); } if (error instanceof SecurityError) { throw new Error(`Security violation: ${error.message}`); } throw error; } } ); // Update Issue server.registerTool( 'update_issue', { title: 'Update Issue', description: 'Update a Jira issue', inputSchema: { issueKey: z.string().describe('Issue key'), summary: z.string().optional().describe('New summary'), description: z.string().optional().describe('New description'), priority: z.string().optional().describe('New priority'), assignee: z.string().optional().describe('New assignee account ID'), labels: z.array(z.string()).optional().describe('New labels'), }, }, async (args) => { try { // Rate limiting and audit logging await withRateLimit( async () => { auditLogger.logOperation('update_issue', 'issue', { issueKey: args.issueKey }); return true; }, rateLimiters.standard, 'update_issue' ); // Validate args using Zod for runtime validation const updateSchema = z.object({ issueKey: z.string(), summary: z.string().optional(), description: z.string().optional(), priority: z.string().optional(), assignee: z.string().optional(), labels: z.array(z.string()).optional(), }); const validatedArgs = updateSchema.parse(args); const fields: any = {}; if (validatedArgs.summary) fields.summary = validatedArgs.summary; if (validatedArgs.description) { fields.description = { type: 'doc', version: 1, content: [ { type: 'paragraph', content: [{ type: 'text', text: validatedArgs.description }], }, ], }; } if (validatedArgs.priority) fields.priority = { name: validatedArgs.priority }; if (validatedArgs.assignee) fields.assignee = { accountId: validatedArgs.assignee }; if (validatedArgs.labels) fields.labels = validatedArgs.labels; // Validate fields for security const validatedFields = validateIssueFields(fields, { allowedFields: safeFieldSets.basic, blockDangerousValues: true, }); await jiraClient.issues.editIssue({ issueIdOrKey: validatedArgs.issueKey, fields: validatedFields, }); // Log successful update auditLogger.logSuccess('update_issue', 'issue', { issueKey: validatedArgs.issueKey, updatedFields: Object.keys(validatedFields), }); return { content: [ { type: 'text', text: JSON.stringify( { success: true, message: `Issue ${validatedArgs.issueKey} updated successfully`, }, null, 2 ), }, ], }; } catch (error) { // Audit failure auditLogger.logFailure( 'update_issue', 'issue', error instanceof Error ? error.message : 'Unknown error', { issueKey: args.issueKey } ); if (error instanceof FieldValidationError) { throw new Error(`Field validation failed: ${error.message}`); } if (error instanceof SecurityError) { throw new Error(`Security violation: ${error.message}`); } throw error; } } ); }

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

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