| list_emailsA | Search inbox for emails matching a query string.
Returns a list of emails with their IDs, subjects, from addresses, dates,
snippets, and labels. IDs can be passed to read_email or reply_email.
Required: none — both params have defaults.
Optional:
query (str, default="") — Gmail search syntax.
Examples: "from:alice@example.com", "subject:invoice is:unread",
"after:2026/01/01 before:2026/01/31", "has:attachment".
Leave empty to get most recent inbox emails.
max_results (int, default=5) — how many emails to return. Max 500.
Returns on success:
{"total": N, "emails": [{"id": "...", "subject": "...", "from": "...",
"date": "...", "snippet": "...", "labels": [...]}]}
Workflow note: The "id" field in each email is a Gmail message ID —
pass it to read_email, reply_email, or get_thread_messages.
Leave query empty to get recent inbox. Use Gmail search syntax
to filter: from:, subject:, after:, before:, is:unread, has:attachment.
|
| get_thread_messagesA | List all messages in a thread by thread ID.
Returns message IDs, subjects, from addresses, dates, and snippets.
Use this to find the Gmail message ID needed by reply_email.
Required:
thread_id (str) — the Gmail thread ID. Get this from list_emails
or read_email (look for "thread_id" in responses).
Returns on success:
{"thread_id": "...", "total": N, "messages": [
{"id": "...", "subject": "...", "from": "...", "date": "...", "snippet": "..."}
]}
Workflow note: reply_email requires a Gmail message ID in the in_reply_to
parameter — NOT an email Message-ID header. Use get_thread_messages
to find the right message_id first. Example flow:
1. list_emails(query="subject:my thread") → get thread_id
2. get_thread_messages(thread_id) → get message_id
3. reply_email(thread_id=..., in_reply_to=message_id, ...)
|
| read_emailA | Fetch the full content of an email by its Gmail message ID.
Returns subject, from, to, date, body (plain text), html_body, and labels.
Required:
message_id (str) — the Gmail message ID from list_emails or
get_thread_messages. NOT an email Message-ID header.
Returns on success:
{"id": "...", "subject": "...", "from": "...", "to": "...",
"date": "...", "body": "plain text...", "html_body": "HTML...",
"labels": ["INBOX", "IMPORTANT"]}
Workflow note: body may be None if the email is HTML-only.
html_body will be present for HTML emails. Labels include system
labels like INBOX, SENT, TRASH, UNREAD and custom labels.
Use list_attachments(message_id) to find downloadable files.
|
| send_emailA | Send a new email. Can thread into an existing conversation by including
thread_id and in_reply_to/references headers. Supports CC, BCC, and
file attachments.
Required:
to (str) — recipient email address.
subject (str) — email subject line.
body (str) — email body text.
Optional:
cc (str, default="") — CC recipients, comma-separated.
bcc (str, default="") — BCC recipients, comma-separated.
thread_id (str, default="") — Gmail thread ID to reply within.
If provided with in_reply_to and references, the email threads
into the existing conversation.
in_reply_to (str, default="") — the Message-ID header of the
message being replied to. Required for threading.
references (str, default="") — References header for threading.
Usually same as in_reply_to.
attachments (list[str] | None, default=None) — absolute file paths
to files to attach. Paths must be absolute or relative to cwd.
Returns on success:
{"message_id": "...", "thread_id": "...", "to": "...", "subject": "..."}
Workflow note: For threading, you need both thread_id and in_reply_to.
If you only have a thread_id but not in_reply_to, use
get_thread_messages(thread_id) first to find a message_id.
Attachments: paths must exist and be under 25MB.
The body parameter is the email text — not a file path.
|
| reply_emailA | Convenience wrapper to reply to an existing thread.
Automatically prepends "Re: " to the subject if missing.
Can CC all original recipients with reply_all=True.
Required:
to (str) — recipient address.
body (str) — reply text.
thread_id (str) — Gmail thread ID to reply within.
in_reply_to (str) — Gmail message ID being replied to.
GET THIS FROM get_thread_messages(thread_id) — do not guess.
Optional:
subject (str, default="") — if empty, "Re: " is prepended to the
original message's subject.
cc (str, default="") — additional CC recipients.
bcc (str, default="") — BCC recipients.
reply_all (bool, default=False) — if True, auto-CCs all other
recipients from the original message (To and CC headers).
Returns on success:
{"message_id": "...", "thread_id": "...", "to": "...", "subject": "..."}
Workflow note: CRITICAL — in_reply_to must be a Gmail message ID
(e.g. "19e09c7f2e8d487d"), NOT an email Message-ID header
(e.g. "<CAC4dBmc...@mail.gmail.com>"). To find the right ID:
1. Call get_thread_messages(thread_id)
2. Pick the message ID from the "messages" array
3. Pass that to reply_email's in_reply_to parameter
|
| archive_emailA | Archive an email — removes it from INBOX without deleting it.
Archived emails are still searchable and accessible via search.
Required:
message_id (str) — Gmail message ID from list_emails.
Returns on success:
{"message_id": "...", "labels": ["IMPORTANT", "SENT"]}}
(INBOX is removed from labels)
Workflow note: This is non-destructive. The email stays in Gmail
with all other labels intact. Use list_emails(query="is:inbox")
to confirm it's gone from the inbox view.
|
| trash_emailA | Move an email to trash. Emails in trash are auto-deleted after 30 days.
Required:
message_id (str) — Gmail message ID from list_emails.
Returns on success:
{"message_id": "...", "labels": [..., "TRASH"]}}
Workflow note: To permanently delete, trash the email first —
Gmail auto-purges trash after 30 days.
|
| mark_readA | Mark an email as read — removes the UNREAD label.
Required:
message_id (str) — Gmail message ID from list_emails.
Returns on success:
{"message_id": "...", "labels": ["INBOX", "IMPORTANT"]}}
(UNREAD is removed from labels)
Workflow note: Use this after reading an important email to
clear the unread indicator.
|
| add_labelA | Add a label (system or custom) to an email.
Required:
message_id (str) — Gmail message ID.
label (str) — label name. System labels: STARRED, IMPORTANT,
SENT, DRAFT, TRASH, SPAM. Custom labels: any name you've
created in Gmail settings.
Returns on success:
{"message_id": "...", "labels": ["INBOX", "IMPORTANT", "..."]}}
Workflow note: Label names are case-sensitive in Gmail.
"STARRED" works, "starred" may not. Custom label names
must exactly match how they appear in Gmail.
|
| remove_labelA | Remove a label from an email.
Required:
message_id (str) — Gmail message ID.
label (str) — label name to remove.
Returns on success:
{"message_id": "...", "labels": ["INBOX", "IMPORTANT"]}}
(the label is removed from labels array)
Workflow note: Removes the label from the message's label set.
No error if the label was not present.
|
| create_draftA | Create a draft email saved in Gmail's Drafts folder — not sent immediately.
Review it in Gmail before sending manually.
Required:
to (str) — recipient email address.
subject (str) — email subject line.
body (str) — email body text.
Optional:
cc (str, default="") — CC recipients.
bcc (str, default="") — BCC recipients.
attachments (list[str] | None, default=None) — file paths to attach.
Returns on success:
{"draft_id": "...", "message_id": "..."}
Workflow note: Drafts are saved to Gmail, not stored locally.
Use list_drafts() to see all drafts. Use update_draft(draft_id, ...)
to modify a draft. Use delete_draft(draft_id) to remove.
The draft stays in Gmail until manually sent or deleted.
|
| list_draftsA | List the user's draft emails from Gmail's Drafts folder.
Optional:
max_results (int, default=5) — max drafts to return.
Returns on success:
{"total": N, "drafts": [
{"id": "...", "subject": "...", "from": "...", "to": "...",
"date": "...", "snippet": "..."}
]}
Workflow note: Each draft has a "id" (draft_id) used by update_draft
and delete_draft. The "subject" field shows the draft's subject line.
Use update_draft(draft_id, body="new content") to edit a draft.
|
| update_draftA | Update a draft body, preserving omitted headers, threading and attachments. body is required. Empty to/subject/cc/bcc retain existing values.
attachments=None preserves existing files; [] explicitly removes them.
|
| delete_draftA | Delete a draft from Gmail's Drafts folder.
Required:
draft_id (str) — draft ID from list_drafts.
Returns on success:
{"draft_id": "..."}
Workflow note: This permanently removes the draft. No undo.
Call list_drafts() first to confirm the draft_id before deleting.
|
| list_attachmentsA | List all file attachments in an email with their IDs, filenames,
MIME types, and sizes.
Required:
message_id (str) — Gmail message ID from list_emails.
Returns on success:
{"attachments": [
{"id": "...", "filename": "report.pdf",
"mime_type": "application/pdf", "size": 204800}
]}
Workflow note: The attachment "id" field is a Gmail attachment ID —
pass it to download_attachment along with the message_id and
a save_path to download the file. Returns empty list if the
email has no attachments.
|
| download_attachmentA | Download a file attachment from an email and save it to a local path.
Required:
message_id (str) — Gmail message ID from list_emails.
attachment_id (str) — attachment ID from list_attachments.
save_path (str) — local file path to save to. Must be inside
your home directory or the gmail-mcp config directory.
Parent directories are created automatically.
If the file already exists, a counter suffix is added
(e.g. "report (1).pdf") to avoid overwriting.
Returns on success:
{"filename": "report.pdf", "path": "C:/Users/.../report.pdf",
"size": 204800}
Workflow note: Paths are validated — path traversal attempts
(e.g. "../../etc/passwd") are rejected. Use absolute paths
or paths relative to your home directory. Supported file
types: any MIME type. Max attachment size: 25MB.
|
| read_emailsA | CHOICE HINT: When the user asks to "read", "show", or "fetch" content from
multiple emails (e.g. "read these 5 emails", "show me the latest 3 emails"),
ALWAYS prefer this tool over making N calls to read_email(). It bundles all
Gmail API calls into bounded HTTP batches.
Fetch full content of multiple emails in a single MCP request.
Uses googleapiclient.http.BatchHttpRequest — calls are bundled
into batches of up to 100 per HTTP round-trip.
Handles up to 100 messages per call automatically.
Optional:
save_to_file (bool, default=False) — force save all results to a
.md file regardless of size. Useful for large fetches.
max_chars (int, default=30000) — if estimated output exceeds this,
auto-saves to .md and returns a compact summary with the file path.
Returns on success (small):
{"ok": true, "data": {
"requested": N, "succeeded": N, "failed": 0,
"results": [{"id": "...", "ok": true, "data": {...}}, ...]
}}
Returns on success (large / save_to_file):
{"ok": true, "data": {
"saved": true,
"path": "C:\Users\...\gmail-mcp\emails\2026-05-11-piraeus-49.md",
"count": 49,
"size_bytes": 184523,
"summary_table": "## Summary
| # | Date | From | Subject | ...|",
"requested": 49,
"succeeded": 49,
"failed": 0,
"results": [{"id": "...", "ok": true, "data": {"date": "...", "from": "...", "subject": "...",
"snippet": "...", "labels": [...]}} ...]
}} Workflow note: Use this instead of N calls to read_email() to bundle
all fetches into a single MCP request. Each result has the same
shape as a read_email() response.
|
| mark_emails_readA | CHOICE HINT: When the user asks to "mark as read" or "mark read" multiple
emails, ALWAYS prefer this tool over making N calls to mark_read(). It uses
Gmail's batchModify API in chunks of at most 1000 message IDs.
Mark multiple emails as read in a single MCP request.
Uses messages().batchModify() with up to 1000 IDs per Gmail API call.
Required:
message_ids (list[str]) — Gmail message IDs.
Returns on success:
{"ok": true, "data": {
"requested": N, "succeeded": N, "failed": 0,
"results": [{"id": "...", "ok": true}, ...]
}}
Workflow note: Use this instead of N calls to mark_read() to bundle
all operations into a single MCP request.
|
| archive_emailsA | CHOICE HINT: When the user asks to "archive" or "remove from inbox" multiple
emails, ALWAYS prefer this tool over making N calls to archive_email(). It uses
Gmail's batchModify API in chunks of at most 1000 message IDs.
Archive multiple emails in a single MCP request.
Uses messages().batchModify() with up to 1000 IDs per Gmail API call.
Required:
message_ids (list[str]) — Gmail message IDs.
Returns on success:
{"ok": true, "data": {
"requested": N, "succeeded": N, "failed": 0,
"results": [{"id": "...", "ok": true}, ...]
}}
Workflow note: Non-destructive. Use this instead of N archive_email() calls.
|
| trash_emailsA | CHOICE HINT: When the user asks to "trash" or "delete" multiple emails,
ALWAYS prefer this tool over making N calls to trash_email(). It uses
Gmail's batchModify API in chunks of at most 1000 message IDs.
Move multiple emails to trash in a single MCP request.
Uses messages().batchModify() with up to 1000 IDs per Gmail API call.
Required:
message_ids (list[str]) — Gmail message IDs.
Returns on success:
{"ok": true, "data": {
"requested": N, "succeeded": N, "failed": 0,
"results": [{"id": "...", "ok": true}, ...]
}}
Workflow note: Use this instead of N trash_email() calls.
|
| add_labelsA | CHOICE HINT: When the user asks to "add a label" to multiple emails,
ALWAYS prefer this tool over making N calls to add_label(). It uses
Gmail's batchModify API in chunks of at most 1000 message IDs.
Add a label to multiple emails in a single MCP request.
Uses messages().batchModify() with up to 1000 IDs per Gmail API call.
Required:
message_ids (list[str]) — Gmail message IDs.
label (str) — label name. System labels: STARRED, IMPORTANT, etc.
Custom labels must exactly match how they appear in Gmail.
Returns on success:
{"ok": true, "data": {
"requested": N, "succeeded": N, "failed": 0,
"results": [{"id": "...", "ok": true}, ...]
}}
Workflow note: Use this instead of N add_label() calls.
Label names are case-sensitive.
|
| download_attachmentsC | Download attachments in batches of 100, preserving input order and unique files. |
| read_emails_fullA | Read multiple emails and always save the full results to a file.
Unlike read_emails() which returns data inline, this tool always writes
to disk and returns only a compact confirmation — making it safe to use
even for hundreds of emails without hitting MCP output limits.
Required:
message_ids (list[str]) — Gmail message IDs to fetch.
save_path (str) — local path to save the file. Must be inside your
home directory (path traversal is rejected).
Optional:
format (str, default="md") — output format. One of:
"md" — Markdown with summary table + full body per email
"json" — pretty-printed JSON
"jsonl" — one JSON object per line
"csv" — complete fields, text/HTML bodies, identifiers and errors
Returns on success:
{"ok": true, "data": {
"saved": true,
"path": "C:\Users\...\my-emails.md",
"count": 49,
"format": "md",
"total_size_bytes": 184523
}}
Workflow note: Use this instead of read_emails() when you know the
result will be large. Always saves — never returns data inline.
|
| search_and_saveA | One-shot search + save: search Gmail and write results directly to a file.
Combines list_emails + read_emails_full into a single tool call.
No data is returned inline — just a compact summary + file path.
Required:
query (str) — Gmail search syntax
(e.g. "from:PiraeusBank after:2026/03/01 has:attachment").
Optional:
max_results (int, default=100) — max emails to fetch.
save_path (str) — local file path. If empty, auto-generates one in
the configured gmail-mcp directory under emails/. Overwrite is rejected — use a fresh
path or let the tool auto-generate.
format (str, default="md") — "md", "json", "jsonl", or "csv".
Returns on success:
{"ok": true, "data": {
"saved": true,
"path": "C:\Users\...\emails\2026-05-11-piraeus-47.md",
"count": 47,
"format": "md",
"total_size_bytes": 184523,
"query": "from:PiraeusBank after:2026/03/01",
"search_summary": "47 emails matched your search query"
}}
Workflow note: Use this when you want to search Gmail and immediately
persist results to a file without worrying about MCP output limits.
|
| append_emailsA | Append more emails to an existing .md, .json, or .jsonl file.
Use this for pagination — fetch 50 emails, save them, then call this
tool to append the next 50 to the same file. Works with .md, .json,
and .jsonl files created by read_emails_full or search_and_save.
Required:
message_ids (list[str]) — Gmail message IDs to fetch and append.
save_path (str) — path to the existing file. File must exist.
Supported extensions: .md, .json, .jsonl
Returns on success:
{"ok": true, "data": {
"appended": true,
"path": "C:\Users\...\my-emails.md",
"count": 50,
"total_size_bytes": 320000
}}
|