Skip to main content
Glama

Create Issue

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
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"}]})

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
idNo
keyNo
selfNo
errorNo
successYes

Implementation Reference

  • 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,
          };
        }
      }
    );
  • 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(),
      },
    },
  • 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;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. While it mentions what fields are supported, it doesn't describe what happens after creation (e.g., does it return the new issue ID?), what permissions are required, whether there are rate limits, or what validation occurs. For a mutation tool with 15 parameters, this represents significant gaps in behavioral understanding.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured in a single sentence that conveys the core purpose and scope. The parenthetical examples of fields add useful context without unnecessary elaboration. While it could potentially benefit from a second sentence about behavioral aspects, the existing text is well-focused and wastes no words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (15 parameters, nested objects) and the presence of an output schema, the description provides adequate basic information about what the tool does. However, for a mutation tool with no annotations, it should ideally include more about permissions, side effects, or typical responses. The existence of an output schema reduces the need to describe return values, but other behavioral aspects remain underspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description mentions 'standard fields' and 'custom fields' with examples, which adds some context beyond the schema. However, with 100% schema description coverage, the schema already documents all 15 parameters thoroughly. The description provides high-level categorization but doesn't add meaningful semantic details about parameter interactions, dependencies, or usage patterns that aren't already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Create') and resource ('new issue in Jira'), making the purpose immediately apparent. It distinguishes this tool from siblings like 'update_issue_field' or 'add_comment' by emphasizing creation rather than modification or commenting. The mention of both standard and custom fields provides additional specificity about what can be created.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'update_issue_field' or 'search_issues'. There's no mention of prerequisites, constraints, or typical use cases. While the purpose is clear, the agent receives no help in deciding when this specific creation tool is appropriate versus other issue-related operations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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