Gmail + Calendar MCP Server
Provides tools to manage Gmail messages, drafts, labels, filters, signatures, vacation responders, and batch operations across multiple accounts.
Provides tools to manage Google Calendar events, calendars, ACLs, free/busy information, settings, and colors across multiple accounts.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Gmail + Calendar MCP Server show my upcoming calendar events for today"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Gmail + Calendar MCP Server
A multi-account Model Context Protocol server for Gmail and
Google Calendar (72 tools). Runs over stdio, installs via uvx, and takes all credentials
from environment variables — the host running the server never needs a credentials.json or
token.json file.
One shared Google OAuth "Desktop" client authorizes every account.
Each account contributes only its refresh token (one token covers both Gmail and Calendar).
Every tool takes an
accountargument to pick the account and an optionalpassword(see below).
How credentials work (2 env vars + 1 optional)
Variable | Required | Format | Example |
| yes |
|
|
| yes |
|
|
| no |
|
|
The selector defaults to the account's email but you can rename it to a short alias
(work, personal). You never hand-write the credential values — the gmail-mcp-auth command prints them.
Password protection (optional)
Set PASSWORDS to require a per-account password on every tool call. When it's set, the
gate is on: each call must pass password matching that account's entry, or the tool returns
Invalid password for account '…' and does nothing (no Gmail request is made). Passwords are
compared in constant time. Notes:
Keys must match your
GMAIL_ACCOUNTSselectors (case-insensitively). APASSWORDSkey that matches no account makes the server refuse to start. An account with noPASSWORDSentry is locked (no password can unlock it) while the gate is on — the server logs which accounts are locked at startup.Passwords cannot contain
=,;, or newlines (those are the separators), and leading/trailing whitespace is trimmed. Pick passwords without those characters.Fail-closed: if
PASSWORDSis set but has no validselector=passwordentries (e.g. only separators), the server errors out instead of silently running unprotected. Only an absent (or blank)PASSWORDSturns the gate off, in which case thepasswordargument is ignored.Comparison is case-sensitive and constant-time. This is an authorization gate for the calling agent, not transport encryption — the value lives in the same environment as the tokens.
Related MCP server: google-workspace-mcp
One-time setup
1. Create a Google OAuth client (once)
In Google Cloud Console create/select a project.
Enable the Gmail API.
Configure the OAuth consent screen (External is fine; add yourself as a Test user, or publish).
Create OAuth client ID → Desktop app. Note the client id and client secret.
2. Authorize each account (on a machine with a browser)
# From PyPI (after publishing — see "Publishing" below):
uvx --from gmail-calendar-mcp gmail-calendar-mcp-auth --client-id <ID> --client-secret <SECRET>
# Or straight from GitHub, no publish needed:
uvx --from git+https://github.com/eitan3/Gmail_MCP.git gmail-mcp-auth --client-id <ID> --client-secret <SECRET>⚠️ The bare name
gmail-mcpbelongs to an unrelated PyPI package — don't use it. This project is published asgmail-calendar-mcp.
A browser opens for Google consent. On success the command prints:
GMAIL_CLIENT="<id>|<secret>"
GMAIL_ACCOUNTS="alice@gmail.com=1//0g..."Add more accounts (re-uses the client from GMAIL_CLIENT in your env if set):
gmail-mcp-auth --merge --alias work # prints the full merged GMAIL_ACCOUNTSIf several Google accounts are signed into your browser, pass --login-hint you@example.com to
target the right one (also handy for re-consenting a specific account after a scope upgrade):
gmail-mcp-auth --merge --login-hint personal@gmail.comHeadless host? Use --no-browser and open the printed URL yourself (forward the redirect
port over SSH if consent happens on another machine).
3. Configure your MCP client
{
"mcpServers": {
"gmail": {
"command": "uvx",
"args": ["gmail-calendar-mcp"],
"env": {
"GMAIL_CLIENT": "…id…|…secret…",
"GMAIL_ACCOUNTS": "alice@gmail.com=1//0g…;work=1//0g…",
"PASSWORDS": "alice@gmail.com=hunter2;work=s3cret"
}
}
}
}uvx gmail-calendar-mcp works once the package is published to PyPI. Before that (or to skip
PyPI entirely), use "args": ["--from", "git+https://github.com/eitan3/Gmail_MCP.git", "gmail-mcp"].
Publishing to PyPI
The bare-name uvx gmail-calendar-mcp requires the package on PyPI. To publish a release:
uv build # builds dist/*.whl and *.tar.gz
uv publish --token pypi-XXXX # upload (get the token from pypi.org -> Account -> API tokens)Create a free account at https://pypi.org, then Account settings → API tokens → Add token (scope "Entire account" for the first upload). The token starts with
pypi-.Each upload needs a new
versioninpyproject.toml(PyPI rejects re-uploading the same version).Prefer a quick dry run on TestPyPI first:
uv publish --publish-url https://test.pypi.org/legacy/ --token <testpypi-token>.
Tools
Send send_email · reply_to_message · forward_message
Drafts create_draft · list_drafts · send_draft · update_draft · delete_draft
Read get_profile · get_message · search_messages · search_threads · get_thread
Attachments get_message_attachments · download_attachment
Trash trash_message · untrash_message · trash_thread · untrash_thread
Labels list_labels · create_label · update_label · delete_label ·
label_message · unlabel_message · label_thread · unlabel_thread
Filters list_filters · create_filter · delete_filter
Signature get_signature · update_signature
Vacation get_vacation_responder · set_vacation_responder
State (extra) mark_read · mark_unread · star · unstar · archive ·
move_to_inbox · mark_important · mark_not_important
Batch (extra) batch_modify_messages · batch_trash · batch_untrash
Every tool accepts account (alias or email). With a single configured account it's optional;
with several, omitting it returns an error listing the available selectors. Every tool also accepts
password, which is required only when PASSWORDS is configured (see Password protection above).
Example tool call (tools/call arguments) with the password gate enabled:
{
"name": "search_messages",
"arguments": { "account": "work", "password": "s3cret", "query": "is:unread", "max_results": 10 }
}Label arguments accept human names (e.g. Clients/Acme) or raw label ids. Search tools use
Gmail query syntax (e.g. is:unread from:alice newer_than:7d).
Google Calendar
Calendar shares the same accounts and password gate — every calendar tool takes the same account
and password arguments and is routed through the identical authentication path.
Events list_events · get_event · create_event · update_event · delete_event ·
quick_add_event · move_event · list_event_instances · respond_to_event · import_event
Calendars list_calendars · get_calendar · create_calendar · update_calendar ·
delete_calendar · clear_calendar · subscribe_calendar · unsubscribe_calendar ·
update_calendar_subscription
Sharing list_acl · share_calendar · update_acl · unshare_calendar
Misc get_freebusy · list_settings · get_setting · get_colors
Conventions:
Time: pass
start/endas RFC3339 (2026-06-15T10:00:00) withtime_zone(IANA, e.g.Asia/Jerusalem), or setall_day=truewith aYYYY-MM-DDdate. Calendar ops default to theprimarycalendar whencalendar_idis omitted.Recurring:
recurrenceis a list of RRULE strings, e.g.["RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=8"].Invites:
attendeesis an email list;add_meet=trueattaches a Google Meet link;send_updates∈all/externalOnly/nonecontrols attendee notifications.extra_fields(a dict) merges into the event body so any Calendar field is reachable.
⚠️ Re-auth required for Calendar. Tokens minted before Calendar was added only have Gmail scopes — Calendar calls return a 403 with a re-auth hint until you re-run
gmail-mcp-auth(re-consent) for each account and replace its token inGMAIL_ACCOUNTS. Gmail keeps working throughout.
Scopes
Gmail: gmail.modify, gmail.compose, gmail.settings.basic (no permanent delete — only Trash).
Calendar: calendar, calendar.settings.readonly.
Local development
uv sync --extra dev
uv run gmail-mcp-auth --client-id <ID> --client-secret <SECRET> # mint a token
$env:GMAIL_CLIENT="…"; $env:GMAIL_ACCOUNTS="…" # PowerShell
uv run gmail-mcp # run the server (stdio)
npx @modelcontextprotocol/inspector uv run gmail-mcp # interactive tool testing
uv run pytest # unit testsAvailable Tools
72 toolsarchiveC
Archive a message (removes it from the Inbox).
Inputs: message_id (required), account, password.
batch_modify_messagesB
Add and/or remove labels across many messages in one call.
add_labels/remove_labels accept label names or ids.
Inputs: message_ids (required), add_labels, remove_labels, account, +1 more.
batch_trashB
Move many messages to Trash in one call.
Inputs: message_ids (required), account, password.
batch_untrashC
Restore many messages from Trash in one call.
Inputs: message_ids (required), account, password.
clear_calendarB
Delete ALL events from a primary calendar (only works on a primary calendar).
Inputs: calendar_id, account, password.
create_calendarC
Create a new secondary calendar.
Inputs: summary (required), description, time_zone, location, +2 more.
create_draftC
Create a draft. thread_id attaches it to an existing thread (e.g. a reply draft).
Inputs: to, subject, body_text, body_html, +7 more.
create_eventA
Create an event.
Time: pass start/end as RFC3339 dateTimes with time_zone (IANA), or all_day=true
with 'YYYY-MM-DD'. recurrence is a list of RRULE strings. attendees/optional_attendees
are email lists. add_meet=true attaches a Google Meet link. reminders is a list of
{method:'popup'|'email', minutes:int}. send_updates ∈ all/externalOnly/none. extra_fields
merges any other Calendar event fields.
Inputs: summary (required), start, end, time_zone, +21 more.
create_filterB
Create a filter.
Criteria: from_address, to_address, subject, query (Gmail search syntax),
negated_query, has_attachment, exclude_chats, size + size_comparison
('larger'/'smaller').
Actions: add_labels/remove_labels (names or ids), forward_to, and the booleans
mark_read, archive (skip Inbox), star, mark_important, never_mark_important,
never_spam, trash.
Inputs: from_address, to_address, subject, query, +17 more.
create_labelB
Create a label. Use '/' in name for nesting (e.g. 'Clients/Acme').
Inputs: name (required), label_list_visibility, message_list_visibility, account, +1 more.
delete_calendarA
Delete a secondary calendar (cannot delete the primary calendar).
Inputs: calendar_id (required), account, password.
delete_draftA
Permanently delete a draft (the draft only — not a sent message).
Inputs: draft_id (required), account, password.
delete_eventC
Delete an event. send_updates ∈ all/externalOnly/none notifies attendees.
Inputs: event_id (required), calendar_id, send_updates, account, +1 more.
delete_filterA
Delete a filter by id (get ids from list_filters).
Inputs: filter_id (required), account, password.
delete_labelC
Delete a label (identified by name or id).
Inputs: label (required), account, password.
download_attachmentB
Download an attachment to save_path on the server host.
Get attachment_id from get_message_attachments or get_message.
Inputs: message_id (required), attachment_id (required), save_path (required), account, +1 more.
forward_messageB
Forward a message to new recipients, optionally re-attaching its attachments.
body_text is prepended before the quoted original. Starts a new thread.
Inputs: message_id (required), to (required), body_text, cc, +4 more.
get_calendarB
Get a calendar's metadata (summary, description, timeZone, location).
Inputs: calendar_id, account, password.
get_colorsB
Get the calendar and event color palettes (colorId -> hex values).
Inputs: account, password.
get_eventC
Get a single event.
Inputs: event_id (required), calendar_id, account, password.
get_freebusyB
Query busy time ranges across one or more calendars between time_min and time_max (RFC3339).
calendar_ids defaults to ['primary']. Returns busy slots per calendar.
Inputs: time_min (required), time_max (required), calendar_ids, account, +1 more.
get_messageB
Get a single message fully decoded: headers, snippet, text/HTML body, attachment list.
Inputs: message_id (required), include_body, account, password.
get_message_attachmentsB
List a message's attachments (filename, mimeType, size, attachmentId).
Inputs: message_id (required), account, password.
get_profileA
Get the Gmail profile (email address, message/thread totals, historyId).
account: which configured account to use (alias or email). Optional when only one
account is configured.
password: the account's password — required when password protection is enabled.
Inputs: account, password.
get_settingB
Get a single user calendar setting by id (e.g. 'timezone', 'weekStart').
Inputs: setting (required), account, password.
get_signatureB
Get the HTML signature for a send-as address (defaults to the primary address).
Inputs: send_as, account, password.
get_threadC
Get a full thread with every message decoded.
Inputs: thread_id (required), include_body, account, password.
get_vacation_responderC
Get the vacation responder (auto-reply) settings.
Inputs: account, password.
import_eventC
Import an event (adds a copy carrying an existing iCalUID, e.g. from an .ics).
Inputs: i_cal_uid (required), start (required), end (required), summary, +8 more.
label_messageC
Add one or more labels to a message.
Inputs: message_id (required), labels (required), account, password.
label_threadC
Add one or more labels to every message in a thread.
Inputs: thread_id (required), labels (required), account, password.
list_aclC
List the access-control rules (who the calendar is shared with) and their ruleIds.
Inputs: calendar_id, account, password.
list_calendarsB
List the calendars in this account (with access role, color, primary/selected flags).
min_access_role ∈ freeBusyReader/reader/writer/owner filters the list.
Inputs: min_access_role, show_hidden, account, password.
list_draftsB
List drafts with subject/recipient summaries and next_page_token.
Inputs: max_results, page_token, account, password.
list_event_instancesC
List the individual instances of a recurring event.
Inputs: event_id (required), calendar_id, time_min, time_max, +4 more.
list_eventsA
List/search events on a calendar.
time_min/time_max are RFC3339 (e.g. '2026-06-15T00:00:00Z'). query is free-text
search. single_events=True expands recurring events into instances (required for
order_by='startTime'). Returns rendered events + next_page_token.
Inputs: calendar_id, time_min, time_max, query, +7 more.
list_filtersC
List all filters with their ids, criteria, and actions.
Inputs: account, password.
list_labelsC
List all labels (system and user) with their ids.
Inputs: account, password.
list_settingsA
List all user calendar settings (timezone, week start, default reminders, etc.).
Inputs: account, password.
mark_importantC
Mark a message as important.
Inputs: message_id (required), account, password.
mark_not_importantC
Mark a message as not important.
Inputs: message_id (required), account, password.
mark_readC
Mark a message as read (removes the UNREAD label).
Inputs: message_id (required), account, password.
mark_unreadC
Mark a message as unread (adds the UNREAD label).
Inputs: message_id (required), account, password.
move_eventC
Move an event from one calendar to another.
Inputs: event_id (required), destination_calendar_id (required), calendar_id, send_updates, +2 more.
move_to_inboxC
Move a message back to the Inbox.
Inputs: message_id (required), account, password.
quick_add_eventA
Create an event from natural language (e.g. 'Lunch with Sam tomorrow 1pm').
Inputs: text (required), calendar_id, send_updates, account, +1 more.
reply_to_messageB
Reply to a message, keeping it in the same thread.
reply_all=True also copies the original To/Cc recipients (minus yourself).
Inputs: message_id (required), body_text, body_html, reply_all, +4 more.
respond_to_eventB
RSVP to an event you're invited to. response ∈ accepted/declined/tentative.
Inputs: event_id (required), response (required), calendar_id, comment, +3 more.
search_messagesA
Search messages using Gmail query syntax (e.g. 'is:unread from:alice newer_than:7d').
Returns message summaries plus next_page_token for pagination.
Inputs: query, max_results, page_token, include_spam_trash, +2 more.
search_threadsB
Search threads using Gmail query syntax. Returns thread stubs + next_page_token.
Inputs: query, max_results, page_token, include_spam_trash, +2 more.
send_draftC
Send an existing draft.
Inputs: draft_id (required), account, password.
send_emailC
Send a new email.
to/cc/bcc: recipient address lists. attachments: file paths on the server host.
sender: optional explicit From (defaults to the account's primary send-as address).
Provide body_text and/or body_html.
password: the account's password — required when password protection is enabled.
Inputs: to (required), subject (required), body_text, body_html, +6 more.
set_vacation_responderB
Enable or disable the vacation responder.
start_time/end_time are epoch-milliseconds strings (omit for no bound).
restrict_to_contacts/restrict_to_domain limit who receives the auto-reply.
Inputs: enabled (required), subject, body_text, body_html, +6 more.
starC
Star a message.
Inputs: message_id (required), account, password.
subscribe_calendarC
Add an existing calendar (by id) to this account's calendar list.
Inputs: calendar_id (required), color_id, account, password.
trash_messageA
Move a message to Trash (recoverable; not a permanent delete).
Inputs: message_id (required), account, password.
trash_threadC
Move an entire thread to Trash.
Inputs: thread_id (required), account, password.
unlabel_messageC
Remove one or more labels from a message.
Inputs: message_id (required), labels (required), account, password.
unlabel_threadA
Remove one or more labels from every message in a thread.
Inputs: thread_id (required), labels (required), account, password.
unstarC
Remove the star from a message.
Inputs: message_id (required), account, password.
unsubscribe_calendarB
Remove a calendar from this account's calendar list (does not delete the calendar).
Inputs: calendar_id (required), account, password.
untrash_messageC
Restore a message from Trash.
Inputs: message_id (required), account, password.
untrash_threadC
Restore an entire thread from Trash.
Inputs: thread_id (required), account, password.
update_aclB
Change the role of an existing share (get ruleId from list_acl).
Inputs: rule_id (required), role (required), calendar_id, account, +1 more.
update_calendarC
Update a calendar's metadata (patch).
Inputs: calendar_id (required), summary, description, time_zone, +3 more.
update_calendar_subscriptionC
Change how a calendar appears in your list: color, selected, hidden, name override, reminders.
Inputs: calendar_id (required), color_id, selected, hidden, +4 more.
update_draftA
Replace a draft's contents (Gmail replaces the whole message — pass all fields you want).
Inputs: draft_id (required), to, subject, body_text, +8 more.
update_eventB
Update an event (patch — only the fields you pass are changed).
Inputs: event_id (required), summary, start, end, +22 more.
update_labelC
Rename or change visibility of a label (identified by name or id).
Inputs: label (required), name, label_list_visibility, message_list_visibility, +2 more.
update_signatureB
Set the HTML signature for a send-as address (defaults to the primary address).
Inputs: signature_html (required), send_as, account, password.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 72 tools
Each tool targets a distinct operation on either Gmail or Calendar resources. Tools like mark_read, mark_unread, star, unstar, etc., are clearly differentiated by action and resource. Even batch operations are distinct from single operations.
Most tools follow a consistent verb_noun pattern (e.g., create_event, delete_label, search_messages). A few deviate (batch_modify_messages, quick_add_event, get_freebusy), but overall the pattern is predictable and aids understanding.
With 72 tools, the server is heavy but covers two major services (Gmail and Calendar). While the scope justifies a large surface, the count is still well above the typical 15–25 range, making it potentially overwhelming.
The tool set covers most core operations for Gmail and Calendar: CRUD, searching, labeling, sharing, etc. Minor gaps exist (e.g., managing send-as aliases, forwarding settings), but the surface is comprehensive for typical use cases.
Maintenance
Related MCP Connectors
A MCP server that works with Google Calendar to manage event listing, reading, and updates.
A MCP server for Gmail that lets you search, read, and draft emails and replies.
Hosted MCP server for your Gmail, Outlook and IMAP mail, with mailboxes and scopes set per agent.
8 MCP servers, 104+ tools: memory, social, PDF, email, images, calendar, scheduler, files.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA comprehensive MCP server providing 24 tools for full Gmail management, including email operations, label organization, and batch filtering. It features multi-account support and secure OAuth authentication for interacting with mailboxes via natural language.283 npmMIT
- AlicenseCqualityAmaintenanceA multi-account Google Workspace MCP server that drives Gmail, Google Calendar, and Google Drive across any number of Google accounts in parallel from one server.422MIT
- AlicenseBqualityDmaintenanceA comprehensive MCP server for Gmail, providing 17 tools to read, send, search, and manage emails with OAuth2 authentication, including attachment handling and privacy features.18124 npmMIT
- FlicenseNot gradedqualityDmaintenanceMulti-user Gmail MCP server that enables sending/receiving emails, managing accounts, and searching mail via SMTP/IMAP with per-user isolation and encrypted app passwords.-