Skip to content

Conversation

@olivermdb
Copy link

Summary

This PR adds comprehensive Gmail attachment functionality to the MCP server, enabling users to list and download email attachments.

Changes

  1. New helper function _extract_attachments()

    • Recursively extracts attachment metadata from Gmail message payloads
    • Returns filename, mimeType, size, and attachmentId for each attachment
  2. Enhanced get_gmail_message_content()

    • Now displays attachment information when messages contain attachments
    • Shows attachment metadata and download instructions
  3. New tool get_gmail_attachment_content()

    • Downloads email attachments via Gmail API
    • Returns base64-encoded attachment data for further processing
    • Properly handles Gmail's ephemeral attachment ID behavior

Key Technical Insight

Gmail attachment IDs are ephemeral and change between API calls for the same message. This PR addresses this by:

  • Using the attachment ID provided by the caller directly without refetching the message
  • Documenting that filename/mime type should be obtained from the initial message fetch
  • Including clear error messages when attachment IDs become stale

If we refetch the message to get metadata, the new attachment IDs won't match the original ID parameter, causing downloads to fail. The attachment download endpoint returns size information directly, making message refetch unnecessary.

Testing

Tested with real Gmail messages containing various attachment types (PDFs, images, etc.). The implementation successfully:

  • Detects attachments in messages
  • Provides accurate metadata
  • Downloads attachment content without ID mismatch errors

Related Issues

Fixes attachment download functionality that was previously failing with "Could not find attachment metadata" errors.

This commit adds comprehensive Gmail attachment functionality:

1. New helper function `_extract_attachments()`:
   - Recursively extracts attachment metadata from message payloads
   - Returns filename, mimeType, size, and attachmentId for each attachment

2. Enhanced `get_gmail_message_content()`:
   - Now displays attachment information when present
   - Shows attachment ID needed for downloading

3. New tool `get_gmail_attachment_content()`:
   - Downloads email attachments via Gmail API
   - Returns base64-encoded attachment data
   - Handles ephemeral attachment ID issue correctly

Key technical insight: Gmail attachment IDs are ephemeral and change
between API calls for the same message. To avoid ID mismatches, the
download function uses the attachment ID provided by the caller directly
without refetching the message, as refetching would generate new IDs
that don't match the original ID.

The attachment download endpoint returns size information directly, while
filename and mime type should be obtained from the initial message fetch
that provided the attachment ID.
@taylorwilsdon taylorwilsdon requested review from Copilot and taylorwilsdon and removed request for Copilot October 15, 2025 21:27
@taylorwilsdon taylorwilsdon self-assigned this Oct 15, 2025
Removed f-string prefixes from strings without placeholders
in get_gmail_attachment_content function (lines 672, 675, 677, 678, 679).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@taylorwilsdon taylorwilsdon requested a review from Copilot October 17, 2025 16:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive Gmail attachment functionality to the MCP server, enabling users to list and download email attachments while properly handling Gmail's ephemeral attachment ID behavior.

Key Changes:

  • New helper function _extract_attachments() recursively extracts attachment metadata from Gmail message payloads
  • Enhanced get_gmail_message_content() now displays attachment information with download instructions
  • New tool get_gmail_attachment_content() downloads email attachments and returns base64-encoded data

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

return "[No readable content found]"


def _extract_attachments(payload: dict) -> List[Dict[str, any]]:
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected type hint from 'any' to 'Any' (should be capitalized as it's from the typing module).

Copilot uses AI. Check for mistakes.
Comment on lines +653 to +657
service.users()
.messages()
.attachments()
.get(userId="me", messageId=message_id, id=attachment_id)
.execute
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing parentheses for method call. Should be .execute() instead of .execute to actually invoke the method.

Suggested change
service.users()
.messages()
.attachments()
.get(userId="me", messageId=message_id, id=attachment_id)
.execute
lambda: service.users()
.messages()
.attachments()
.get(userId="me", messageId=message_id, id=attachment_id)
.execute()

Copilot uses AI. Check for mistakes.
@TYRONEMICHAEL
Copy link

TYRONEMICHAEL commented Oct 23, 2025

Hi All. What is the status of this PR? Really looking for GMAIL attachment support.

@taylorwilsdon
Copy link
Owner

Hi All. What is the status of this PR? Really looking for GMAIL attachment support.

Has some outstanding little bits to address, one sec

@taylorwilsdon taylorwilsdon requested a review from Copilot October 23, 2025 18:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

.messages()
.attachments()
.get(userId="me", messageId=message_id, id=attachment_id)
.execute
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing parentheses for method call. Should be .execute() to actually invoke the method.

Suggested change
.execute
.execute()

Copilot uses AI. Check for mistakes.
f"Message ID: {message_id}",
f"Size: {size_kb:.1f} KB ({size_bytes} bytes)",
"\nBase64-encoded content (first 100 characters shown):",
f"{attachment['data'][:100]}...",
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential KeyError if 'data' key is missing from attachment response. Add a guard with .get('data', '') to handle cases where the attachment download fails but doesn't raise an exception.

Suggested change
f"{attachment['data'][:100]}...",
f"{attachment.get('data', '')[:100]}...",

Copilot uses AI. Check for mistakes.
@TYRONEMICHAEL
Copy link

Thanks man. Looking forward to this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants