Skip to main content
Glama

MCP Atlassian

by ArconixForge
links.py11 kB
"""Module for Jira issue link operations.""" import logging from typing import Any from requests.exceptions import HTTPError from ..exceptions import MCPAtlassianAuthenticationError from ..models.jira import JiraIssueLinkType from .client import JiraClient logger = logging.getLogger("mcp-jira") class LinksMixin(JiraClient): """Mixin for Jira issue link operations.""" def get_issue_link_types(self) -> list[JiraIssueLinkType]: """ Get all available issue link types. Returns: List of JiraIssueLinkType objects Raises: MCPAtlassianAuthenticationError: If authentication fails with the Jira API (401/403) Exception: If there is an error retrieving issue link types """ try: link_types_response = self.jira.get("rest/api/2/issueLinkType") if not isinstance(link_types_response, dict): msg = f"Unexpected return value type from `jira.get`: {type(link_types_response)}" logger.error(msg) raise TypeError(msg) link_types_data = link_types_response.get("issueLinkTypes", []) link_types = [ JiraIssueLinkType.from_api_response(link_type) for link_type in link_types_data ] return link_types except HTTPError as http_err: if http_err.response is not None and http_err.response.status_code in [ 401, 403, ]: error_msg = ( f"Authentication failed for Jira API " f"({http_err.response.status_code}). " "Token may be expired or invalid. Please verify credentials." ) logger.error(error_msg) raise MCPAtlassianAuthenticationError(error_msg) from http_err else: logger.error(f"HTTP error during API call: {http_err}", exc_info=True) raise Exception( f"Error getting issue link types: {http_err}" ) from http_err except Exception as e: error_msg = str(e) logger.error(f"Error getting issue link types: {error_msg}", exc_info=True) raise Exception(f"Error getting issue link types: {error_msg}") from e def create_issue_link(self, data: dict[str, Any]) -> dict[str, Any]: """ Create a link between two issues. Args: data: A dictionary containing the link data with the following structure: { "type": {"name": "Duplicate" }, # Link type name (e.g., "Duplicate", "Blocks", "Relates to") "inwardIssue": { "key": "ISSUE-1"}, # The issue that is the source of the link "outwardIssue": {"key": "ISSUE-2"}, # The issue that is the target of the link "comment": { # Optional comment to add to the link "body": "Linked related issue!", "visibility": { # Optional visibility settings "type": "group", "value": "jira-software-users" } } } Returns: Dictionary with the created link information Raises: ValueError: If required fields are missing MCPAtlassianAuthenticationError: If authentication fails with the Jira API (401/403) Exception: If there is an error creating the issue link """ # Validate required fields if not data.get("type"): raise ValueError("Link type is required") if not data.get("inwardIssue") or not data["inwardIssue"].get("key"): raise ValueError("Inward issue key is required") if not data.get("outwardIssue") or not data["outwardIssue"].get("key"): raise ValueError("Outward issue key is required") try: # Create the issue link self.jira.create_issue_link(data) # Return a response with the link information response = { "success": True, "message": f"Link created between {data['inwardIssue']['key']} and {data['outwardIssue']['key']}", "link_type": data["type"]["name"], "inward_issue": data["inwardIssue"]["key"], "outward_issue": data["outwardIssue"]["key"], } return response except HTTPError as http_err: if http_err.response is not None and http_err.response.status_code in [ 401, 403, ]: error_msg = ( f"Authentication failed for Jira API " f"({http_err.response.status_code}). " "Token may be expired or invalid. Please verify credentials." ) logger.error(error_msg) raise MCPAtlassianAuthenticationError(error_msg) from http_err else: logger.error(f"HTTP error during API call: {http_err}", exc_info=True) raise Exception(f"Error creating issue link: {http_err}") from http_err except Exception as e: error_msg = str(e) logger.error(f"Error creating issue link: {error_msg}", exc_info=True) raise Exception(f"Error creating issue link: {error_msg}") from e def create_remote_issue_link( self, issue_key: str, link_data: dict[str, Any] ) -> dict[str, Any]: """ Create a remote issue link (web link or Confluence link) for an issue. Args: issue_key: The key of the issue to add the link to (e.g., 'PROJ-123') link_data: A dictionary containing the remote link data with the following structure: { "object": { "url": "https://example.com/page", # The URL to link to "title": "Example Page", # The title/name of the link "summary": "Optional description of the link", # Optional description "icon": { # Optional icon configuration "url16x16": "https://example.com/icon16.png", "title": "Icon Title" } }, "relationship": "causes" # Optional relationship description } Returns: Dictionary with the created remote link information Raises: ValueError: If required fields are missing MCPAtlassianAuthenticationError: If authentication fails with the Jira API (401/403) Exception: If there is an error creating the remote issue link """ # Validate required fields if not issue_key: raise ValueError("Issue key is required") if not link_data.get("object"): raise ValueError("Link object is required") if not link_data["object"].get("url"): raise ValueError("URL is required in link object") if not link_data["object"].get("title"): raise ValueError("Title is required in link object") try: # Create the remote issue link using the Jira API endpoint = f"rest/api/3/issue/{issue_key}/remotelink" response = self.jira.post(endpoint, json=link_data) # Return a response with the link information result = { "success": True, "message": f"Remote link created for issue {issue_key}", "issue_key": issue_key, "link_title": link_data["object"]["title"], "link_url": link_data["object"]["url"], "relationship": link_data.get("relationship", ""), } return result except HTTPError as http_err: if http_err.response is not None and http_err.response.status_code in [ 401, 403, ]: error_msg = ( f"Authentication failed for Jira API " f"({http_err.response.status_code}). " "Token may be expired or invalid. Please verify credentials." ) logger.error(error_msg) raise MCPAtlassianAuthenticationError(error_msg) from http_err else: logger.error(f"HTTP error during API call: {http_err}", exc_info=True) raise Exception( f"Error creating remote issue link: {http_err}" ) from http_err except Exception as e: error_msg = str(e) logger.error( f"Error creating remote issue link: {error_msg}", exc_info=True ) raise Exception(f"Error creating remote issue link: {error_msg}") from e def remove_issue_link(self, link_id: str) -> dict[str, Any]: """ Remove a link between two issues. Args: link_id: The ID of the link to remove Returns: Dictionary with the result of the operation Raises: ValueError: If link_id is empty MCPAtlassianAuthenticationError: If authentication fails with the Jira API (401/403) Exception: If there is an error removing the issue link """ # Validate input if not link_id: raise ValueError("Link ID is required") try: # Remove the issue link self.jira.remove_issue_link(link_id) # Return a response indicating success response = { "success": True, "message": f"Link with ID {link_id} has been removed", "link_id": link_id, } return response except HTTPError as http_err: if http_err.response is not None and http_err.response.status_code in [ 401, 403, ]: error_msg = ( f"Authentication failed for Jira API " f"({http_err.response.status_code}). " "Token may be expired or invalid. Please verify credentials." ) logger.error(error_msg) raise MCPAtlassianAuthenticationError(error_msg) from http_err else: logger.error(f"HTTP error during API call: {http_err}", exc_info=True) raise Exception(f"Error removing issue link: {http_err}") from http_err except Exception as e: error_msg = str(e) logger.error(f"Error removing issue link: {error_msg}", exc_info=True) raise Exception(f"Error removing issue link: {error_msg}") from e

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/ArconixForge/mcp-atlassian'

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