create_issue
Create new Jira issues with standard fields like summary, description, assignee, priority, and labels, plus custom fields for tracking health status, completion percentage, and progress updates.
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
| Name | Required | Description | Default |
|---|---|---|---|
| projectKey | Yes | The project key (e.g., "TSSE") | |
| issueType | Yes | The issue type (e.g., "Epic", "Story", "Task", "Bug") | |
| summary | Yes | Issue summary/title | |
| description | No | Issue description (plain text) | |
| assignee | No | Assignee accountId or "currentuser()" for current user | |
| priority | No | Priority name (e.g., "High", "Medium", "Low") | |
| labels | No | Array of labels to apply | |
| duedate | No | Due date in YYYY-MM-DD format | |
| components | No | Array of component names | |
| healthStatus | No | Health Status value (e.g., "On Track", "At Risk", "Off Track") | |
| completionPercentage | No | Completion percentage (0-100) | |
| decisionNeeded | No | Decision Needed field content | |
| risksBlockers | No | Risks/Blockers field content | |
| progressUpdate | No | Progress Update field with three sections | |
| customFields | No | Additional custom fields as key-value pairs (e.g., {"customfield_14707": [{"value": "Data"}]}) |
Implementation Reference
- src/jira-client.ts:438-553 (handler)Core handler function that implements the create_issue tool logic: builds comprehensive issue payload with standard fields, custom fields (Health Status, Progress Update, etc.), handles defaults for TSSE project, resolves current user assignee, and creates the issue via Jira REST API POST /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, }; }
- src/index.ts:588-675 (registration)MCP server tool registration for 'create_issue': defines title, description, full inputSchema (zod for projectKey, issueType, summary, description, custom fields etc.), outputSchema (success, key, id), and thin async handler that validates inputs, calls core createIssue, and formats MCP response.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, }; } } );
- src/index.ts:590-625 (schema)Detailed Zod inputSchema and outputSchema for the create_issue MCP tool, validating all parameters (project, type, summary, custom fields like healthStatus, progressUpdate nested object) and response format.{ 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(), }, },
- src/jira-client.ts:402-431 (schema)TypeScript interfaces CreateIssueOptions (mirrors tool input params) and CreateIssueResult (matches tool output) for type-safe createIssue function.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; }