jira_create_issue
Create a new Jira issue in a specified project with configurable fields including type, priority, assignee, labels, components, and custom fields. Supports multiple description formats.
Instructions
Creates a new Jira issue in the specified project. Supports setting issue type, priority, assignee, labels, components, and custom fields. Description format is controlled by the "format" parameter (default: markdown). For required custom fields, supply them via customFields (e.g., { "customfield_12345": { id: "..." } }). Returns the created issue with all details.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectKey | Yes | Project key where the issue will be created | |
| summary | Yes | Issue summary/title | |
| description | No | Issue description. Accepts plain text or ADF object. | |
| issueType | Yes | Issue type (e.g., Bug, Story, Task) | |
| priority | No | Issue priority | |
| assignee | No | Assignee account ID | |
| labels | No | Issue labels | |
| components | No | Component names | |
| customFields | No | Additional Jira field mappings, e.g. { "customfield_12345": value }. Use for required custom fields. | |
| returnIssue | No | When false, skip fetching full issue after creation | |
| format | No | Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown" | markdown |
Implementation Reference
- src/utils/api-helpers.ts:303-371 (handler)Core API function that sends POST /issue to Jira to create an issue with fields (project, summary, issuetype, description, priority, assignee, labels, components, customFields). Returns issue key string or full JiraIssue.
export async function createIssue( issueData: { projectKey: string; summary: string; description?: string; issueType: string; priority?: string; assignee?: string; labels?: string[]; components?: string[]; customFields?: Record<string, any>; format?: 'markdown' | 'adf' | 'plain'; }, options: { returnIssue?: boolean } = { returnIssue: true } ): Promise<JiraIssue | string> { const fields: Record<string, any> = { project: { key: issueData.projectKey }, summary: issueData.summary, issuetype: { name: issueData.issueType }, }; if (issueData.description !== undefined) { fields.description = ensureAdfDescription( issueData.description, issueData.format || 'markdown' ); } if (issueData.priority) { fields.priority = { name: issueData.priority }; } if (issueData.assignee) { fields.assignee = { accountId: issueData.assignee }; } if (issueData.labels && issueData.labels.length > 0) { fields.labels = issueData.labels; } if (issueData.components && issueData.components.length > 0) { fields.components = issueData.components.map((name) => ({ name })); } // Merge any custom fields provided by the caller if (issueData.customFields && typeof issueData.customFields === 'object') { for (const [key, value] of Object.entries(issueData.customFields)) { // Do not overwrite standard fields if accidentally duplicated if (!(key in fields)) { fields[key] = value; } } } const config: AxiosRequestConfig = { method: 'POST', url: '/issue', data: { fields }, }; const response = await makeJiraRequest<{ key: string; id: string; self: string }>(config); if (options.returnIssue === false) { return response.key; } // Return the created issue return await getIssue(response.key); } - src/tools/create-issue.ts:82-123 (handler)Tool handler function that validates input via Zod schema, builds createParams, calls createIssue() API helper, and returns formatted success/error response.
export async function handleCreateIssue(input: unknown): Promise<McpToolResponse> { try { const validated = validateInput(CreateIssueInputSchema, input); log.info(`Creating issue in project ${validated.projectKey}...`); const createParams: any = { projectKey: validated.projectKey, summary: validated.summary, issueType: validated.issueType, }; if (validated.description !== undefined) createParams.description = validated.description; if (validated.priority !== undefined) createParams.priority = validated.priority; if (validated.assignee !== undefined) createParams.assignee = validated.assignee; if (validated.labels !== undefined) createParams.labels = validated.labels; if (validated.components !== undefined) createParams.components = validated.components; if (validated.customFields !== undefined) createParams.customFields = validated.customFields; if (validated.format !== undefined) createParams.format = validated.format; let issueOrKey; if (validated.returnIssue === false) { issueOrKey = await createIssue(createParams, { returnIssue: false }); } else { // Default behavior: request full issue; keep API surface compatible with tests issueOrKey = await createIssue(createParams); } if (validated.returnIssue === false) { const key = typeof issueOrKey === 'string' ? issueOrKey : (issueOrKey as any).key; log.info(`Created issue ${key}`); return formatSuccessResponse(`Issue created: ${key}`); } const issue = issueOrKey as any; log.info(`Created issue ${issue.key}`); return formatIssueResponse(issue); } catch (error) { log.error('Error in handleCreateIssue:', error); return handleError(error); } } - src/tools/create-issue.ts:14-80 (registration)Tool definition/registration with name TOOL_NAMES.CREATE_ISSUE ('jira_create_issue'), description, and JSON Schema input schema for MCP tool registration.
export const createIssueTool: Tool = { name: TOOL_NAMES.CREATE_ISSUE, description: 'Creates a new Jira issue in the specified project. Supports setting issue type, priority, assignee, labels, components, and custom fields. Description format is controlled by the "format" parameter (default: markdown). For required custom fields, supply them via customFields (e.g., { "customfield_12345": { id: "..." } }). Returns the created issue with all details.', inputSchema: { type: 'object', properties: { projectKey: { type: 'string', description: 'Project key where the issue will be created', }, summary: { type: 'string', description: 'Issue summary/title', minLength: 1, }, description: { anyOf: [{ type: 'string' }, { type: 'object' }], description: 'Detailed issue description (optional). Format depends on the "format" parameter.', }, issueType: { type: 'string', description: 'Issue type name (e.g., Bug, Story, Task, Epic)', }, priority: { type: 'string', description: 'Issue priority name (e.g., High, Medium, Low) - optional', }, assignee: { type: 'string', description: 'Assignee account ID (optional)', }, labels: { type: 'array', items: { type: 'string' }, description: 'Issue labels (optional)', default: [], }, components: { type: 'array', items: { type: 'string' }, description: 'Component names (optional)', default: [], }, customFields: { type: 'object', additionalProperties: true, description: 'Additional Jira fields, e.g. { "customfield_10071": value }. Use this to set required custom fields.', }, returnIssue: { type: 'boolean', description: 'If false, returns only the issue key without fetching full details', default: true, }, format: { type: 'string', enum: ['markdown', 'adf', 'plain'], description: 'Description format: "markdown" (converts Markdown to ADF, default), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting)', default: 'markdown', }, }, required: ['projectKey', 'summary', 'issueType'], }, }; - src/index.ts:166-172 (registration)MCP server registration of the jira_create_issue tool, linking name, description, Zod schema, and handler function.
{ name: TOOL_NAMES.CREATE_ISSUE, description: tools.createIssueTool.description!, inputSchema: CreateIssueInputSchema, handler: tools.handleCreateIssue, annotations: { readOnlyHint: false }, }, - src/types/tools.ts:48-82 (schema)Zod schema for validating create-issue input: projectKey, summary, description, issueType, priority, assignee, labels, components, customFields, returnIssue, format.
export const CreateIssueInputSchema = z.object({ projectKey: z .string() .describe('Project key where the issue will be created') .refine((v) => isValidProjectKey(v), 'Invalid project key format'), summary: z.string().min(1).describe('Issue summary/title'), description: z .union([z.string(), z.any()]) .optional() .describe('Issue description. Accepts plain text or ADF object.'), issueType: z.string().describe('Issue type (e.g., Bug, Story, Task)'), priority: z.string().optional().describe('Issue priority'), assignee: z.string().optional().describe('Assignee account ID'), labels: z.array(z.string()).optional().describe('Issue labels'), components: z.array(z.string()).optional().describe('Component names'), customFields: z .record(z.any()) .optional() .describe( 'Additional Jira field mappings, e.g. { "customfield_12345": value }. Use for required custom fields.' ), returnIssue: z .boolean() .optional() .describe('When false, skip fetching full issue after creation'), format: z .enum(['markdown', 'adf', 'plain']) .optional() .default('markdown') .describe( 'Description format: "markdown" (converts Markdown to ADF), "adf" (use as-is ADF object), "plain" (converts plain text to ADF with basic formatting). Default: "markdown"' ), }); export type CreateIssueInput = z.infer<typeof CreateIssueInputSchema>;