Skip to main content
Glama

gmail-mcp

A local MCP server that gives Claude Code access to several Gmail accounts at once, and replies to customers from the alias they wrote to (for example support@example.com).


1. What it does

Tool

Purpose

list_accounts

Signed-in accounts and the "Send mail as" addresses of each.

search_threads

Search one account with Gmail syntax (to:support@example.com is:unread).

get_thread

Read a conversation as plain text, including the list of attachments.

save_attachment

Download an attachment to the inbox folder and return its local path.

create_draft

Write a draft, optionally with files from the outbox. As a reply, it threads correctly and picks the right alias. Nothing is sent.

update_draft

Revise a draft in place (same draft id). Pass only what changes; existing attachments are kept.

list_drafts

Find drafts, e.g. ones created in an earlier session.

delete_draft

Discard a draft. Permanent: drafts skip the Trash.

send_draft

Send a draft you reviewed.

Every tool takes an account. You can pass the account address or one of its aliases: support@example.com resolves to owner@example.com. A new (non-reply) message is then sent from that alias.


Related MCP server: multi-gmail-mcp-server

2. Setup

You do steps 2.1–2.3 once. You repeat step 2.5 for each Google account.

2.1 Gmail: allow sending from each alias

For every alias you want to reply from (support@, sales@, …):

  1. Open Gmail as the account that owns the aliases.

  2. Go to Settings → See all settings → Accounts → Send mail as → Add another email address.

  3. Enter the alias. Leave Treat as an alias ticked.

Because these are aliases of your own Workspace user, Gmail adds them without SMTP setup.

New app later? Add the alias in the Workspace Admin console, and add it here. Receiving needs the first; replying needs the second.

2.2 Google Cloud: create an OAuth client

  1. Open console.cloud.google.com and create a project, e.g. gmail-mcp.

  2. APIs & Services → Library → enable the Gmail API.

  3. Google Auth Platform → Branding: set an app name and your support email.

  4. Audience:

    • User type: External (needed so personal @gmail.com accounts can sign in).

    • Publishing status: click Publish app → In production.

  5. Data Access → add these scopes:

    • https://www.googleapis.com/auth/gmail.readonly

    • https://www.googleapis.com/auth/gmail.compose

  6. Clients → Create client → type Desktop app → download the JSON.

  7. Save it as ~/.config/gmail-mcp/client_secret.json:

    mkdir -p ~/.config/gmail-mcp && chmod 700 ~/.config/gmail-mcp
    mv ~/Downloads/client_secret_*.json ~/.config/gmail-mcp/client_secret.json

Why "In production"? In "Testing" mode Google expires refresh tokens after 7 days, so you'd have to sign in again every week. In production the app stays unverified, which is fine for personal use. You'll see a warning when signing in (step 2.5).

2.3 Install and build

npm install
npm run build

2.4 Register with Claude Code

claude mcp add gmail --scope user -- node <path-to-repo>/dist/index.js

Recommended permissions in ~/.claude/settings.json. Reading and drafting run freely; sending and deleting always ask:

{
  "permissions": {
    "allow": [
      "mcp__gmail__list_accounts",
      "mcp__gmail__search_threads",
      "mcp__gmail__get_thread",
      "mcp__gmail__save_attachment",
      "mcp__gmail__list_drafts",
      "mcp__gmail__create_draft",
      "mcp__gmail__update_draft"
    ],
    "ask": ["mcp__gmail__send_draft", "mcp__gmail__delete_draft"]
  }
}

2.5 Sign in each account

Run once per account. The address is optional; it only pre-selects the account in the browser.

npm run login -- owner@example.com
npm run login -- you@gmail.com

On the "Google hasn't verified this app" screen, click Advanced → Go to gmail-mcp. This screen appears because you are the app's developer and its only users.

New accounts show up right away; you don't need to restart.


3. How the reply address is chosen

When create_draft gets a replyToMessageId, it decides the From address in this order:

  1. from argument, if given. It must be a "Send mail as" address.

  2. Your own message? If you're following up on a message you sent, it reuses that sender.

  3. The alias the customer wrote to. It takes the first "Send mail as" address found in the original message's To, then Cc, then Delivered-To headers.

  4. The account default as a fallback.

The result always includes from and fromReason, so the choice is never silent.

The reply also:

  • goes to the customer's Reply-To if set, otherwise to the sender;

  • keeps the original subject (with Re:) and sets In-Reply-To / References, so Gmail threads it;

  • quotes the original message underneath (turn off with quoteOriginal: false).

