Skip to main content
Glama

gmail_send_email

Send emails programmatically via Gmail API using OAuth2 tokens, enabling automated email dispatch from remote server environments without browser access.

Instructions

Send an email via Gmail

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
google_access_tokenYesGoogle OAuth2 access token
toYesRecipient email address
subjectYesEmail subject
bodyYesEmail body content (plain text)
html_bodyNoEmail body content in HTML format (optional)

Implementation Reference

  • Core implementation of gmail_send_email tool logic in GmailClient.send_email method. Constructs MIME multipart message with plain and optional HTML body, base64url encodes it, and sends via Gmail API v1 users.messages.send.
    def send_email(self, to: str, subject: str, body: str, html_body: Optional[str] = None) -> str:
        """Send an email via Gmail
        
        Args:
            to: Recipient email address
            subject: Email subject
            body: Plain text email body
            html_body: Optional HTML email body
        """
        try:
            # Check if service is initialized
            if not hasattr(self, 'service'):
                return json.dumps({
                    "error": "No valid access token provided. Please refresh your token first.",
                    "status": "error"
                })
                
            # Define the operation
            def _operation():
                # Create message container
                message = MIMEMultipart('alternative')
                message['to'] = to
                message['subject'] = subject
                
                # Attach plain text and HTML parts
                message.attach(MIMEText(body, 'plain'))
                if html_body:
                    message.attach(MIMEText(html_body, 'html'))
                
                # Encode the message
                encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
                
                # Create the message body
                create_message = {
                    'raw': encoded_message
                }
                
                # Send the message
                send_response = self.service.users().messages().send(
                    userId='me', 
                    body=create_message
                ).execute()
                
                return json.dumps({
                    "messageId": send_response['id'],
                    "threadId": send_response.get('threadId', ''),
                    "labelIds": send_response.get('labelIds', [])
                })
            
            # Execute the operation with token refresh handling
            return self._handle_token_refresh(_operation)
            
        except HttpError as e:
            logger.error(f"API Exception: {str(e)}")
            return json.dumps({"error": str(e)})
        except Exception as e:
            logger.error(f"Exception: {str(e)}")
            return json.dumps({"error": str(e)})
  • MCP dispatch handler in handle_call_tool for gmail_send_email: extracts arguments, initializes GmailClient, validates params, calls send_email method.
    elif name == "gmail_send_email":
        # Initialize Gmail client with just access token
        gmail = GmailClient(
            access_token=access_token
        )
        
        to = arguments.get("to")
        subject = arguments.get("subject")
        body = arguments.get("body")
        html_body = arguments.get("html_body")
        
        if not to or not subject or not body:
            raise ValueError("Missing required parameters: to, subject, and body are required")
        
        results = gmail.send_email(to=to, subject=subject, body=body, html_body=html_body)
        return [types.TextContent(type="text", text=results)]
  • Tool registration in server.list_tools(): defines name, description, input schema with required parameters for gmail_send_email.
    types.Tool(
        name="gmail_send_email",
        description="Send an email via Gmail",
        inputSchema={
            "type": "object",
            "properties": {
                "google_access_token": {"type": "string", "description": "Google OAuth2 access token"},
                "to": {"type": "string", "description": "Recipient email address"},
                "subject": {"type": "string", "description": "Email subject"},
                "body": {"type": "string", "description": "Email body content (plain text)"},
                "html_body": {"type": "string", "description": "Email body content in HTML format (optional)"}
            },
            "required": ["google_access_token", "to", "subject", "body"]
        },
    ),
  • JavaScript GmailClient.sendEmail method - core implementation that builds multipart raw email message manually, base64 encodes, sends via Gmail API.
    async sendEmail({ to, subject, body, html_body }) {
      const operation = async () => {
        if (!this.gmail) {
          throw new Error('Gmail service not initialized. No valid access token provided.');
        }
        const messageParts = [
          `To: ${to}`,
          `Subject: ${subject}`,
          'Content-Type: multipart/alternative; boundary="boundary"',
          '',
          '--boundary',
          'Content-Type: text/plain; charset="UTF-8"',
          '',
          body,
          '--boundary',
          'Content-Type: text/html; charset="UTF-8"',
          '',
          html_body || '',
          '--boundary--'
        ];
        const rawMessage = Buffer.from(messageParts.join('\r\n')).toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
        const res = await this.gmail.users.messages.send({
          userId: 'me',
          requestBody: { raw: rawMessage }
        });
        return JSON.stringify({
          messageId: res.data.id,
          threadId: res.data.threadId,
          labelIds: res.data.labelIds
        });
      };
      return await this._handleTokenRefresh(operation);
    }
  • JavaScript MCP server.tool registration and handler for gmail_send_email: Zod schema, creates GmailClient, calls sendEmail, returns result.
    server.tool(
      'gmail_send_email',
      'Send an email via Gmail',
      {
        google_access_token: z.string().describe('Google OAuth2 access token'),
        to: z.string().describe('Recipient email address'),
        subject: z.string().describe('Email subject'),
        body: z.string().describe('Email body content (plain text)'),
        html_body: z.string().optional().describe('Email body content in HTML format (optional)')
      },
      async ({ google_access_token, to, subject, body, html_body }) => {
        try {
          const gmail = new GmailClient({
            accessToken: google_access_token
          });
          const result = await gmail.sendEmail({ to, subject, body, html_body });
          return { content: [{ type: 'text', text: result }] };
        } catch (error) {
          return { content: [{ type: 'text', text: `Error: ${error.message}` }] };
        }
      }
    );
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the action ('send an email') but lacks critical details: it doesn't mention authentication requirements (implied by the 'google_access_token' parameter but not explicitly stated), potential rate limits, error handling, or what happens upon success (e.g., whether it returns a confirmation). This leaves significant gaps for a mutation tool.

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

Conciseness5/5

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

The description is a single, efficient sentence with zero wasted words—'Send an email via Gmail' is front-loaded and directly conveys the core action. It's appropriately sized for the tool's complexity, making it easy to parse quickly.

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?

Given the tool's complexity (a mutation tool with 5 parameters, no annotations, and no output schema), the description is incomplete. It fails to address key contextual aspects like authentication needs, behavioral traits (e.g., what 'send' entails operationally), or output expectations, leaving the agent with insufficient information for reliable use.

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?

The schema description coverage is 100%, with all parameters well-documented in the schema (e.g., 'to' as recipient email, 'body' as plain text content). The description adds no additional meaning beyond the schema, such as explaining parameter interactions (e.g., 'body' vs. 'html_body') or constraints. This meets the baseline for high schema coverage.

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 'Send an email via Gmail' clearly states the verb ('send') and resource ('email via Gmail'), making the purpose immediately understandable. However, it doesn't differentiate from sibling tools like 'gmail_get_recent_emails' or 'gmail_refresh_token' beyond the obvious action distinction, which keeps it from a perfect score.

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?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., needing authentication via 'google_access_token'), nor does it clarify scenarios where other tools like 'gmail_get_recent_emails' might be more appropriate, leaving usage context entirely implicit.

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

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/baryhuang/mcp-headless-gmail'

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