We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/timohaa/simple-jira-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Jira attachment download operation implementation."""
import logging
from pathlib import Path
from typing import Any
import httpx
from src.jira.base import (
ATTACHMENT_PATH,
HTTP_NOT_FOUND,
HTTP_OK,
HTTP_TOO_MANY_REQUESTS,
HTTP_UNAUTHORIZED,
JiraClientBase,
)
from src.utils.errors import (
ATTACHMENT_NOT_FOUND,
AUTH_FAILED,
DOWNLOAD_FAILED,
RATE_LIMITED,
ErrorResponse,
error_response,
)
from src.utils.validation import sanitize_filename
logger = logging.getLogger(__name__)
class AttachmentOperation(JiraClientBase):
"""Handles Jira attachment download operations."""
async def download_attachment(
self,
attachment_id: str,
output_dir: Path,
issue_key: str,
filename: str,
) -> dict[str, Any] | ErrorResponse:
"""Download an attachment to a local file.
Args:
attachment_id: The attachment ID.
output_dir: Directory to save the file.
issue_key: The issue key (for subdirectory).
filename: The original filename.
Returns:
Download result or error response.
"""
url = f"{self.base_url}{ATTACHMENT_PATH}/{attachment_id}"
# Create safe output path
safe_filename = sanitize_filename(filename)
target_dir = output_dir / issue_key
target_dir.mkdir(parents=True, exist_ok=True)
target_path = target_dir / safe_filename
logger.info(
"Downloading attachment %s for issue %s to %s",
attachment_id,
issue_key,
target_path,
)
try:
async with self._create_client() as client:
response = await client.get(
url,
auth=self._get_auth(),
follow_redirects=True,
)
if response.status_code == HTTP_UNAUTHORIZED:
return error_response(AUTH_FAILED, "Invalid credentials")
if response.status_code == HTTP_NOT_FOUND:
return error_response(ATTACHMENT_NOT_FOUND, "Attachment not found")
if response.status_code == HTTP_TOO_MANY_REQUESTS:
return error_response(RATE_LIMITED, "Too many requests")
if response.status_code != HTTP_OK:
return error_response(
DOWNLOAD_FAILED, f"Download failed: {response.status_code}"
)
# Write file
target_path.write_bytes(response.content)
content_type = response.headers.get(
"content-type", "application/octet-stream"
)
size_kb = round(len(response.content) / 1024, 2)
return {
"success": True,
"filename": safe_filename,
"path": str(target_path),
"size_kb": size_kb,
"mime_type": content_type,
}
except httpx.RequestError as e:
logger.exception("Request failed for download_attachment")
return error_response(DOWNLOAD_FAILED, f"Download failed: {e}")