update_draft keeps all of this. It keeps the sender, recipients and threading headers unless you override them. A new body replaces the old text, and reply drafts get the original quoted again underneath.


4. Attachments

Two local folders, created on first start with owner-only access:

Folder

Default

Used for

Inbox

~/gmail-mcp/inbox/

save_attachment writes files to <inbox>/<account>/<messageId>/.

Outbox

~/gmail-mcp/outbox/

The only folder files can be attached from.

4.1 Reading an attachment

  1. get_thread lists each attachment with its name, type, size and partId.

  2. save_attachment with the messageId and partId downloads it and returns the path.

  3. Claude opens the file from that path (images, PDFs and text files work directly).

Files are never overwritten: saving the same attachment again returns the existing file.

4.2 Attaching a file

  1. Put the file in the outbox, e.g. ~/gmail-mcp/outbox/guide.pdf.

  2. Ask Claude to attach it. create_draft takes attachments: ["guide.pdf"]; update_draft takes addAttachments and removeAttachments (by filename).

The combined size limit is Gmail's 25 MB.

4.3 Changing the folders

Create ~/.config/gmail-mcp/config.json and set either key:

{
  "inboxDir": "~/Documents/Mail/inbox",
  "outboxDir": "~/Documents/Mail/outbox"
}

Paths must be absolute or start with ~/. Restart Claude Code sessions to apply.


5. Security model

Concern

How it's handled

What the server can do

Scopes are gmail.readonly + gmail.compose: read, manage drafts, send. It can't delete mail or change labels.

Accidental sending

Replies are drafts. send_draft is separate and should be set to "ask" in Claude Code.

Discarding drafts

delete_draft only touches drafts, but it's permanent. Set it to "ask" as well.

Token storage

~/.config/gmail-mcp/tokens/<email>.json, mode 0600, outside the repo. Change the folder with GMAIL_MCP_HOME.

Leaking local files

Attachments come only from the outbox. Paths outside it, including via .. or symlinks, are refused.

Untrusted downloads

Attachments are only saved, never opened or run. File names are cleaned so they can't point outside the inbox folder.

Header injection

Header values with line breaks are rejected. Recipients are validated as email addresses.

Revoking access

Remove the app at myaccount.google.com/permissions and delete the token file.


6. Development

npm run check

This runs typecheck, lint and tests together.

Path

Responsibility

src/index.ts

Entry point: wires config, auth and the MCP server over stdio.

src/server.ts

Tool definitions (input schemas, descriptions) → GmailService.

src/gmail/service.ts

Gmail API calls for every tool.

src/gmail/reply.ts

Pure reply planning: recipients, subject, threading, quote.

src/gmail/alias.ts

Pure alias selection.

src/gmail/mime.ts

Builds the RFC 2822 message, multipart when files are attached.

src/gmail/parse.ts

Reads headers and body text out of Gmail payloads.

src/attachments/

Filename cleanup, outbox path check, inbox saving.

src/settings.ts

Optional config.json: inbox and outbox folders.

src/auth/

OAuth client, token storage, refresh persistence.

src/cli/login.ts

Browser sign-in (loopback redirect + PKCE).

The pure modules have no I/O and are covered by unit tests. GmailService is tested against a fake Gmail API in test/service.test.ts.


7. Troubleshooting

Symptom

Fix

Access blocked: … admin policy when signing in a Workspace account

Admin console → Security → Access and data control → API controls → Manage third-party app access → add your OAuth client ID as Trusted.

… is outside the outbox folder

Copy the file into the outbox first (see section 4.2).

… is not a "Send mail as" address

Add the alias in Gmail (step 2.1). The server picks up new aliases within 10 minutes.

saved sign-in … revoked or expired

Run npm run login for that account again. If this happens weekly, the app is still in "Testing" (step 2.2.4).

Tools missing in Claude Code

Run npm run build, and check the path registered in step 2.4 still exists.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables Claude to read, search, send, label, and trash emails in any Gmail account via Google's Gmail API, using OAuth2 authentication with automatic token refresh.
    67 npm
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Connects multiple Gmail accounts to Claude Desktop via MCP, enabling email search, labeling, drafts, and confirmed sending through natural language.
    13
    167 npm
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables reading, searching, drafting, and sending customer support emails via Gmail directly through Claude, eliminating manual copy-pasting.
    -