multi-gmail-mcp
Multi-account Gmail MCP server that lets assistants scan inbox, read threads, draft and send emails only after human approval, and manage follow-up reminders.
README
multi-gmail-mcp
Multi-account Gmail MCP server for Claude Desktop, Cursor, and other MCP hosts.
It lets your assistant:
- connect one or more Gmail accounts
- scan the inbox with lightweight list mode (metadata only)
- load one full thread at a time with
get_thread(plain or quote-stripped bodies) - draft replies from real message text (not Gmail snippets)
- send replies (
send) or new outbound mail (send_new), including HTML bodies - save drafts to Gmail
- send only after explicit approval
- manage follow-up reminders for email threads
Safety rule: nothing is ever sent automatically. send, send_new, and followup_send must only be called after human approval.
What This MCP Does
This server is thread-first, not message-first, and metadata-first for inbox triage.
multi_gmail_fetchwithmode=list(default) returns small per-thread metadata — snippets are not full emails- for each thread you care about, call
multi_gmail_get_threadonce — never load many full threads in one LLM context multi_gmail_get_threadreturns a chronological transcript (format,stripped,latestN)multi_gmail_sendsends an approved reply (messageIdrequired);multi_gmail_send_newstarts a new thread (standalone outbound mail)- outbound tools support
format:text/plain(default) ortext/html(pricing tables, CTAs) - legacy
mode=fullon fetch still batch-loads and auto-drafts (token-heavy — avoid for normal inbox review) multi_gmail_followup_duerefreshes the thread before showing a due follow-up
This project is designed for setups where one person may handle:
- multiple Gmail accounts
- multiple chats against one MCP server session
- inbox review plus follow-up workflows in the same toolset
Requirements
- Node.js
18+ - a Gmail account
- Claude Desktop, Cursor, or another MCP-compatible host
- a Google Cloud project with Gmail API enabled
Check Node:
node --version
Install
npm install -g @nitsantechnologies/multi-gmail-mcp
mkdir -p ~/.multi-gmail-mcp
Set this env var anywhere you run the server or auth command:
export MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp"
Useful commands:
multi-gmail-mcp
multi-gmail-mcp-auth
Local development from git still works:
git clone https://github.com/nitsan-ai/Multi-Gmail-MCP.git
cd Multi-Gmail-MCP
npm install
npm start
npm run auth
Google OAuth Setup
- Open Google Cloud Console
- Create or select a project
- Enable Gmail API
- Go to APIs & Services -> Credentials
- Create OAuth client ID
- Choose Desktop app
- Download the JSON file
- Save it in your config home as:
~/.multi-gmail-mcp/credentials.json
Set a different location with MULTI_GMAIL_MCP_HOME. For local git development, project root still works by default.
OAuth scopes used by this MCP:
https://www.googleapis.com/auth/gmail.readonlyhttps://www.googleapis.com/auth/gmail.modifyhttps://www.googleapis.com/auth/gmail.sendhttps://www.googleapis.com/auth/gmail.settings.basic(Gmail signature on send/draft)
After upgrading: re-authenticate every connected account once so tokens include gmail.settings.basic (required for automatic signature appending on send, set_draft, and followup_send):
MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp" multi-gmail-mcp-auth --alias <your-alias>
Then reconnect in your MCP client (connect + connect_finish).
Local Account Auth
Authenticate the first Gmail account:
MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp" multi-gmail-mcp-auth
Add another account with an alias:
MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp" multi-gmail-mcp-auth --alias work
Notes:
- token files are saved under
$MULTI_GMAIL_MCP_HOME/accounts/ - each alias gets its own token JSON
--accountis accepted as a synonym for--alias
Connect to Claude Desktop
Edit:
~/Library/Application Support/Claude/claude_desktop_config.json
Example:
{
"mcpServers": {
"multi-gmail-mcp": {
"command": "npx",
"args": [
"-y",
"@nitsantechnologies/multi-gmail-mcp"
],
"env": {
"MULTI_GMAIL_MCP_HOME": "/Users/you/.multi-gmail-mcp"
}
}
}
}
Replace /Users/you/.multi-gmail-mcp with your real path.
After saving:
- fully quit Claude Desktop
- reopen Claude Desktop
Connect to Cursor
Open Cursor Settings -> MCP -> Add server and use:
{
"multi-gmail-mcp": {
"command": "npx",
"args": [
"-y",
"@nitsantechnologies/multi-gmail-mcp"
],
"env": {
"MULTI_GMAIL_MCP_HOME": "/Users/you/.multi-gmail-mcp"
}
}
}
Replace /Users/you/.multi-gmail-mcp with your real path.
First-Time Flow Inside Claude or Cursor
Run this once per account:
multi_gmail_statusmulti_gmail_connectwithConnect you@example.com personalmulti_gmail_connect_finishmulti_gmail_set_signermulti_gmail_fetchwithmode="list"multi_gmail_get_threadfor each thread you will read or reply to- review drafts
- use
multi_gmail_send(reply) ormulti_gmail_send_new(new outbound) only after approval
Typical signer example:
Set signer name to Jane Smith
Daily Workflow
Recommended inbox review (list → one thread at a time)
Ask:
Fetch my inbox with mode list, then get_thread for threads I need to reply to
Step 1 — triage (small payload):
{ "mode": "list", "maxResults": 10 }
You get per thread: threadId, latestMessageId, subject, participants, date, direction, snippet (preview only).
Step 2 — full bodies (one thread per call):
{
"threadId": "<from fetch>",
"format": "full",
"stripped": false
}
stripped=false(default) — full plain-text body per message (best for reading mail)stripped=true— quote/signature-stripped text (best when drafting in long threads)format=latest+latestN— first message + last N messages, with omission markers
Present message.text verbatim to the user — do not summarize snippets or bodies.
Step 3 — draft and send:
- Reply in an existing thread →
multi_gmail_sendwithmessageIdfrom fetch /get_thread - New outbound email →
multi_gmail_send_new(nomessageId)
Optional: mode=full on fetch still auto-drafts every thread in one batch (legacy; can cause token overflow).
With mode=list, follow-up-labeled threads are excluded from normal inbox review. Optional markdown export is written unless writeMarkdownFile: false. Nothing is sent until you approve.
After review, per item you can:
sendorsend_newset_drafteditcancel/skip
Follow-up review
Ask:
Show due follow-ups
What happens:
- due reminders are loaded
- the thread is refreshed from Gmail first
- if the recipient already replied, the reminder is resolved automatically
- otherwise a fresh follow-up draft is shown
Tool Reference
The MCP registers both prefixed and unprefixed names:
multi_gmail_fetchandfetchmulti_gmail_followup_dueandfollowup_due- etc.
In practice, most hosts will show the multi_gmail_* names.
Setup and status tools
multi_gmail_help
Shows the first-time setup flow.
Input: none
multi_gmail_status
Shows:
- connected account
- signer status
- due follow-ups
- last inbox batch
- useful local paths
Input:
accountAliasoptionalchatScopeoptional
multi_gmail_connect
Starts Gmail login for one account and opens the browser.
Input:
commandrequired
Format:
Connect you@example.com personal
Connect you@example.com work
multi_gmail_connect_finish
Completes the login started by connect.
Input:
codeoptionalpendingAliasoptionalchatScopeoptional
Normally you do not need to paste the code manually; the local callback server completes it.
multi_gmail_accounts
Lists all saved local account aliases.
Input: none
multi_gmail_set_signer
Stores the display name used in draft replies for the current session.
Input:
namerequiredfollowUpLabeloptionalaccountAliasoptionalchatScopeoptional
multi_gmail_set_mode
Switches response mode for the current chat scope.
Input:
moderequired:standardorcompact
multi_gmail_diagnostics
Checks:
- credentials file
- accounts directory
- reminder store
- active account binding
Input:
accountAliasoptionalchatScopeoptional
multi_gmail_setup_labels
Ensures the configured Gmail labels exist.
Useful if labels do not appear after connect or fetch.
Input:
accountAliasoptionalchatScopeoptional
Inbox and thread tools
multi_gmail_fetch
Lists inbox threads for triage, or (legacy) batch-loads full threads and auto-drafts replies.
Recommended: mode=list (default), then get_thread per selected thread.
mode |
Behavior |
|---|---|
list (default) |
Metadata only: threadId, latestMessageId, subject, participants, snippet, dates, direction. Snippets are not full emails. |
full |
Legacy: loads full bodies and drafts every thread in one call — token-heavy; avoid for normal inbox review. |
Important behavior:
maxResultsmeans unique inbox threadslistreturnsmessageId/threadIdforsendandget_threadincludeLatestBody(list only): optional latest-message plain body per thread, capped at 15 threads — still preferget_threadfor one thread- Response includes
gmailListQuery— the exact Gmailqstring sent to the API
queryMode |
Behavior |
|---|---|
inbox (default) |
Prepends inbox review filters (in:inbox, excludes follow-up label) |
raw |
Passes query directly to Gmail — use for sent mail, archives, all-mail, date filters |
Input:
modeoptional:listorfull, defaultlistmaxResultsoptional, default20, max100queryoptional Gmail search string (combined with inbox filters whenqueryMode=inbox)queryModeoptional:inboxorraw, defaultinboxincludeLatestBodyoptional, defaultfalse(list mode only)saveGmailDraftsoptional, defaultfalse(mainlyfullmode)writeMarkdownFileoptional, defaulttrue(fullmode / review export)accountAliasoptionalchatScopeoptional
Examples:
{
"mode": "list",
"maxResults": 10
}
{
"mode": "list",
"maxResults": 15,
"query": "newer_than:7d"
}
Sent-mail / historical analysis (raw Gmail query):
{
"mode": "list",
"queryMode": "raw",
"query": "in:sent after:2026/01/01 before:2026/04/01",
"maxResults": 50
}
Legacy batch mode (avoid for daily triage):
{
"mode": "full",
"maxResults": 5,
"writeMarkdownFile": false
}
multi_gmail_send
Sends one approved reply in an existing thread. Marks the source message read and returns IDs for follow-up threading.
For new outbound mail (no source message), use multi_gmail_send_new instead.
Input:
messageIdrequired — fromfetchorget_threadtorequiredsubjectrequiredbodyrequired unless legacyhtmlis setformatoptional:text/plain(default) ortext/html— whentext/html,bodyis the HTML part (plain part auto-generated)htmloptional — legacy HTML part (preferformat+body)cc/bccoptional — single email, comma-separated string, array, orName <email@example.com>(multiple recipients)quoteOriginaloptional, defaulttrue— append Gmail-style quoted parent history below your new body (false= new body only)accountAliasoptionalchatScopeoptional
Response includes: sentMessageId, threadId, isNewThread: false, markedReadMessageId.
Example (plain reply):
{
"messageId": "19e90840fbfd1961",
"to": "recipient@example.com",
"subject": "Re: Pricing",
"body": "Thanks — here is the updated quote."
}
multi_gmail_send_new
Sends one approved new email. Use for standalone outbound mail or follow-ups in the same thread.
Input:
torequiredthreadIdoptional — from a priorsend_newresponse; adds this message to that Gmail thread (follow-up in same thread)subjectrequired — Unicode (–, ü, €, etc.) is RFC 2047–encoded automaticallybodyrequired unless legacyhtmlBodyis setformatoptional:text/plain(default) ortext/htmlhtmlBodyoptional — legacy HTML (preferformat+body)ccoptionalbccoptionalaccountAliasoptionalchatScopeoptional
Response includes: sentMessageId, threadId, isNewThread: true — store threadId for phase-2 threading.
Example (HTML outbound):
{
"to": "recipient@example.com",
"subject": "Product — pricing overview",
"format": "text/html",
"body": "<h1>Hello</h1><p>See the <a href=\"https://example.com/pricing\">pricing table</a>.</p>"
}
multi_gmail_set_draft
Creates or updates a Gmail draft reply without sending.
Input:
messageIdrequiredtorequiredsubjectrequiredbodyrequired unless legacyhtmlis setformatoptional:text/plain(default) ortext/htmlhtmloptional — legacy HTML partcc/bccoptional — comma-separated string, array, orName <email@example.com>quoteOriginaloptional, defaulttrue— append quoted parent history (Gmail···expander)accountAliasoptionalchatScopeoptional
multi_gmail_get_thread
Loads one Gmail thread as an ordered transcript. Call after fetch mode=list when you need real message bodies.
format |
Behavior |
|---|---|
full (default) |
All messages with bodies |
latest |
First message + latest latestN messages; middle messages collapsed with [N earlier messages omitted] |
metadata |
Headers/dates only — no bodies |
Body options (when format is not metadata):
strippeddefaultfalse— full plain-text per messagestripped=true— removes quoted reply history and signatures (drafting in long threads)includeRaw=true— addsrawTextalongside strippedtextfor debugging
Input:
threadIdrequired — fromfetch,followup_due, orarchiveformatoptional:metadata,latest, orfull(defaultfull)latestNoptional, default5, max50(whenformat=latest)strippedoptional, defaultfalseincludeRawoptional, defaultfalseaccountAliasoptionalchatScopeoptional
Example (read full mail):
{
"threadId": "19e913bf5e640ea2",
"format": "full",
"stripped": false
}
Example (draft in a long thread):
{
"threadId": "19e913bf5e640ea2",
"format": "full",
"stripped": true
}
multi_gmail_archive
Archives one thread by removing the INBOX label.
Input:
threadIdrequiredaccountAliasoptionalchatScopeoptional
multi_gmail_fetch_drafts
Lists messages in Gmail Drafts.
Input:
maxResultsoptionalaccountAliasoptionalchatScopeoptional
multi_gmail_fetch_sent (deprecated)
Lists the latest messages in Gmail Sent without query filters.
Prefer multi_gmail_fetch with queryMode=raw and a Gmail query such as in:sent after:2026/01/01 before:2026/04/01 for filtered sent-mail and historical analysis.
Input:
maxResultsoptionalaccountAliasoptionalchatScopeoptional
Follow-up tools
multi_gmail_followup_trigger
Creates a new follow-up plan for a thread, or updates the existing open plan for that same thread.
Input:
messageIdrequiredpatternoptionaldaysListoptionalbusinessDaysOnlyoptional, defaultfalsedueWeekdayoptionalcreateGmailDraftoptional, defaultfalseaccountAliasoptionalchatScopeoptional
Rules:
- use either
patternordaysList - not both
daysListmust be ascending- duplicates are not allowed
Examples:
{
"messageId": "gmail-message-id",
"daysList": [1, 3]
}
{
"messageId": "gmail-message-id",
"pattern": "1, 3, 7 business days",
"businessDaysOnly": true
}
Scheduling behavior:
- first entry is due from now
- later entries are chained
- example:
[1, 3]means:- follow-up 1 in 1 day
- follow-up 2 in 3 days after follow-up 1 is sent
multi_gmail_followup_due
Lists due follow-up reminders for the active account.
Behavior:
- refreshes the thread before returning results
- includes full
threadContext - skips reminders if the recipient already replied
Input:
accountAliasoptionalchatScopeoptional
multi_gmail_followup_send
Sends one approved follow-up email.
Input:
reminderIdrequiredtooptionalsubjectoptionalbodyoptional (unless legacyhtmlis set)formatoptional:text/plain(default) ortext/htmlhtmloptional — legacy HTML partcc/bccoptional — comma-separated string, array, orName <email@example.com>quoteOriginaloptional, defaulttrue— append quoted parent history below the follow-up bodyaccountAliasoptionalchatScopeoptional
Only use this after the user explicitly approves the draft.
multi_gmail_followup_cleanup
Deletes follow-up reminder records from the local store.
Input filters:
reminderIdsmessageIdfor Gmail internal message idmessageHeaderIdfor RFCMessage-IDfrom Gmail Show originalsourceThreadIdfollowUpChainIddeleteAllwithconfirm: truestatusescancelChainremoveGmailLabelaccountAliaschatScope
Examples:
Delete by RFC Message-ID:
{
"messageHeaderId": "<abc123@example.com>"
}
Delete one chain:
{
"reminderIds": ["reminder-id"],
"cancelChain": true
}
Delete all reminders for an account:
{
"deleteAll": true,
"confirm": true
}
Multiple Accounts and Shared Sessions
accountAlias
Use accountAlias when you want to target a specific saved local account:
{
"accountAlias": "work"
}
chatScope
Some MCP hosts reuse one server session across many chats.
In that case, pass the same chatScope on:
connectconnect_finishset_signer- every later Gmail tool in that same chat
Example:
{
"chatScope": "work-inbox"
}
This keeps one chat’s active account binding separate from another chat’s.
Files and Data
Important local paths under $MULTI_GMAIL_MCP_HOME:
credentials.jsonGoogle OAuth client credentialsaccounts/saved Gmail OAuth tokens, one JSON file per aliasdata/followup-reminders.jsonlocal follow-up reminder storedata/inbox-reviews/latest-inbox-review.mdlatest markdown inbox review export
Project layout:
~/.multi-gmail-mcp/
├── credentials.json
├── accounts/
└── data/
├── followup-reminders.json
└── inbox-reviews/
Useful env vars:
| Name | Purpose |
|---|---|
MULTI_GMAIL_MCP_HOME |
base directory for credentials, tokens, and local data |
GOOGLE_CREDENTIALS_PATH |
absolute or config-home-relative path to OAuth credentials |
ACCOUNTS_DIR |
absolute or config-home-relative path to token files |
FOLLOWUP_REMINDERS_PATH |
absolute or config-home-relative reminder store path |
GMAIL_REVIEW_MARKDOWN_DIR |
absolute or config-home-relative inbox export directory |
Dependencies (for HTML bodies and quote stripping): email-reply-parser, planer, jsdom.
Gmail Labels
This MCP can create Gmail user labels automatically.
Expected labels:
Inbox-reviewMulti-Gmail-MCP Follow-up
Use multi_gmail_setup_labels if they do not appear.
Troubleshooting
Server not showing in Claude or Cursor
- make sure
MULTI_GMAIL_MCP_HOMEpoints at your real config directory - make sure
node --versionis18+ - fully quit and reopen the app
- if
npxis unavailable inside the app, install globally and use the full path tomulti-gmail-mcp
multi_gmail_status not available
Usually this means the MCP server did not start at all.
Check:
- JSON config syntax
- absolute path to the server entry
- Node availability
- app restart after config change
Token expired or auth errors
Re-authenticate:
rm ~/.multi-gmail-mcp/accounts/*.json
MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp" multi-gmail-mcp-auth
Permission or scope errors
Make sure:
- Gmail API is enabled
- OAuth client is a Desktop app
- scopes include
gmail.modify,gmail.send, andgmail.settings.basic
Then re-authenticate (see Local Account Auth above).
Multiple accounts not working
- run
multi_gmail_accounts - verify the alias exists
- re-authenticate missing accounts:
MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp" multi-gmail-mcp-auth --alias work
Labels missing in Gmail
Run:
multi_gmail_setup_labels
If that still fails, re-authenticate so the token includes gmail.modify.
Setup state unclear
Run:
multi_gmail_diagnostics
Security Notes
credentials.jsoncontains your Google OAuth client secretaccounts/contains Gmail refresh/access tokens- never commit either of those paths
.gitignorealready excludes them- token files are written with mode
0600
This repository may also write:
- local reminder data
- local markdown inbox review exports
Treat those as sensitive personal data.
License
MIT
Company
Developed by NITSAN Technologies
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.