We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sooperset/mcp-atlassian'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""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
# Cloud uses v3 API, Server/Data Center uses v2 API
if self.config.is_cloud:
endpoint = f"rest/api/3/issue/{issue_key}/remotelink"
else:
endpoint = f"rest/api/2/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