Skip to main content
Glama

create_jira_issues

Bulk create Jira issues using field dictionaries for multiple entries. Specify issue types like 'Bug', 'Task', or 'Story' as per your Jira instance, and optionally reload created issues.

Instructions

Bulk create new Jira issues. IMPORTANT: For 'issue_type', use the exact case-sensitive types in your Jira instance (common: 'Bug', 'Task', 'Story', 'Epic')

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
field_listYesA list of field dictionaries, each representing an issue to create
prefetchNoWhether to reload created issues (default: true)

Implementation Reference

  • Main handler: validates and transforms input field_list to v3 API format, handles description ADF conversion, issue type normalization, calls bulk_create_issues on v3 client, processes issues and errors in response.
    async def create_jira_issues(
        self, field_list: List[Dict[str, Any]], prefetch: bool = True
    ) -> List[Dict[str, Any]]:
        """Bulk create new Jira issues using v3 REST API.
    
        Parameters:
            field_list (List[Dict[str, Any]]): a list of dicts each containing field names and the values to use.
                                             Each dict is an individual issue to create.
            prefetch (bool): True reloads the created issue Resource so all of its data is present in the value returned (Default: True)
    
        Returns:
            List[Dict[str, Any]]: List of created issues with their details
    
        Issue Types:
            Common issue types include: 'Bug', 'Task', 'Story', 'Epic', 'New Feature', 'Improvement'
            Note: Available issue types vary by Jira instance and project
    
        Example:
            # Create multiple issues in bulk
            await create_jira_issues([
                {
                    'project': 'PROJ',
                    'summary': 'Implement user authentication',
                    'description': 'Add login and registration functionality',
                    'issue_type': 'Story'  # Note: case-sensitive, match to your Jira instance types
                },
                {
                    'project': 'PROJ',
                    'summary': 'Fix navigation bar display on mobile',
                    'description': 'Navigation bar is not displaying correctly on mobile devices',
                    'issue_type': 'Bug',
                    'priority': {'name': 'High'},
                    'labels': ['mobile', 'ui']
                }
            ])
        """
        logger.info("Starting create_jira_issues...")
    
        try:
            # Process each field dict to ensure proper formatting for v3 API
            processed_field_list = []
            for fields in field_list:
                # Create a properly formatted issue dictionary
                issue_dict = {}
    
                # Process required fields first to ensure they exist
                # Project field - required
                if "project" not in fields:
                    raise ValueError("Each issue must have a 'project' field")
                project_value = fields["project"]
                if isinstance(project_value, str):
                    issue_dict["project"] = {"key": project_value}
                else:
                    issue_dict["project"] = project_value
    
                # Summary field - required
                if "summary" not in fields:
                    raise ValueError("Each issue must have a 'summary' field")
                issue_dict["summary"] = fields["summary"]
    
                # Description field - convert to ADF format for v3 API if it's a simple string
                if "description" in fields:
                    description = fields["description"]
                    if isinstance(description, str):
                        # Convert simple string to Atlassian Document Format
                        issue_dict["description"] = {
                            "type": "doc",
                            "version": 1,
                            "content": [
                                {
                                    "type": "paragraph",
                                    "content": [
                                        {
                                            "type": "text",
                                            "text": description
                                        }
                                    ]
                                }
                            ]
                        }
                    else:
                        # Assume it's already in ADF format
                        issue_dict["description"] = description
    
                # Issue type field - required, handle both 'issuetype' and 'issue_type'
                issue_type = None
                if "issuetype" in fields:
                    issue_type = fields["issuetype"]
                elif "issue_type" in fields:
                    issue_type = fields["issue_type"]
                else:
                    raise ValueError(
                        "Each issue must have an 'issuetype' or 'issue_type' field"
                    )
    
                # Check for common issue type variants and fix case-sensitivity issues
                logger.debug(
                    f"Processing bulk issue_type: '{issue_type}' (type: {type(issue_type)})"
                )
                common_types = [
                    "bug",
                    "task",
                    "story",
                    "epic",
                    "improvement",
                    "newfeature",
                    "new feature",
                ]
    
                if isinstance(issue_type, str):
                    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.debug(
                            f"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
    
                # Process other fields
                for key, value in fields.items():
                    if key in [
                        "project",
                        "summary",
                        "description",
                        "issuetype",
                        "issue_type",
                    ]:
                        # Skip fields we've already processed
                        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
    
                # Add to the field list in v3 API format
                processed_field_list.append({"fields": issue_dict})
    
            logger.debug(f"Processed field list: {json.dumps(processed_field_list, indent=2)}")
    
            # Use v3 API client
            v3_client = self._get_v3_api_client()
            
            # Call the bulk create API
            response_data = await v3_client.bulk_create_issues(processed_field_list)
            
            # Process the results to maintain compatibility with existing interface
            processed_results = []
            
            # Handle successful issues
            if "issues" in response_data:
                for issue in response_data["issues"]:
                    processed_results.append({
                        "key": issue.get("key"),
                        "id": issue.get("id"),
                        "self": issue.get("self"),
                        "success": True,
                    })
            
            # Handle errors
            if "errors" in response_data:
                for error in response_data["errors"]:
                    processed_results.append({
                        "error": error,
                        "success": False,
                    })
    
            logger.info(f"Successfully processed {len(processed_results)} issue creations")
            return processed_results
    
        except Exception as e:
            error_msg = f"Failed to create issues in bulk: {type(e).__name__}: {str(e)}"
            logger.error(error_msg, exc_info=True)
            print(error_msg)
            raise ValueError(error_msg)
  • Tool registration in list_tools(): defines name 'create_jira_issues', description, and inputSchema for field_list array.
        name=JiraTools.CREATE_ISSUES.value,
        description="Bulk create new Jira issues. IMPORTANT: For 'issue_type', use the exact case-sensitive types in your Jira instance (common: 'Bug', 'Task', 'Story', 'Epic')",
        inputSchema={
            "type": "object",
            "properties": {
                "field_list": {
                    "type": "array",
                    "description": "A list of field dictionaries, each representing an issue to create",
                    "items": {
                        "type": "object",
                        "description": "Field dictionary for a single issue",
                    },
                },
                "prefetch": {
                    "type": "boolean",
                    "description": "Whether to reload created issues (default: true)",
                },
            },
            "required": ["field_list"],
        },
    ),
  • Tool dispatch in call_tool(): extracts arguments, calls jira_server.create_jira_issues(field_list, prefetch).
    case JiraTools.CREATE_ISSUES.value:
        logger.info("Calling async tool create_jira_issues...")
        field_list = arguments.get("field_list")
        if not field_list:
            raise ValueError("Missing required argument: field_list")
        prefetch = arguments.get("prefetch", True)
        result = await jira_server.create_jira_issues(field_list, prefetch)
        logger.info("Async tool create_jira_issues completed.")
  • Low-level helper: makes HTTP POST to /rest/api/3/issue/bulk with issueUpdates payload, handles response with issues and errors.
    async def bulk_create_issues(
        self, 
        issue_updates: list
    ) -> Dict[str, Any]:
        """
        Bulk create issues using the v3 REST API.
    
        Creates up to 50 issues and, where the option to create subtasks is enabled in Jira,
        subtasks. Transitions may be applied, to move the issues or subtasks to a workflow
        step other than the default start step, and issue properties set.
    
        Args:
            issue_updates: List of issue creation specifications. Each item should contain
                          'fields' dict with issue fields, and optionally 'update' dict
                          for additional operations during creation.
    
        Returns:
            Dict containing:
            - issues: List of successfully created issues with their details
            - errors: List of errors for failed issue creations
    
        Raises:
            ValueError: If required parameters are missing or bulk creation fails
        """
        if not issue_updates:
            raise ValueError("issue_updates list cannot be empty")
    
        if len(issue_updates) > 50:
            raise ValueError("Cannot create more than 50 issues in a single bulk operation")
    
        # Build the request payload for v3 API
        payload = {"issueUpdates": issue_updates}
    
        endpoint = "/issue/bulk"
        logger.debug(f"Bulk creating issues with v3 API endpoint: {endpoint}")
        logger.debug(f"Payload: {json.dumps(payload, indent=2)}")
    
        response_data = await self._make_v3_api_request("POST", endpoint, data=payload)
        logger.debug(f"Bulk create response: {json.dumps(response_data, indent=2)}")
    
        return response_data
  • Helper: lazily initializes the JiraV3APIClient used for all v3 API calls including bulk_create_issues.
    def _get_v3_api_client(self) -> JiraV3APIClient:
        """Get or create a v3 API client instance"""
        if not self._v3_api_client:
            self._v3_api_client = JiraV3APIClient(
                server_url=self.server_url,
                username=self.username,
                password=self.password,
                token=self.token,
            )
        return self._v3_api_client
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 mentions the bulk nature and case-sensitivity requirement, but fails to address critical aspects like authentication needs, rate limits, error handling, whether the operation is idempotent, or what happens on partial failures. For a mutation tool with zero annotation coverage, this leaves significant gaps.

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 two sentences: one stating the core purpose and another providing critical implementation detail. Both sentences earn their place, with the second addressing a common pitfall. There's no wasted verbiage, though it could benefit from more complete behavioral context.

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 bulk creation tool with no annotations and no output schema, the description is insufficient. It doesn't explain what the tool returns, how errors are handled, whether there are size limits for bulk operations, or what permissions are required. The case-sensitivity warning is helpful but doesn't compensate for the broader contextual gaps.

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 both parameters thoroughly. The description adds specific guidance about case-sensitivity for 'issue_type' values, which provides useful context beyond the schema. However, it doesn't explain the structure of field dictionaries or provide examples, leaving the schema to carry most of the semantic weight.

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 action ('bulk create') and resource ('new Jira issues'), distinguishing it from the singular 'create_jira_issue' sibling. However, it doesn't explicitly differentiate from other creation tools like 'create_jira_project', leaving some ambiguity.

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 bulk creation tool versus the singular 'create_jira_issue' sibling, nor does it mention prerequisites, rate limits, or alternative approaches. The only contextual note is about case-sensitivity for issue types, which is parameter-specific rather than usage guidance.

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