Skip to main content
Glama
BMCCREATIVEDEVELOPER

Gmail & Calendar Multi-Account MCP Server

Gmail & Calendar Multi-Account MCP Server

A local Model Context Protocol (MCP) server that connects multiple Gmail accounts and Google Calendars to Claude Desktop. Runs entirely on your machine — no cloud hosting required.

Features

  • Multiple accounts — connect as many Gmail or Google Workspace accounts as you need

  • Unified email search — search across all accounts simultaneously with Gmail's full query syntax

  • Full read access — read individual messages and entire threads

  • Send & draft — compose and send emails, or save drafts, from any account

  • Label management — list labels, mark as read/unread, star messages

  • Google Calendar — list calendars, browse upcoming events, search by keyword

  • Google Docs — create, read, update, and delete documents

  • Google Sheets — create, read, update, and delete spreadsheets, with cell formatting

  • Google Forms — create, read, update, and delete forms, questions, and settings; read submitted responses

Related MCP server: Google Workspace MCP

Requirements

  • macOS (tested on macOS 14+)

  • Python 3.11+

  • A Google Cloud project with the Gmail API, Calendar API, Docs API, Sheets API, and Forms API enabled (free)

  • Claude Desktop

Installation

1. Clone the repository

git clone https://github.com/DiegoMaldonadoRosas/gmail-mcp.git
cd gmail-mcp

2. Run the setup script

bash setup.sh

This creates a virtual environment and installs all Python dependencies.

3. Configure your accounts

Copy the example config and fill in your accounts:

cp config.json.example config.json

Edit config.json:

{
  "accounts": {
    "personal": {
      "email": "you@gmail.com",
      "description": "Personal Gmail",
      "signature_html": "<div><br>--<br><strong>Your Name</strong><br>example.com</div>",
      "signature_image_path": "./signatures/personal.png"
    },
    "work": {
      "email": "you@company.com",
      "description": "Work account",
      "signature_image_path": "./signatures/work.png"
    }
  },
  "credentials_dir": "./credentials"
}

The account keys (personal, work) are the names you'll use when asking Claude to interact with a specific account.

Per-account signatures (optional)

Each account can have a signature applied automatically to every message it sends — you never pass signature info as a tool parameter. The signature is resolved from the first available source in this priority order (only one source is ever used per email):

  1. Auto-fetched Gmail signature — the signature set in that account's own Gmail Settings (Settings → See all settings → Signature), read live via the Gmail Settings API. If a signature exists there, it's used and config.json is ignored. Nothing to configure.

  2. signature_html (config.json) — used only if no Gmail signature is set. Appended to the HTML part of the email.

  3. signature_image_path (config.json) — used only if neither of the above applies. The image is embedded inline at the bottom of the email (via Content-ID / <img src="cid:...">), so it appears as a signature image, not a file attachment. Path is relative to the project root or absolute.

  4. No signature — if none of the above is set.

A plain-text body is always included as a fallback for clients that don't render HTML.

Field

Behavior

signature_html

HTML block appended to the HTML part. Used only when no Gmail Settings signature is found.

signature_image_path

Inline signature image. Used only when neither a Gmail Settings signature nor signature_html is set.

Re-authentication required: Auto-fetching needs the gmail.settings.basic scope, which was added to SCOPES. Accounts authenticated before this change must be re-authenticated once (python setup_auth.py) before the auto-fetched signature will work. Until then, the server silently falls back to the config.json sources.

4. Get Google OAuth credentials

  1. Go to Google Cloud Console

  2. Create a project and enable the Gmail API, Google Calendar API, Google Drive API, Google Docs API, Google Sheets API, and Google Forms API

  3. Go to APIs & Services → Credentials → + Create Credentials → OAuth 2.0 Client ID

  4. Choose Desktop app as the application type

  5. Download the JSON file and save it as credentials/client_secret.json

  6. Go to APIs & Services → OAuth consent screen → Test users and add every email address you configured in config.json

5. Authenticate your accounts

source .venv/bin/activate
python setup_auth.py

A browser window will open for each account. Sign in with the correct Google account. Tokens are saved locally and refreshed automatically — you only need to do this once per account.

Note: If you previously authenticated for Gmail only, you must re-run setup_auth.py after adding Calendar support so the tokens include the new Calendar permissions. The same applies after adding Google Docs, Sheets, or Forms support — re-run setup_auth.py once per account so tokens pick up the new documents/spreadsheets/forms.body/forms.responses.readonly scopes, or docs_*/sheets_*/forms_* calls will fail with a permission error.

6. Add the server to Claude Desktop

Open ~/Library/Application Support/Claude/claude_desktop_config.json and add:

