MCP Atlassian

"""Module for Confluence space operations.""" import logging from typing import cast import requests from .client import ConfluenceClient logger = logging.getLogger("mcp-atlassian") class SpacesMixin(ConfluenceClient): """Mixin for Confluence space operations.""" def get_spaces(self, start: int = 0, limit: int = 10) -> dict[str, object]: """ Get all available spaces. Args: start: The starting index for pagination limit: Maximum number of spaces to return Returns: Dictionary containing space information with results and metadata """ spaces = self.confluence.get_all_spaces(start=start, limit=limit) # Cast the return value to the expected type return cast(dict[str, object], spaces) def get_user_contributed_spaces(self, limit: int = 250) -> dict: """ Get spaces the current user has contributed to. Args: limit: Maximum number of results to return Returns: Dictionary of space keys to space information """ try: # Use CQL to find content the user has contributed to cql = "contributor = currentUser() order by lastmodified DESC" results = self.confluence.cql(cql=cql, limit=limit) # Extract and deduplicate spaces spaces = {} for result in results.get("results", []): space_key = None space_name = None # Try to extract space from container if "resultGlobalContainer" in result: container = result.get("resultGlobalContainer", {}) space_name = container.get("title") display_url = container.get("displayUrl", "") if display_url and "/spaces/" in display_url: space_key = display_url.split("/spaces/")[1].split("/")[0] # Try to extract from content expandable if ( not space_key and "content" in result and "_expandable" in result["content"] ): expandable = result["content"].get("_expandable", {}) space_path = expandable.get("space", "") if space_path and space_path.startswith("/rest/api/space/"): space_key = space_path.split("/rest/api/space/")[1] # Try to extract from URL if not space_key and "url" in result: url = result.get("url", "") if url and url.startswith("/spaces/"): space_key = url.split("/spaces/")[1].split("/")[0] # Only add if we found a space key and it's not already in our results if space_key and space_key not in spaces: # Add some defaults if we couldn't extract all fields space_name = space_name or f"Space {space_key}" spaces[space_key] = {"key": space_key, "name": space_name} return spaces except KeyError as e: logger.error(f"Missing key in Confluence spaces data: {str(e)}") return {} except ValueError as e: logger.error(f"Invalid value in Confluence spaces: {str(e)}") return {} except TypeError as e: logger.error(f"Type error when processing Confluence spaces: {str(e)}") return {} except requests.RequestException as e: logger.error(f"Network error when fetching spaces: {str(e)}") return {} except Exception as e: # noqa: BLE001 - Intentional fallback with logging logger.error(f"Unexpected error fetching Confluence spaces: {str(e)}") logger.debug("Full exception details for Confluence spaces:", exc_info=True) return {}