Skip to main content
Glama
jira_client.py4.92 kB
"""JIRA client with authentication and custom fields support.""" import logging from typing import Any, Dict, Optional from jira import JIRA from jira.exceptions import JIRAError from requests import Session from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry from mcp_jira.auth import AuthManager from mcp_jira.client.custom_fields import CustomFieldsManager from mcp_jira.config import Config logger = logging.getLogger(__name__) class JiraClient: """Enhanced JIRA client with authentication and custom fields support.""" def __init__(self, config: Config): """Initialize JIRA client. Args: config: Application configuration """ self.config = config self.auth_manager = AuthManager(config) self._jira: Optional[JIRA] = None self._custom_fields_manager: Optional[CustomFieldsManager] = None def _create_session(self) -> Session: """Create requests session with retry logic. Returns: Configured requests Session """ session = Session() # Configure retries retry_strategy = Retry( total=self.config.max_retries, backoff_factor=self.config.retry_backoff_factor, status_forcelist=[429, 500, 502, 503, 504], allowed_methods=["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE", "POST"], ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) # Set authentication session.auth = self.auth_manager.get_auth() return session def connect(self) -> JIRA: """Connect to JIRA and return authenticated client. Returns: Authenticated JIRA client instance Raises: JIRAError: If connection fails """ if self._jira is not None: return self._jira try: # Create session with authentication session = self._create_session() # Initialize JIRA client options = { "server": self.config.jira_url, "rest_api_version": self.config.jira_api_version, "timeout": self.config.request_timeout, } logger.info(f"Connecting to JIRA at {self.config.jira_url}") self._jira = JIRA(options=options, session=session) # Test connection server_info = self._jira.server_info() logger.info( f"Successfully connected to JIRA {server_info.get('version', 'unknown')}" ) # Initialize custom fields manager self._custom_fields_manager = CustomFieldsManager(self._jira) return self._jira except JIRAError as e: logger.error(f"Failed to connect to JIRA: {e}") raise except Exception as e: logger.error(f"Unexpected error connecting to JIRA: {e}") raise @property def jira(self) -> JIRA: """Get JIRA client instance, connecting if necessary. Returns: Authenticated JIRA client """ if self._jira is None: self.connect() return self._jira # type: ignore @property def custom_fields(self) -> CustomFieldsManager: """Get custom fields manager. Returns: CustomFieldsManager instance """ if self._custom_fields_manager is None: # Ensure connection is established self.connect() return self._custom_fields_manager # type: ignore def reconnect(self) -> JIRA: """Reconnect to JIRA (useful after credential refresh). Returns: New authenticated JIRA client instance """ logger.info("Reconnecting to JIRA") self._jira = None self._custom_fields_manager = None self.auth_manager.refresh_credentials() return self.connect() def test_connection(self) -> Dict[str, Any]: """Test JIRA connection and return server info. Returns: Dictionary with server information Raises: JIRAError: If connection test fails """ try: jira = self.jira server_info = jira.server_info() current_user = jira.current_user() return { "connected": True, "server_version": server_info.get("version"), "server_title": server_info.get("serverTitle"), "base_url": server_info.get("baseUrl"), "current_user": current_user, } except JIRAError as e: logger.error(f"Connection test failed: {e}") return { "connected": False, "error": str(e), }

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/tarangbhavsar/mcp-jira-server'

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