{
  "mcpServers": {
    "gmail": {
      "command": "/absolute/path/to/gmail-mcp/.venv/bin/python",
      "args": ["/absolute/path/to/gmail-mcp/server.py"]
    }
  }
}

Replace /absolute/path/to/gmail-mcp with the actual path where you cloned the repo.

7. Restart Claude Desktop

All tools will appear automatically.

Available Tools

Gmail

Tool

Description

list_accounts

List all configured accounts and their auth status

gmail_get_profile

Get account profile and mailbox stats

gmail_search

Search emails using Gmail query syntax (one or all accounts)

gmail_read_message

Read the full content of a message

gmail_read_thread

Read all messages in a thread

gmail_send

Send an email from a specific account (supports HTML body + attachments)

gmail_create_draft

Save an email as a draft (supports HTML body + attachments)

gmail_list_drafts

List drafts in an account

gmail_list_labels

List all labels and folders

gmail_modify_labels

Add or remove labels (mark read/unread, star, etc.)

gmail_trash

Move a message to trash

gmail_send / gmail_create_draft parameters

Parameter

Required

Description

account

yes

Account name to send from / draft in

to

yes

Recipient(s), comma-separated

subject

yes

Email subject

body

yes

Plain-text body. Always sent as a fallback for non-HTML clients.

html_body

no

HTML body. The account's configured signature is appended automatically.

cc

no

CC recipients, comma-separated

bcc

no

BCC recipients, comma-separated

attachments

no

Array of local file paths to attach. A missing path returns a clear error.

thread_id

no

Gmail thread ID to reply into, nesting the message/draft into an existing conversation instead of starting a new thread.

in_reply_to

no

Message-ID header of the message being replied to. Sets the MIME In-Reply-To header.

references

no

Space-separated chain of Message-IDs for the thread. Sets the MIME References header.

Google Calendar

Tool

Description

calendar_list_calendars

List all calendars for an account (primary, work, shared, etc.)

calendar_list_events

List upcoming events, optionally filtered by date range

calendar_search

Search events by keyword (title, description, location, attendees)

calendar_get_event

Get full details of a specific event

Google Docs

Tool

Description

docs_create

Create a new Google Doc, optionally seeded with initial text content

docs_read

Read a document's title and plain-text content

docs_update

Append text and/or find-and-replace text across a document

docs_format

Apply bold, italic, underline, strikethrough, font size/family, or color to text

docs_rename

Rename a document (changes its title/file name)

docs_delete

Move a document to Drive trash

docs_update parameters

Parameter

Required

Description

account

yes

Account name

document_id

yes

Google Docs document ID

append_text

no

Text to append to the end of the document

find_text

no

Text to find and replace across the whole document

replace_text

no

Replacement text used when find_text is set (defaults to empty string, i.e. deletes find_text)

match_case

no

Whether find_text matching is case-sensitive (default: false)

At least one of append_text or find_text must be provided.

docs_format parameters

Parameter

Required

Description

account

yes

Account name

document_id

yes

Google Docs document ID

find_text

no

Text to locate and format (alternative to start_index/end_index)

match_case

no

Whether find_text matching is case-sensitive (default: false)

match_all

no

Format every occurrence of find_text, not just the first (default: true)

start_index / end_index

no

Explicit range to format, in place of find_text

bold / italic / underline / strikethrough

no

Set (true) or clear (false) the style

font_size

no

Font size in points, e.g. 14

font_family

no

Font family name, e.g. "Arial"

foreground_color

no

Text color as a hex string, e.g. "#FF0000"

background_color

no

Highlight color as a hex string, e.g. "#FFFF00"

At least one style change (bold, italic, underline, strikethrough, font_size, font_family, foreground_color, and/or background_color) must be provided, and either find_text or start_index/end_index to target the range.

Google Sheets

Tool

Description

sheets_create

Create a new spreadsheet, optionally seeded with initial row data

sheets_info

Get a spreadsheet's title, URL, and list of tabs

sheets_read

Read cell values for a range (defaults to the whole first tab)

sheets_update

Overwrite cell values for a range

sheets_append

Append rows after the last row of existing data

sheets_clear

Clear cell values (not formatting) in a range

sheets_format

Apply bold, italic, underline, strikethrough, font size/family, colors, alignment, or number format to a range

sheets_add_tab

Add a new tab to a spreadsheet

sheets_rename_tab

Rename a tab

sheets_delete_tab

Delete a tab

sheets_rename

Rename a spreadsheet (changes its title/file name)

sheets_delete

Move a spreadsheet to Drive trash

Ranges use A1 notation, e.g. "Sheet1!A1:D10" or just "Sheet1" for the whole tab.

sheets_format parameters

Parameter

Required

Description

account

yes

Account name

