Skip to main content
Glama
ai-zerolab

MCP Email Server

by ai-zerolab

download_attachment

Download email attachments from the MCP Email Server and save them to a specified local path. Requires enabling attachment downloads in server settings.

Instructions

Download an email attachment and save it to the specified path. This feature must be explicitly enabled in settings (enable_attachment_download=true) due to security considerations.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
account_nameYesThe name of the email account.
email_idYesThe email ID (obtained from list_emails_metadata or get_emails_content).
attachment_nameYesThe name of the attachment to download (as shown in the attachments list).
save_pathYesThe absolute path where the attachment should be saved.
mailboxNoThe mailbox to search in (default: INBOX).INBOX

Implementation Reference

  • MCP tool registration for 'download_attachment' including input schema via Annotated Fields, permission check, and dispatch to account-specific handler.
    @mcp.tool(
        description="Download an email attachment and save it to the specified path. This feature must be explicitly enabled in settings (enable_attachment_download=true) due to security considerations.",
    )
    async def download_attachment(
        account_name: Annotated[str, Field(description="The name of the email account.")],
        email_id: Annotated[
            str, Field(description="The email ID (obtained from list_emails_metadata or get_emails_content).")
        ],
        attachment_name: Annotated[
            str, Field(description="The name of the attachment to download (as shown in the attachments list).")
        ],
        save_path: Annotated[str, Field(description="The absolute path where the attachment should be saved.")],
    ) -> AttachmentDownloadResponse:
        settings = get_settings()
        if not settings.enable_attachment_download:
            msg = (
                "Attachment download is disabled. Set 'enable_attachment_download=true' in settings to enable this feature."
            )
            raise PermissionError(msg)
    
        handler = dispatch_handler(account_name)
        return await handler.download_attachment(email_id, attachment_name, save_path)
  • Core implementation in EmailClient (or similar) that performs IMAP fetch, MIME parsing, attachment extraction, file saving, and returns result dict.
    async def download_attachment(
        self,
        email_id: str,
        attachment_name: str,
        save_path: str,
    ) -> dict[str, Any]:
        """Download a specific attachment from an email and save it to disk."""
        imap = self.imap_class(self.email_server.host, self.email_server.port)
        try:
            await imap._client_task
            await imap.wait_hello_from_server()
    
            await imap.login(self.email_server.user_name, self.email_server.password)
            try:
                await imap.id(name="mcp-email-server", version="1.0.0")
            except Exception as e:
                logger.warning(f"IMAP ID command failed: {e!s}")
            await imap.select("INBOX")
    
            data = await self._fetch_email_with_formats(imap, email_id)
            if not data:
                msg = f"Failed to fetch email with UID {email_id}"
                logger.error(msg)
                raise ValueError(msg)
    
            raw_email = self._extract_raw_email(data)
            if not raw_email:
                msg = f"Could not find email data for email ID: {email_id}"
                logger.error(msg)
                raise ValueError(msg)
    
            parser = BytesParser(policy=default)
            email_message = parser.parsebytes(raw_email)
    
            # Find the attachment
            attachment_data = None
            mime_type = None
    
            if email_message.is_multipart():
                for part in email_message.walk():
                    content_disposition = str(part.get("Content-Disposition", ""))
                    if "attachment" in content_disposition:
                        filename = part.get_filename()
                        if filename == attachment_name:
                            attachment_data = part.get_payload(decode=True)
                            mime_type = part.get_content_type()
                            break
    
            if attachment_data is None:
                msg = f"Attachment '{attachment_name}' not found in email {email_id}"
                logger.error(msg)
                raise ValueError(msg)
    
            # Save to disk
            save_file = Path(save_path)
            save_file.parent.mkdir(parents=True, exist_ok=True)
            save_file.write_bytes(attachment_data)
    
            logger.info(f"Attachment '{attachment_name}' saved to {save_path}")
    
            return {
                "email_id": email_id,
                "attachment_name": attachment_name,
                "mime_type": mime_type or "application/octet-stream",
                "size": len(attachment_data),
                "saved_path": str(save_file.resolve()),
            }
    
        finally:
            try:
                await imap.logout()
            except Exception as e:
                logger.info(f"Error during logout: {e}")
  • Pydantic model defining the output schema for attachment download response.
    class AttachmentDownloadResponse(BaseModel):
        """Attachment download response"""
    
        email_id: str
        attachment_name: str
        mime_type: str
        size: int
        saved_path: str
  • ClassicEmailHandler implementation that delegates to incoming_client and converts result to typed response.
    async def download_attachment(
        self,
        email_id: str,
        attachment_name: str,
        save_path: str,
    ) -> AttachmentDownloadResponse:
        """Download an email attachment and save it to the specified path."""
        result = await self.incoming_client.download_attachment(email_id, attachment_name, save_path)
        return AttachmentDownloadResponse(
            email_id=result["email_id"],
            attachment_name=result["attachment_name"],
            mime_type=result["mime_type"],
            size=result["size"],
            saved_path=result["saved_path"],
        )
  • Abstract base class method defining the EmailHandler interface for download_attachment.
    @abc.abstractmethod
    async def download_attachment(
        self,
        email_id: str,
        attachment_name: str,
        save_path: str,
    ) -> "AttachmentDownloadResponse":
        """
        Download an email attachment and save it to the specified path
        """

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/ai-zerolab/mcp-email-server'

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