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
| Name | Required | Description | Default |
|---|---|---|---|
| description | Yes | Issue description | |
| fields | No | Additional fields for the issue (optional) | |
| issue_type | Yes | Issue type (e.g., 'Bug', 'Task', 'Story', 'Epic', 'New Feature', 'Improvement'). IMPORTANT: Types are case-sensitive and vary by Jira instance. | |
| project | Yes | Project key (e.g., 'MYPROJ') | |
| summary | Yes | Issue summary/title |
Implementation Reference
- src/mcp_server_jira/server.py:475-680 (handler)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)
- src/mcp_server_jira/server.py:1435-1451 (registration)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.")
- src/mcp_server_jira/server.py:1217-1246 (registration)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"], }, ),
- src/mcp_server_jira/server.py:76-94 (schema)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