Skip to main content
Glama
Gmail + Calendar MCP Server

Gmail + Calendar MCP Server

by eitan3

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 account argument to pick the account and an optional password (see below).

How credentials work (2 env vars + 1 optional)

Variable

Required

Format

Example

GMAIL_CLIENT

yes

client_id|client_secret

123-abc.apps.googleusercontent.com|GOCSPX-xxxx

GMAIL_ACCOUNTS

yes

selector=refresh_token pairs, separated by ; or newlines

alice@gmail.com=1//0gFoo;work=1//0gBar

PASSWORDS

no

selector=password pairs, separated by ; or newlines

alice@gmail.com=hunter2;work=s3cret

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_ACCOUNTS selectors (case-insensitively). A PASSWORDS key that matches no account makes the server refuse to start. An account with no PASSWORDS entry 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 PASSWORDS is set but has no valid selector=password entries (e.g. only separators), the server errors out instead of silently running unprotected. Only an absent (or blank) PASSWORDS turns the gate off, in which case the password argument 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)

  1. In Google Cloud Console create/select a project.

  2. Enable the Gmail API.

  3. Configure the OAuth consent screen (External is fine; add yourself as a Test user, or publish).

  4. 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-mcp belongs to an unrelated PyPI package — don't use it. This project is published as gmail-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_ACCOUNTS

If 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.com

Headless 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 version in pyproject.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/end as RFC3339 (2026-06-15T10:00:00) with time_zone (IANA, e.g. Asia/Jerusalem), or set all_day=true with a YYYY-MM-DD date. Calendar ops default to the primary calendar when calendar_id is omitted.

  • Recurring: recurrence is a list of RRULE strings, e.g. ["RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=8"].

  • Invites: attendees is an email list; add_meet=true attaches a Google Meet link; send_updates ∈ all/externalOnly/none controls 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 in GMAIL_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 tests

Available Tools

72 tools
archiveC

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.

share_calendarC

Share a calendar.

role ∈ reader/writer/owner/freeBusyReader. scope_type ∈ user/group/domain/default; scope_value is the email/domain (omit for 'default' = public).

Inputs: role (required), scope_value, scope_type, calendar_id, +3 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.

unshare_calendarB

Remove a share / access rule (get ruleId from list_acl).

Inputs: rule_id (required), calendar_id, 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

    B3/5.0

    Scored across 72 tools

    Disambiguation5/5

    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.

    Naming Consistency4/5

    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.

    Tool Count3/5

    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.

    Completeness4/5

    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

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      A 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 npm
      MIT
    • A
      license
      B
      quality
      D
      maintenance
      A comprehensive MCP server for Gmail, providing 17 tools to read, send, search, and manage emails with OAuth2 authentication, including attachment handling and privacy features.
      18
      124 npm
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      Multi-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.
      -