spreadsheet_id

yes

Google Sheets spreadsheet ID

range

yes

A1-notation range to format, e.g. "Sheet1!A1:C5"

bold / italic / underline / strikethrough

no

Set (true) or clear (false) the style

font_size

no

Font size in points, e.g. 12

font_family

no

Font family name, e.g. "Arial"

foreground_color

no

Text color as a hex string, e.g. "#FF0000"

background_color

no

Cell fill/highlight color as a hex string, e.g. "#FFFF00"

horizontal_alignment

no

"LEFT", "CENTER", or "RIGHT"

number_format

no

Number format pattern, e.g. "#,##0.00" or "yyyy-mm-dd"

number_format_type

no

Category for number_format: NUMBER, CURRENCY, PERCENT, DATE, TIME, DATE_TIME, SCIENTIFIC, or TEXT (default: NUMBER)

At least one style change must be provided.

Google Forms

Tool

Description

forms_create

Create a new blank form (title only — add questions afterward)

forms_read

Get a form's title, description, questions (with item IDs/types/options), share link, and publish state

forms_add_question

Add a question: SHORT_TEXT, PARAGRAPH, RADIO, CHECKBOX, DROPDOWN, SCALE, DATE, TIME, or RATING

forms_update_question

Update an existing question by item ID

forms_move_question

Move a question to a new position

forms_delete_question

Delete a question

forms_update_info

Update the form's visible title/description

forms_update_settings

Update email collection and/or quiz mode

forms_set_publish_settings

Publish/unpublish a form and control whether it accepts responses

forms_list_responses

List submitted responses

forms_get_response

Get a single submitted response

forms_rename

Rename a form's Drive file name (not the visible title — use forms_update_info for that)

forms_delete

Move a form to Drive trash

Known limitation: the Forms API cannot create FILE_UPLOAD questions at all (a hard Google API restriction, not something this server can work around). Submitted file-upload answers can still be read via forms_get_response/forms_list_responses (returned as Drive file IDs), just not created as a new question.

forms_add_question parameters

Parameter

Required

Description

account

yes

Account name

form_id

yes

Google Form ID

title

yes

The question text

question_type

yes

SHORT_TEXT, PARAGRAPH, RADIO, CHECKBOX, DROPDOWN, SCALE, DATE, TIME, or RATING

options

conditional

Answer choices — required for RADIO/CHECKBOX/DROPDOWN

required

no

Whether the question must be answered (default: false)

description

no

Helper text shown under the question

low / high / low_label / high_label

no

SCALE only

include_year / include_time

no

DATE only

duration

no

TIME only — elapsed duration vs. time of day

rating_icon / rating_levels

no

RATING only

index

no

0-based position to insert at (default: append at the end)

Usage Examples

Once connected, you can ask Claude things like:

Email:

  • "Do I have any unread emails in my work account?"

  • "Search for invoices received in the last month across all my accounts"

  • "Read the last email from John in my personal account"

  • "Draft a reply to the budget email in my work account"

  • "Mark all emails from newsletter@example.com as read"

Calendar:

  • "What meetings do I have this week in my work account?"

  • "Search for events related to 'product launch' in my personal calendar"

  • "List all my calendars in my work account"

  • "What are the details of tomorrow's standup?"

Docs:

  • "Create a Google Doc called 'Meeting Notes' in my work account"

  • "Read the content of this doc: "

  • "Append today's action items to the end of my project plan doc"

  • "Replace all instances of 'Q1' with 'Q2' in that doc"

Adding a New Account

  1. Add the account to config.json

  2. Add the email as a Test User in Google Cloud Console (OAuth consent screen)

  3. Run python setup_auth.py — it will only prompt for the new account

  4. Restart Claude Desktop

Security

  • OAuth tokens are stored locally in credentials/tokens/ and are excluded from version control via .gitignore

  • config.json (which contains your email addresses) is also excluded from version control

  • Nothing is sent to any third-party server — all communication is directly between your Mac and Google's APIs

  • To revoke access at any time, visit myaccount.google.com/permissions

Project Structure

gmail-mcp/
├── server.py           # MCP server — exposes tools to Claude
├── auth.py             # OAuth2 token manager (per account)
├── gmail.py            # Gmail API wrapper
├── gcalendar.py        # Google Calendar API wrapper
├── gdrive.py           # Google Drive API wrapper
├── gdocs.py            # Google Docs API wrapper
├── config.py           # Configuration loader
├── setup_auth.py       # One-time authentication script
├── setup.sh            # First-time installer
├── requirements.txt    # Python dependencies
├── config.json.example # Account configuration template
└── .gitignore          # Excludes credentials and config.json

License

MIT

Related MCP Connectors

Related MCP Servers