Skip to main content
Glama
taylorwilsdon

Google Workspace MCP Server - Control Gmail, Calendar, Docs, Sheets, Slides, Chat, Forms & Drive

send_gmail_message

Send emails directly from your Gmail account by specifying recipient, subject, and message body. Confirms delivery with the sent email's message ID.

Instructions

Sends an email using the user's Gmail account.

Args:
    to (str): Recipient email address.
    subject (str): Email subject.
    body (str): Email body (plain text).
    user_google_email (str): The user's Google email address. Required.

Returns:
    str: Confirmation message with the sent email's message ID.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bodyYesEmail body (plain text).
serviceYes
subjectYesEmail subject.
toYesRecipient email address.
user_google_emailYes

Implementation Reference

  • The primary handler for the 'send_gmail_message' MCP tool. Decorated with @server.tool() for registration. Uses Gmail API to send emails, supports threading/replies, plain/HTML body, CC/BCC. Relies on helper _prepare_gmail_message for MIME message construction.
    @server.tool()
    @handle_http_errors("send_gmail_message", service_type="gmail")
    @require_google_service("gmail", GMAIL_SEND_SCOPE)
    async def send_gmail_message(
        service,
        user_google_email: str,
        to: str = Body(..., description="Recipient email address."),
        subject: str = Body(..., description="Email subject."),
        body: str = Body(..., description="Email body content (plain text or HTML)."),
        body_format: Literal["plain", "html"] = Body(
            "plain", description="Email body format. Use 'plain' for plaintext or 'html' for HTML content."
        ),
        cc: Optional[str] = Body(None, description="Optional CC email address."),
        bcc: Optional[str] = Body(None, description="Optional BCC email address."),
        thread_id: Optional[str] = Body(None, description="Optional Gmail thread ID to reply within."),
        in_reply_to: Optional[str] = Body(None, description="Optional Message-ID of the message being replied to."),
        references: Optional[str] = Body(None, description="Optional chain of Message-IDs for proper threading."),
    ) -> str:
        """
        Sends an email using the user's Gmail account. Supports both new emails and replies.
    
        Args:
            to (str): Recipient email address.
            subject (str): Email subject.
            body (str): Email body content.
            body_format (Literal['plain', 'html']): Email body format. Defaults to 'plain'.
            cc (Optional[str]): Optional CC email address.
            bcc (Optional[str]): Optional BCC email address.
            user_google_email (str): The user's Google email address. Required.
            thread_id (Optional[str]): Optional Gmail thread ID to reply within. When provided, sends a reply.
            in_reply_to (Optional[str]): Optional Message-ID of the message being replied to. Used for proper threading.
            references (Optional[str]): Optional chain of Message-IDs for proper threading. Should include all previous Message-IDs.
    
        Returns:
            str: Confirmation message with the sent email's message ID.
    
        Examples:
            # Send a new email
            send_gmail_message(to="user@example.com", subject="Hello", body="Hi there!")
    
            # Send an HTML email
            send_gmail_message(
                to="user@example.com",
                subject="Hello",
                body="<strong>Hi there!</strong>",
                body_format="html"
            )
    
            # Send an email with CC and BCC
            send_gmail_message(
                to="user@example.com",
                cc="manager@example.com",
                bcc="archive@example.com",
                subject="Project Update",
                body="Here's the latest update..."
            )
    
            # Send a reply
            send_gmail_message(
                to="user@example.com",
                subject="Re: Meeting tomorrow",
                body="Thanks for the update!",
                thread_id="thread_123",
                in_reply_to="<message123@gmail.com>",
                references="<original@gmail.com> <message123@gmail.com>"
            )
        """
        logger.info(
            f"[send_gmail_message] Invoked. Email: '{user_google_email}', Subject: '{subject}'"
        )
    
        # Prepare the email message
        raw_message, thread_id_final = _prepare_gmail_message(
            subject=subject,
            body=body,
            to=to,
            cc=cc,
            bcc=bcc,
            thread_id=thread_id,
            in_reply_to=in_reply_to,
            references=references,
            body_format=body_format,
            from_email=user_google_email,
        )
    
        send_body = {"raw": raw_message}
    
        # Associate with thread if provided
        if thread_id_final:
            send_body["threadId"] = thread_id_final
    
        # Send the message
        sent_message = await asyncio.to_thread(
            service.users().messages().send(userId="me", body=send_body).execute
        )
        message_id = sent_message.get("id")
        return f"Email sent! Message ID: {message_id}"
  • Helper function used by send_gmail_message to construct the MIME email message, handle threading headers (In-Reply-To, References), support plain/HTML body, and base64 encode the raw message for Gmail API.
    def _prepare_gmail_message(
        subject: str,
        body: str,
        to: Optional[str] = None,
        cc: Optional[str] = None,
        bcc: Optional[str] = None,
        thread_id: Optional[str] = None,
        in_reply_to: Optional[str] = None,
        references: Optional[str] = None,
        body_format: Literal["plain", "html"] = "plain",
        from_email: Optional[str] = None,
    ) -> tuple[str, Optional[str]]:
        """
        Prepare a Gmail message with threading support.
    
        Args:
            subject: Email subject
            body: Email body content
            to: Optional recipient email address
            cc: Optional CC email address
            bcc: Optional BCC email address
            thread_id: Optional Gmail thread ID to reply within
            in_reply_to: Optional Message-ID of the message being replied to
            references: Optional chain of Message-IDs for proper threading
            body_format: Content type for the email body ('plain' or 'html')
            from_email: Optional sender email address
    
        Returns:
            Tuple of (raw_message, thread_id) where raw_message is base64 encoded
        """
        # Handle reply subject formatting
        reply_subject = subject
        if in_reply_to and not subject.lower().startswith('re:'):
            reply_subject = f"Re: {subject}"
    
        # Prepare the email
        normalized_format = body_format.lower()
        if normalized_format not in {"plain", "html"}:
            raise ValueError("body_format must be either 'plain' or 'html'.")
    
        message = MIMEText(body, normalized_format)
        message["Subject"] = reply_subject
    
        # Add sender if provided
        if from_email:
            message["From"] = from_email
    
        # Add recipients if provided
        if to:
            message["To"] = to
        if cc:
            message["Cc"] = cc
        if bcc:
            message["Bcc"] = bcc
    
        # Add reply headers for threading
        if in_reply_to:
            message["In-Reply-To"] = in_reply_to
    
        if references:
            message["References"] = references
    
        # Encode message
        raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
    
        return raw_message, thread_id
  • Decorators on the handler function that register it as an MCP tool via @server.tool(), handle errors, and require Gmail send scope.
    @server.tool()
  • Input schema defined via FastAPI Body() parameters with descriptions, type hints (str, Optional[str], Literal), and comprehensive docstring with usage examples serving as schema documentation.
    async def send_gmail_message(
        service,
        user_google_email: str,
        to: str = Body(..., description="Recipient email address."),
        subject: str = Body(..., description="Email subject."),
        body: str = Body(..., description="Email body content (plain text or HTML)."),
        body_format: Literal["plain", "html"] = Body(
            "plain", description="Email body format. Use 'plain' for plaintext or 'html' for HTML content."
        ),
        cc: Optional[str] = Body(None, description="Optional CC email address."),
        bcc: Optional[str] = Body(None, description="Optional BCC email address."),
        thread_id: Optional[str] = Body(None, description="Optional Gmail thread ID to reply within."),
        in_reply_to: Optional[str] = Body(None, description="Optional Message-ID of the message being replied to."),
        references: Optional[str] = Body(None, description="Optional chain of Message-IDs for proper threading."),
    ) -> str:
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It states the action (sends an email) and return value (confirmation with message ID), but doesn't mention authentication requirements, rate limits, error conditions, whether it's synchronous/asynchronous, or what happens if sending fails. For a mutation tool with zero annotation coverage, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections (purpose, args, returns). The description is appropriately sized - every sentence serves a purpose. Could be slightly more concise by integrating the parameter descriptions more naturally rather than as a separate 'Args' section.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with 5 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain authentication requirements, error handling, rate limits, or the purpose of the undocumented 'service' parameter. The return value description is helpful but doesn't substitute for a proper output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 60% (3 of 5 parameters have descriptions in schema). The description adds parameter documentation for 'to', 'subject', 'body', and 'user_google_email', but doesn't mention the 'service' parameter at all. It provides some additional context about 'user_google_email' being required, but doesn't fully compensate for the undocumented 'service' parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Sends an email using the user's Gmail account' - a specific verb (send) and resource (email via Gmail). It distinguishes from sibling 'draft_gmail_message' by specifying sending rather than drafting, but doesn't explicitly contrast with 'send_message' which appears to be a different messaging system.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like 'draft_gmail_message' or 'send_message'. The description doesn't mention prerequisites, authentication requirements, or any context about when this specific Gmail sending tool is appropriate versus other messaging options.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/taylorwilsdon/google_workspace_mcp'

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