Skip to main content
Glama

MCP Atlassian

projects.py14.9 kB
"""Module for Jira project operations.""" import logging from typing import Any from ..models import JiraProject from ..models.jira.search import JiraSearchResult from ..models.jira.version import JiraVersion from .client import JiraClient from .protocols import SearchOperationsProto logger = logging.getLogger("mcp-jira") class ProjectsMixin(JiraClient, SearchOperationsProto): """Mixin for Jira project operations. This mixin provides methods for retrieving and working with Jira projects, including project details, components, versions, and other project-related operations. """ def get_all_projects(self, include_archived: bool = False) -> list[dict[str, Any]]: """ Get all projects visible to the current user. Args: include_archived: Whether to include archived projects Returns: List of project data dictionaries """ try: params = {} if include_archived: params["includeArchived"] = "true" projects = self.jira.projects(included_archived=include_archived) return projects if isinstance(projects, list) else [] except Exception as e: logger.error(f"Error getting all projects: {str(e)}") return [] def get_project(self, project_key: str) -> dict[str, Any] | None: """ Get project information by key. Args: project_key: The project key (e.g. 'PROJ') Returns: Project data or None if not found """ try: project_data = self.jira.project(project_key) if not isinstance(project_data, dict): msg = f"Unexpected return value type from `jira.project`: {type(project_data)}" logger.error(msg) raise TypeError(msg) return project_data except Exception as e: logger.warning(f"Error getting project {project_key}: {e}") return None def get_project_model(self, project_key: str) -> JiraProject | None: """ Get project information as a JiraProject model. Args: project_key: The project key (e.g. 'PROJ') Returns: JiraProject model or None if not found """ project_data = self.get_project(project_key) if not project_data: return None return JiraProject.from_api_response(project_data) def project_exists(self, project_key: str) -> bool: """ Check if a project exists. Args: project_key: The project key to check Returns: True if the project exists, False otherwise """ try: project = self.get_project(project_key) return project is not None except Exception: return False def get_project_components(self, project_key: str) -> list[dict[str, Any]]: """ Get all components for a project. Args: project_key: The project key Returns: List of component data dictionaries """ try: components = self.jira.get_project_components(key=project_key) return components if isinstance(components, list) else [] except Exception as e: logger.error( f"Error getting components for project {project_key}: {str(e)}" ) return [] def get_project_versions(self, project_key: str) -> list[dict[str, Any]]: """ Get all versions for a project. Args: project_key: The project key. Returns: List of version data dictionaries """ try: raw_versions = self.jira.get_project_versions(key=project_key) if not isinstance(raw_versions, list): return [] versions: list[dict[str, Any]] = [] for v in raw_versions: ver = JiraVersion.from_api_response(v) versions.append(ver.to_simplified_dict()) return versions except Exception as e: logger.error(f"Error getting versions for project {project_key}: {str(e)}") return [] def get_project_roles(self, project_key: str) -> dict[str, Any]: """ Get all roles for a project. Args: project_key: The project key Returns: Dictionary of role names mapped to role details """ try: roles = self.jira.get_project_roles(project_key=project_key) return roles if isinstance(roles, dict) else {} except Exception as e: logger.error(f"Error getting roles for project {project_key}: {str(e)}") return {} def get_project_role_members( self, project_key: str, role_id: str ) -> list[dict[str, Any]]: """ Get members assigned to a specific role in a project. Args: project_key: The project key role_id: The role ID Returns: List of role members """ try: members = self.jira.get_project_actors_for_role_project( project_key=project_key, role_id=role_id ) # Extract the actors from the response actors = [] if isinstance(members, dict) and "actors" in members: actors = members.get("actors", []) return actors except Exception as e: logger.error( f"Error getting role members for project {project_key}, role {role_id}: {str(e)}" ) return [] def get_project_permission_scheme(self, project_key: str) -> dict[str, Any] | None: """ Get the permission scheme for a project. Args: project_key: The project key Returns: Permission scheme data if found, None otherwise """ try: scheme = self.jira.get_project_permission_scheme( project_id_or_key=project_key ) if not isinstance(scheme, dict): msg = f"Unexpected return value type from `jira.get_project_permission_scheme`: {type(scheme)}" logger.error(msg) raise TypeError(msg) return scheme except Exception as e: logger.error( f"Error getting permission scheme for project {project_key}: {str(e)}" ) return None def get_project_notification_scheme( self, project_key: str ) -> dict[str, Any] | None: """ Get the notification scheme for a project. Args: project_key: The project key Returns: Notification scheme data if found, None otherwise """ try: scheme = self.jira.get_project_notification_scheme( project_id_or_key=project_key ) if not isinstance(scheme, dict): msg = f"Unexpected return value type from `jira.get_project_notification_scheme`: {type(scheme)}" logger.error(msg) raise TypeError(msg) return scheme except Exception as e: logger.error( f"Error getting notification scheme for project {project_key}: {str(e)}" ) return None def get_project_issue_types(self, project_key: str) -> list[dict[str, Any]]: """ Get all issue types available for a project. Args: project_key: The project key Returns: List of issue type data dictionaries """ try: meta = self.jira.issue_createmeta(project=project_key) if not isinstance(meta, dict): msg = f"Unexpected return value type from `jira.issue_createmeta`: {type(meta)}" logger.error(msg) raise TypeError(msg) issue_types = [] # Extract issue types from createmeta response if "projects" in meta and len(meta["projects"]) > 0: project_data = meta["projects"][0] if "issuetypes" in project_data: issue_types = project_data["issuetypes"] return issue_types except Exception as e: logger.error( f"Error getting issue types for project {project_key}: {str(e)}" ) return [] def get_project_issues_count(self, project_key: str) -> int: """ Get the total number of issues in a project. Args: project_key: The project key Returns: Count of issues in the project """ try: # Use JQL to count issues in the project jql = f'project = "{project_key}"' result = self.jira.jql(jql=jql, fields="key", limit=1) if not isinstance(result, dict): msg = f"Unexpected return value type from `jira.jql`: {type(result)}" logger.error(msg) raise TypeError(msg) # Extract total from the response total = 0 if isinstance(result, dict) and "total" in result: total = result.get("total", 0) return total except Exception as e: logger.error( f"Error getting issue count for project {project_key}: {str(e)}" ) return 0 def get_project_issues( self, project_key: str, start: int = 0, limit: int = 50 ) -> JiraSearchResult: """ Get issues for a specific project. Args: project_key: The project key start: Index of the first issue to return limit: Maximum number of issues to return Returns: List of JiraIssue models representing the issues """ try: # Use JQL to get issues in the project jql = f'project = "{project_key}"' return self.search_issues(jql, start=start, limit=limit) except Exception as e: logger.error(f"Error getting issues for project {project_key}: {str(e)}") return JiraSearchResult(issues=[], total=0) def get_project_keys(self) -> list[str]: """ Get all project keys. Returns: List of project keys """ try: projects = self.get_all_projects() project_keys: list[str] = [] for project in projects: key = project.get("key") if not isinstance(key, str): msg = f"Unexpected return value type from `get_all_projects`: {type(key)}" logger.error(msg) raise TypeError(msg) project_keys.append(key) return project_keys except Exception as e: logger.error(f"Error getting project keys: {str(e)}") return [] def get_project_leads(self) -> dict[str, str]: """ Get all project leads mapped to their projects. Returns: Dictionary mapping project keys to lead usernames """ try: projects = self.get_all_projects() leads = {} for project in projects: if "key" in project and "lead" in project: key = project.get("key") lead = project.get("lead", {}) # Handle different formats of lead information lead_name = None if isinstance(lead, dict): lead_name = lead.get("name") or lead.get("displayName") elif isinstance(lead, str): lead_name = lead if key and lead_name: leads[key] = lead_name return leads except Exception as e: logger.error(f"Error getting project leads: {str(e)}") return {} def get_user_accessible_projects(self, username: str) -> list[dict[str, Any]]: """ Get projects that a specific user can access. Args: username: The username to check access for Returns: List of accessible project data dictionaries """ try: # This requires admin permissions # For non-admins, a different approach might be needed all_projects = self.get_all_projects() accessible_projects = [] for project in all_projects: project_key = project.get("key") if not project_key: continue try: # Check if user has browse permission for this project browse_users = ( self.jira.get_users_with_browse_permission_to_a_project( username=username, project_key=project_key, limit=1 ) ) # If the user is in the list, they have access user_has_access = False if isinstance(browse_users, list): for user in browse_users: if isinstance(user, dict) and user.get("name") == username: user_has_access = True break if user_has_access: accessible_projects.append(project) except Exception: # Skip projects that cause errors continue return accessible_projects except Exception as e: logger.error( f"Error getting accessible projects for user {username}: {str(e)}" ) return [] def create_project_version( self, project_key: str, name: str, start_date: str = None, release_date: str = None, description: str = None, ) -> dict[str, Any]: """ Create a new version in the specified Jira project. Args: project_key: The project key (e.g., 'PROJ') name: The name of the version start_date: The start date (YYYY-MM-DD, optional) release_date: The release date (YYYY-MM-DD, optional) description: Description of the version (optional) Returns: The created version object as returned by Jira """ return self.create_version( project=project_key, name=name, start_date=start_date, release_date=release_date, description=description, )

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

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