@nitsantechnologies/multi-gmail-mcp
v1.0.0
Published
Multi-account Gmail MCP server for Claude Desktop, Cursor, and other MCP hosts
Maintainers
Readme
@nitsantechnologies/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 --versionInstall
npm install -g @nitsantechnologies/multi-gmail-mcp
mkdir -p ~/.multi-gmail-mcpSet 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-authLocal 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 authGoogle 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.jsonSet 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-authAdd another account with an alias:
MULTI_GMAIL_MCP_HOME="$HOME/.multi-gmail-mcp" multi-gmail-mcp-auth --alias workNotes:
- 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.jsonExample:
{
"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 [email protected] 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 SmithDaily 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 toStep 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-upsWhat 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 [email protected] personal
Connect [email protected] workmulti_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 protected]>(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": "[email protected]",
"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": "[email protected]",
"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 protected]>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 protected]>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": "<[email protected]>"
}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-authPermission 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 workLabels missing in Gmail
Run:
multi_gmail_setup_labelsIf that still fails, re-authenticate so the token includes gmail.modify.
Setup state unclear
Run:
multi_gmail_diagnosticsSecurity 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
