Skip to main content
Glama

create_issue

Create new Jira issues with standard fields (summary, description, assignee, priority, labels) and custom fields (Health Status, Completion Percentage, Progress Update) for project tracking.

Instructions

Create a new issue in Jira. Supports standard fields (summary, description, assignee, priority, labels) and custom fields (Health Status, Completion Percentage, Progress Update, etc.)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectKeyYesThe project key (e.g., "TSSE")
issueTypeYesThe issue type (e.g., "Epic", "Story", "Task", "Bug")
summaryYesIssue summary/title
descriptionNoIssue description (plain text)
assigneeNoAssignee accountId or "currentuser()" for current user
priorityNoPriority name (e.g., "High", "Medium", "Low")
labelsNoArray of labels to apply
duedateNoDue date in YYYY-MM-DD format
componentsNoArray of component names
healthStatusNoHealth Status value (e.g., "On Track", "At Risk", "Off Track")
completionPercentageNoCompletion percentage (0-100)
decisionNeededNoDecision Needed field content
risksBlockersNoRisks/Blockers field content
progressUpdateNoProgress Update field with three sections
customFieldsNoAdditional custom fields as key-value pairs (e.g., {"customfield_14707": [{"value": "Data"}]})

Implementation Reference

  • Core handler function that builds the issue payload with standard and custom fields, handles defaults for TSSE project, and calls Jira REST API /issue POST endpoint to create the issue.
    export async function createIssue(options: CreateIssueOptions): Promise<CreateIssueResult> { const isTSSEProject = options.projectKey.toUpperCase() === 'TSSE'; const fields: Record<string, unknown> = { project: { key: options.projectKey }, issuetype: { name: options.issueType }, summary: options.summary, }; // Add description if provided if (options.description) { fields.description = createADFDocument(options.description); } // Add assignee - default to currentuser() for TSSE project const assignee = options.assignee ?? (isTSSEProject ? 'currentuser()' : undefined); if (assignee) { // Handle 'currentuser()' or accountId if (assignee.toLowerCase() === 'currentuser()') { // Fetch actual accountId for current user (Jira Cloud requires accountId) const accountId = await getCurrentUserAccountId(); fields.assignee = { accountId }; } else { fields.assignee = { accountId: assignee }; } } // Add priority if provided if (options.priority) { fields.priority = { name: options.priority }; } // Add labels - default to ['EngProd', 'TSSP'] for TSSE project const labels = options.labels ?? (isTSSEProject ? ['EngProd', 'TSSP'] : undefined); if (labels && labels.length > 0) { fields.labels = labels; } // Add duedate - default to 30 days from today for TSSE project let duedate = options.duedate; if (!duedate && isTSSEProject) { const date = new Date(); date.setDate(date.getDate() + 30); duedate = date.toISOString().split('T')[0]; // YYYY-MM-DD format } if (duedate) { fields.duedate = duedate; } // Add components if provided if (options.components && options.components.length > 0) { fields.components = options.components.map(name => ({ name })); } // Add custom fields if (options.healthStatus) { fields[resolveFieldId('Health Status')] = { value: options.healthStatus }; } // BUG FIX: Completion Percentage field expects decimal (0.0-1.0), not percentage (0-100) // Convert percentage input to decimal: 10% -> 0.10 if (options.completionPercentage !== undefined) { const decimalValue = options.completionPercentage / 100; fields[resolveFieldId('Completion Percentage')] = decimalValue; } if (options.decisionNeeded) { fields[resolveFieldId('Decision Needed')] = createADFDocument(options.decisionNeeded); } if (options.risksBlockers) { fields[resolveFieldId('Risks/Blockers')] = createADFDocument(options.risksBlockers); } // Add Progress Update field if provided if (options.progressUpdate) { const currentDate = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', }); const weeklyUpdate = options.progressUpdate.weeklyUpdate || ''; const delivered = options.progressUpdate.delivered || ''; const whatsNext = options.progressUpdate.whatsNext || ''; fields[resolveFieldId('Progress Update')] = createProgressUpdateADF( `${currentDate}\n${weeklyUpdate}`, delivered, whatsNext ); } // Add any additional custom fields if (options.customFields) { for (const [key, value] of Object.entries(options.customFields)) { const fieldId = key.startsWith('customfield_') ? key : resolveFieldId(key); fields[fieldId] = value; } } const response = await jiraFetch<{ id: string; key: string; self: string; }>('/issue', { method: 'POST', body: JSON.stringify({ fields }), }); return { key: response.key, id: response.id, self: response.self, }; }
  • TypeScript interfaces defining input parameters (CreateIssueOptions) and output (CreateIssueResult) for the createIssue handler.
    export interface CreateIssueOptions { projectKey: string; issueType: string; summary: string; description?: string; assignee?: string; // 'currentuser()' or accountId priority?: string; labels?: string[]; duedate?: string; // YYYY-MM-DD format components?: string[]; // Component names // Custom fields healthStatus?: string; completionPercentage?: number; decisionNeeded?: string; risksBlockers?: string; // Progress Update field sections progressUpdate?: { weeklyUpdate?: string; delivered?: string; whatsNext?: string; }; // Any additional custom fields as key-value pairs customFields?: Record<string, unknown>; } export interface CreateIssueResult { key: string; id: string; self: string;
  • src/index.ts:588-675 (registration)
    MCP server registration of 'create_issue' tool, defining Zod input/output schemas (mirroring CreateIssueOptions/Result) and wrapper handler that validates inputs and delegates to jira-client.createIssue.
    server.registerTool( 'create_issue', { title: 'Create Issue', description: `Create a new issue in Jira. Supports standard fields (summary, description, assignee, priority, labels) and custom fields (Health Status, Completion Percentage, Progress Update, etc.)`, inputSchema: { projectKey: z.string().describe('The project key (e.g., "TSSE")'), issueType: z.string().describe('The issue type (e.g., "Epic", "Story", "Task", "Bug")'), summary: z.string().describe('Issue summary/title'), description: z.string().optional().describe('Issue description (plain text)'), assignee: z.string().optional().describe('Assignee accountId or "currentuser()" for current user'), priority: z.string().optional().describe('Priority name (e.g., "High", "Medium", "Low")'), labels: z.array(z.string()).optional().describe('Array of labels to apply'), duedate: z.string().optional().describe('Due date in YYYY-MM-DD format'), components: z.array(z.string()).optional().describe('Array of component names'), healthStatus: z.string().optional().describe('Health Status value (e.g., "On Track", "At Risk", "Off Track")'), completionPercentage: z.number().optional().describe('Completion percentage (0-100)'), decisionNeeded: z.string().optional().describe('Decision Needed field content'), risksBlockers: z.string().optional().describe('Risks/Blockers field content'), progressUpdate: z.object({ weeklyUpdate: z.string().optional().describe('Weekly update content'), delivered: z.string().optional().describe('What was delivered'), whatsNext: z.string().optional().describe('What is next'), }).optional().describe('Progress Update field with three sections'), customFields: z.record(z.unknown()).optional().describe('Additional custom fields as key-value pairs (e.g., {"customfield_14707": [{"value": "Data"}]})'), }, outputSchema: { success: z.boolean(), key: z.string().optional(), id: z.string().optional(), self: z.string().optional(), error: z.object({ message: z.string(), statusCode: z.number().optional(), details: z.unknown().optional(), }).optional(), }, }, async ({ projectKey, issueType, summary, description, assignee, priority, labels, duedate, components, healthStatus, completionPercentage, decisionNeeded, risksBlockers, progressUpdate, customFields }) => { try { if (!projectKey || !projectKey.trim()) { throw new Error('projectKey is required'); } if (!issueType || !issueType.trim()) { throw new Error('issueType is required'); } if (!summary || !summary.trim()) { throw new Error('summary is required'); } const result = await createIssue({ projectKey, issueType, summary, description, assignee, priority, labels, duedate, components, healthStatus, completionPercentage, decisionNeeded, risksBlockers, progressUpdate, customFields: customFields as Record<string, unknown> | undefined, }); const output = { success: true, key: result.key, id: result.id, self: result.self, }; return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }], structuredContent: output, }; } catch (error) { const output = { success: false, ...formatError(error) }; return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }], structuredContent: output, isError: true, }; } } );

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/eh24905-wiz/jira-mcp'

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