A secure Gmail MCP server with OAuth 2.0, encrypted token storage, and full Gmail API integration.
Strong tool definitions with clear naming conventions, comprehensive parameter descriptions, and explicit schemas via Zod. All 18 tools follow verb_noun naming pattern (list_, read_, search_, get_, download_, send_, reply_to_, forward_, save_, delete_, move_, mark_, create_, batch_, empty_). Tool descriptions are detailed and explain the WHAT, WHEN, and consequences (destructive operations clearly flagged). Schemas are generated from Zod validators via zodToJsonSchema, providing type safety and structure. However, some tools lack enum constraints where they should have them (e.g., folder names in list_emails), output schemas are not explicitly documented in descriptions, and error handling guidance is minimal. Security-sensitive operations (delete_email, batch_delete, empty_trash) include confirmation patterns but lack explicit permission gating or scope declarations.
Delete multiple emails at once (up to 1000). Default moves to Trash. With permanent:true, PERMANENTLY deletes all — IRREVERSIBLE. You MUST ask the user to confirm before setting permanent:true and confirmed:true. Returns partial failure report if some IDs fail.
Create a new Gmail label with optional visibility settings.
Delete an email. By default (permanent:false) moves to Trash — recoverable. With permanent:true, PERMANENTLY deletes — IRREVERSIBLE. You MUST ask the user to confirm before setting permanent:true and confirmed:true.
Delete a Gmail user label by its ID. System labels (INBOX, SENT, etc.) cannot be deleted.
Download a single attachment by its attachmentId (from get_attachments metadata). Returns base64-encoded data for that specific attachment.
Output schemas not documented in tool descriptions. Users/LLMs cannot predict what fields will be returned from list_emails, read_email, get_attachments, etc. This violates the pattern:tool-description and pattern:response-shaper guidance.
Folder/label parameters accept free-form strings instead of enums. list_emails accepts 'folder' as a string (default 'INBOX'), but valid values (INBOX, SENT, SPAM, TRASH, DRAFT, etc.) should be enumerated or documented. move_email accepts arbitrary 'targetLabel' without validation. LLMs will hallucinate invalid folder names.
No error recovery guidance. Error descriptions do not explain what the LLM should do next (e.g., 'If authentication fails, check that OAuth token is valid' or 'If email not found, try search_emails first'). This breaks the pattern:recovery-guide contract.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 58 | <=2025-11-25 | v2 |
| 2026-03-09 | D | 51 | - | v1 |
PERMANENTLY delete ALL messages in the Trash folder. THIS IS IRREVERSIBLE. You MUST explicitly warn the user and obtain confirmation before calling this. Only set confirmed:true after the user has acknowledged this cannot be undone.
Forward an email to new recipients, optionally prepending an additional message. Subject is automatically prefixed with 'Fwd:' (deduplicated).
Get attachments for an email. If total attachment size is under 5 MB, returns full base64 data. If over 5 MB, returns metadata only — use download_attachment to fetch individual files.
List emails in a Gmail folder/label. Returns message summaries with subject, sender, date, and snippet. Use pageToken for pagination. Default folder is INBOX.
List all Gmail labels (both system and user-created) with message counts.
Mark an email as read/unread, starred/unstarred, or important/unimportant.
Move an email to a different label/folder. Removes all existing location labels (INBOX, SENT, SPAM, TRASH) and adds only the target label.
Health check — returns server version, authenticated user email, token expiry status, and mailbox stats.
Read the full content of a single email by its ID. Returns headers, body (preferring plain text), and attachment metadata. Use get_attachments to retrieve attachment data.
Reply to an existing email thread. Set replyAll:true to include all CC recipients. Subject is automatically prefixed with 'Re:' (deduplicated). HTML is sanitised.
Save an email as a draft. All fields except body are optional, allowing partial drafts.
Search emails using Gmail query syntax (e.g. 'from:alice@example.com is:unread after:2024/01/01'). Returns matching message summaries. Use pageToken for pagination.
Send an email. Supports plain text and HTML (HTML is sanitised before sending). Body is limited to 5 MB. CC and BCC are supported.
Permission gating not explicitly declared. Tools like delete_email, batch_delete, empty_trash, send_email lack documented permission requirements (e.g., 'Requires scope: gmail.modify'). This violates pattern:scope-declaration and complicates least-privilege configuration.
Destructive operations rely on parameter-level confirmation (permanent:true + confirmed:true) but do not document expected agent behavior in detail. The description says 'You MUST ask the user' but provides no format or guidance for how the agent should request confirmation. Consider a dedicated confirmation tool or MRTR (Multi Round-Trip Request) pattern.
Pagination metadata incomplete. Tools like list_emails, search_emails return pageToken for pagination but do not document total count or whether more results exist. An LLM cannot determine if pagination is exhausted without counting returned items.
Batch tool (batch_delete) returns partial failure report but error format is not documented. LLMs cannot parse per-item success/failure without knowing the response structure.
Tool composition relies on IDs (emailId, attachmentId, labelId) without documenting whether these IDs are stable, opaque, or human-readable. No guidance for agents on whether they should cache or lookup IDs between calls.