Skip to main content
Glama

create_jira_issue

Create a new Jira issue with specified project, summary, description, and issue type. Automatically handles common issue types like 'Bug', 'Task', 'Story', and 'Epic'. Supports additional fields for customization.

Instructions

Create a new Jira issue. Common issue types include 'Bug', 'Task', 'Story', 'Epic' (capitalization handled automatically)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
descriptionYesIssue description
fieldsNoAdditional fields for the issue (optional)
issue_typeYesIssue type (e.g., 'Bug', 'Task', 'Story', 'Epic', 'New Feature', 'Improvement'). IMPORTANT: Types are case-sensitive and vary by Jira instance.
projectYesProject key (e.g., 'MYPROJ')
summaryYesIssue summary/title

Implementation Reference

  • Main handler function that processes input parameters, formats the issue payload, handles issue type normalization, calls the v3 API to create the issue, and returns a JiraIssueResult.
    async def create_jira_issue(
        self,
        project: str,
        summary: str,
        description: str,
        issue_type: str,
        fields: Optional[Dict[str, Any]] = None,
    ) -> JiraIssueResult:
        """Create a new Jira issue using v3 REST API
    
        Args:
            project: Project key (e.g., 'PROJ')
            summary: Issue summary/title
            description: Issue description
            issue_type: Issue type - common values include 'Bug', 'Task', 'Story', 'Epic', 'New Feature', 'Improvement'
                       Note: Available issue types vary by Jira instance and project
            fields: Optional additional fields dictionary
    
        Returns:
            JiraIssueResult object with the created issue details
    
        Example:
            # Create a bug
            await create_jira_issue(
                project='PROJ',
                summary='Login button not working',
                description='The login button on the homepage is not responding to clicks',
                issue_type='Bug'
            )
    
            # Create a task with custom fields
            await create_jira_issue(
                project='PROJ',
                summary='Update documentation',
                description='Update API documentation with new endpoints',
                issue_type='Task',
                fields={
                    'assignee': 'jsmith',
                    'labels': ['documentation', 'api'],
                    'priority': {'name': 'High'}
                }
            )
        """
        logger.info("Starting create_jira_issue...")
    
        try:
            # Create a properly formatted issue dictionary
            issue_dict = {}
    
            # Process required fields first
            # Project field - required
            if isinstance(project, str):
                issue_dict["project"] = {"key": project}
            else:
                issue_dict["project"] = project
    
            # Summary - required
            issue_dict["summary"] = summary
    
            # Description
            if description:
                issue_dict["description"] = description
    
            # Issue type - required, with validation for common issue types
            logger.info(
                f"Processing issue_type: '{issue_type}' (type: {type(issue_type)})"
            )
            common_types = [
                "bug",
                "task",
                "story",
                "epic",
                "improvement",
                "newfeature",
                "new feature",
            ]
    
            if isinstance(issue_type, str):
                # Check for common issue type variants and fix case-sensitivity issues
                issue_type_lower = issue_type.lower()
    
                if issue_type_lower in common_types:
                    # Convert first letter to uppercase for standard Jira types
                    issue_type_proper = issue_type_lower.capitalize()
                    if (
                        issue_type_lower == "new feature"
                        or issue_type_lower == "newfeature"
                    ):
                        issue_type_proper = "New Feature"
    
                    logger.info(
                        f"Note: Converting issue type from '{issue_type}' to '{issue_type_proper}'"
                    )
                    issue_dict["issuetype"] = {"name": issue_type_proper}
                else:
                    # Use the type as provided - some Jira instances have custom types
                    issue_dict["issuetype"] = {"name": issue_type}
            else:
                issue_dict["issuetype"] = issue_type
    
            # Add any additional fields with proper type handling
            if fields:
                for key, value in fields.items():
                    # Skip fields we've already processed
                    if key in [
                        "project",
                        "summary",
                        "description",
                        "issuetype",
                        "issue_type",
                    ]:
                        continue
    
                    # Handle special fields that require specific formats
                    if key == "assignees" or key == "assignee":
                        # Convert string to array for assignees or proper format for assignee
                        if isinstance(value, str):
                            if key == "assignees":
                                issue_dict[key] = [value] if value else []
                            else:  # assignee
                                issue_dict[key] = {"name": value} if value else None
                        elif isinstance(value, list) and key == "assignee" and value:
                            # If assignee is a list but should be a dict with name
                            issue_dict[key] = {"name": value[0]}
                        else:
                            issue_dict[key] = value
                    elif key == "labels":
                        # Convert string to array for labels
                        if isinstance(value, str):
                            issue_dict[key] = [value] if value else []
                        else:
                            issue_dict[key] = value
                    elif key == "milestone":
                        # Convert string to number for milestone
                        if isinstance(value, str) and value.isdigit():
                            issue_dict[key] = int(value)
                        else:
                            issue_dict[key] = value
                    else:
                        issue_dict[key] = value
    
            # Use v3 API client
            v3_client = self._get_v3_api_client()
            response_data = await v3_client.create_issue(fields=issue_dict)
    
            # Extract issue details from v3 API response
            issue_key = response_data.get("key")
            issue_id = response_data.get("id")
    
            logger.info(f"Successfully created issue {issue_key} (ID: {issue_id})")
    
            # Return JiraIssueResult with the created issue details
            # For v3 API, we return what we have from the create response
            return JiraIssueResult(
                key=issue_key,
                summary=summary,  # Use the summary we provided
                description=description,  # Use the description we provided
                status="Open",  # Default status for new issues
            )
    
        except Exception as e:
            error_msg = f"Failed to create issue: {type(e).__name__}: {str(e)}"
            logger.error(error_msg, exc_info=True)
    
            # Enhanced error handling for issue type errors
            if "issuetype" in str(e).lower() or "issue type" in str(e).lower():
                logger.info(
                    "Issue type error detected, trying to provide helpful suggestions..."
                )
                try:
                    project_key = (
                        project if isinstance(project, str) else project.get("key")
                    )
                    if project_key:
                        issue_types = await self.get_jira_project_issue_types(
                            project_key
                        )
                        type_names = [t.get("name") for t in issue_types]
                        logger.info(
                            f"Available issue types for project {project_key}: {', '.join(type_names)}"
                        )
    
                        # Try to find the closest match
                        attempted_type = issue_type
                        closest = None
                        attempted_lower = attempted_type.lower()
                        for t in type_names:
                            if (
                                attempted_lower in t.lower()
                                or t.lower() in attempted_lower
                            ):
                                closest = t
                                break
    
                        if closest:
                            logger.info(
                                f"The closest match to '{attempted_type}' is '{closest}'"
                            )
                            error_msg += f" Available types: {', '.join(type_names)}. Closest match: '{closest}'"
                        else:
                            error_msg += f" Available types: {', '.join(type_names)}"
                except Exception as fetch_error:
                    logger.error(f"Could not fetch issue types: {str(fetch_error)}")
    
            raise ValueError(error_msg)
  • Tool call dispatching in call_tool() that handles invocation of create_jira_issue by extracting arguments and calling the handler method.
    case JiraTools.CREATE_ISSUE.value:
        logger.info("About to AWAIT jira_server.create_jira_issue...")
        required_args = ["project", "summary", "description", "issue_type"]
        if not all(arg in arguments for arg in required_args):
            missing = [arg for arg in required_args if arg not in arguments]
            raise ValueError(
                f"Missing required arguments: {', '.join(missing)}"
            )
        result = await jira_server.create_jira_issue(
            arguments["project"],
            arguments["summary"],
            arguments["description"],
            arguments["issue_type"],
            arguments.get("fields", {}),
        )
        logger.info("COMPLETED await jira_server.create_jira_issue.")
  • Registers the create_jira_issue tool in list_tools() with name, description, and detailed input schema definition.
    Tool(
        name=JiraTools.CREATE_ISSUE.value,
        description="Create a new Jira issue. Common issue types include 'Bug', 'Task', 'Story', 'Epic' (capitalization handled automatically)",
        inputSchema={
            "type": "object",
            "properties": {
                "project": {
                    "type": "string",
                    "description": "Project key (e.g., 'MYPROJ')",
                },
                "summary": {
                    "type": "string",
                    "description": "Issue summary/title",
                },
                "description": {
                    "type": "string",
                    "description": "Issue description",
                },
                "issue_type": {
                    "type": "string",
                    "description": "Issue type (e.g., 'Bug', 'Task', 'Story', 'Epic', 'New Feature', 'Improvement'). IMPORTANT: Types are case-sensitive and vary by Jira instance.",
                },
                "fields": {
                    "type": "object",
                    "description": "Additional fields for the issue (optional)",
                },
            },
            "required": ["project", "summary", "description", "issue_type"],
        },
    ),
  • Pydantic model defining the output structure returned by the create_jira_issue handler.
    class JiraIssueResult(BaseModel):
        key: str
        summary: str
        description: Optional[str] = None
        status: Optional[str] = None
        assignee: Optional[str] = None
        reporter: Optional[str] = None
        created: Optional[str] = None
        updated: Optional[str] = None
        fields: Optional[Dict[str, Any]] = None
        comments: Optional[List[Dict[str, Any]]] = None
        watchers: Optional[Dict[str, Any]] = None
        attachments: Optional[List[Dict[str, Any]]] = None
        subtasks: Optional[List[Dict[str, Any]]] = None
        project: Optional[Dict[str, Any]] = None
        issue_links: Optional[List[Dict[str, Any]]] = None
        worklog: Optional[List[Dict[str, Any]]] = None
        timetracking: Optional[Dict[str, Any]] = None
  • Low-level helper in JiraV3APIClient that performs the actual HTTP POST to create a single Jira issue via v3 REST API.
    async def create_issue(
        self,
        fields: Dict[str, Any],
        update: Optional[Dict[str, Any]] = None,
        history_metadata: Optional[Dict[str, Any]] = None,
        properties: Optional[list] = None,
        transition: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Create an issue using the v3 REST API.
    
        Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask.
        A transition may be applied, to move the issue or subtask to a workflow step other than
        the default start step, and issue properties set.
    
        Args:
            fields: Dict containing field names and values (required).
                   Must include project, summary, description, and issuetype.
            update: Dict containing update operations for fields
            history_metadata: Optional history metadata for the issue creation
            properties: Optional list of properties to set
            transition: Optional transition to apply after creation
    
        Returns:
            Dictionary containing the created issue details:
            - id: Issue ID
            - key: Issue key
            - self: URL to the created issue
            - transition: Transition result if applied
    
        Raises:
            ValueError: If required parameters are missing or creation fails
        """
        if not fields:
            raise ValueError("fields is required")
    
        # Build the request payload
        payload = {"fields": fields}
    
        # Add optional parameters
        if update:
            payload["update"] = update
    
        if history_metadata:
            payload["historyMetadata"] = history_metadata
    
        if properties:
            payload["properties"] = properties
    
        if transition:
            payload["transition"] = transition
    
        endpoint = "/issue"
        logger.debug(f"Creating issue with v3 API endpoint: {endpoint}")
        logger.debug(f"Create issue payload: {json.dumps(payload, indent=2)}")
    
        response_data = await self._make_v3_api_request("POST", endpoint, data=payload)
        logger.debug(f"Create issue response: {response_data}")
        return response_data
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure. It states the tool creates an issue but doesn't mention authentication requirements, rate limits, side effects (e.g., notifications sent), or what happens on failure (e.g., duplicate issues). The mention of 'capitalization handled automatically' for issue types adds some context but is insufficient for a mutation tool with zero annotation coverage.

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

Conciseness5/5

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

The description is a single, efficient sentence that front-loads the core purpose ('Create a new Jira issue') and adds only essential supplemental information about issue types. There is no wasted verbiage or redundant phrasing, making it highly concise and well-structured.

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

Completeness2/5

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

For a mutation tool with no annotations and no output schema, the description is inadequate. It doesn't cover behavioral aspects like permissions, error handling, or response format, nor does it address usage relative to siblings. The high schema coverage helps with inputs, but overall completeness is poor given the tool's complexity and lack of structured metadata.

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?

Schema description coverage is 100%, so the schema already documents all 5 parameters thoroughly. The description adds minimal value by listing common issue types and noting capitalization handling, but doesn't explain parameter interactions, default values, or provide examples beyond what's in the schema. Baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose4/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 Jira issue', making the purpose immediately understandable. It distinguishes from siblings like 'add_jira_comment' or 'get_jira_issue' by specifying creation rather than modification or retrieval. However, it doesn't explicitly differentiate from 'create_jira_issues' (plural), which might handle bulk creation.

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 'create_jira_issues' (for bulk operations) or 'create_jira_project' (for different resources). It mentions common issue types but doesn't specify prerequisites, error conditions, or contextual triggers for selection among sibling tools.

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

Related 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/InfinitIQ-Tech/mcp-jira'

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