gmail-multi-mcp
Allows searching, reading, drafting, sending, and labeling emails across multiple Gmail accounts.
Allows listing events and finding free time across multiple Google Calendars.
Allows searching and accessing files across multiple Google Drive accounts.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@gmail-multi-mcpSearch all my accounts for emails with invoices sent this week."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
gmail-multi-mcp
An MCP server that connects your AI assistant to several Google accounts at once — Gmail, Drive, Calendar and Contacts.
Why this exists
The official connectors handle one account. If your work lives in you@company.com,
your invoices arrive at billing@company.com and your life happens in you@gmail.com, you
end up switching connectors — or simply cannot ask a question that spans two of them.
This server makes the account a first-class parameter, and then goes one step further:
"Any unread invoices this week, and do I have a free hour to deal with them?"
→ search_emails searches work@, billing@ and personal@ in parallel
→ calendar_find_free_time gaps that are free on ALL THREE calendars
→ one answerOmit the account and it searches every mailbox, every Drive, every calendar and every address book you have connected. One Google Cloud project, as many accounts as you want.
Related MCP server: better-workspace-mcp
Features
Multi-account by design. Add as many Google accounts as you like; each keeps its own credentials.
Cross-account by default.
search_emails,drive_search,calendar_list_events,calendar_find_free_timeandcontacts_searchall work across every connected account when you omitaccount.Degrades instead of failing. If one account's token is revoked, the others still return — and the broken one is reported in a
failuresfield rather than silently dropped.Free time across accounts.
calendar_find_free_timeintersects the busy blocks of several calendars. If it cannot read one of them it returns no slots at all rather than a confident answer built on half the picture.Aliases. Call an account
workorpersonalinstead of typing the full address.Standard OAuth, no password ever. Consent happens in your browser, on Google's own screen, through a CSRF-protected loopback callback.
Tokens stay on your machine, in your home directory,
0600on Unix — never in the repository, never in the MCP client configuration.Automatic token refresh, written back to disk so a restart does not re-refresh.
Per-service scope gate. An account authorised before Drive existed keeps doing Gmail and gets a clear
MISSING_SCOPEfor Drive — not a baffling Google 403.Twenty-eight tools across mail, files, calendar and contacts.
Contacts that cannot be clobbered.
contacts_updatere-reads the contact for itsetagbefore writing, so a change made on a phone thirty seconds earlier is not silently overwritten.Cannot permanently delete email.
trash_emailmoves a message to the bin anduntrash_emailbrings it back. There is no permanent-delete tool: it would need a scope this server does not request, and an unrecoverable action does not belong behind an agent.Strict TypeScript, no
any, and a dependency list you can read in one glance.
Prerequisites
Node.js | ≥ 20.12 — see the note below |
Google Cloud | A free account. No billing needed for personal use. |
An MCP client | Claude Code, Claude Desktop, or anything that speaks MCP over stdio. |
Why 20.12 and not 18. The server loads a local
.envwith Node's built-inprocess.loadEnvFile(), which landed in 20.12 (and 21.7). On Node 18 that file would be ignored silently, and you would get a confusing "no credentials" error with a perfectly good.envsitting right there. The floor is real, not cautious.
Installation
git clone https://github.com/blackwings-dev/gmail-multi-mcp.git
cd gmail-multi-mcp
npm install
npm run buildThat produces dist/index.js, which is what your MCP client will run. Check it:
node dist/index.js --versionGoogle Cloud setup
You do this once, no matter how many accounts you connect. About five minutes.
1. Create a project
Open the Google Cloud Console and create a project —
call it whatever you like, e.g. google-mcp.
2. Enable four APIs
With your new project selected, enable each of these. Missing one produces a 403 that says nothing useful, and only for the tools that need it:
API | Direct link |
Gmail API | https://console.cloud.google.com/apis/library/gmail.googleapis.com |
Google Drive API | https://console.cloud.google.com/apis/library/drive.googleapis.com |
Google Calendar API | https://console.cloud.google.com/apis/library/calendar-json.googleapis.com |
People API (contacts) | https://console.cloud.google.com/apis/library/people.googleapis.com |
Contacts live behind the People API, not a "Contacts API". Looking for the latter in the library and finding the long-dead Contacts API v3 is the usual wrong turn.
Press Enable on each. Nothing else on those pages matters.
3. Configure the consent screen
In the sidebar this now lives under Google Auth Platform; older projects show it as APIs & Services → OAuth consent screen.
Field | What to put |
App name | Anything, e.g. |
User support email | Your own address. |
Audience / User type | External |
Developer contact | Your own address again. |
Then, under Audience → Test users, press Add users and add every Google address you intend to connect.
⚠️ This step is not optional. While the app is in Testing, an account that is not listed as a test user cannot grant consent — Google shows an "access blocked" screen that does not explain why.
4. Create the OAuth client
APIs & Services → Credentials → Create Credentials → OAuth client ID
Field | Value |
Application type | Desktop app ← this exact type |
Name | Anything, e.g. |
Copy the Client ID and Client secret from the dialog that appears.
Why "Desktop app" specifically. That client type accepts any loopback port as a redirect URI. The setup flow opens a throwaway server on a random free port and hands that URL to Google, so you never register redirect URIs by hand — which is exactly what lets a single project serve any number of accounts.
Scopes, and what they cost you
This server asks for four scopes:
Scope | What it buys | Google's tier |
| Read, draft, send, label. Not permanent deletion. | Restricted |
| Full access to the user's files. | Restricted |
| Read and write events, and free/busy. | Sensitive |
| Read and write the account’s own contacts. | Sensitive |
Be aware of what full drive costs, because it changes your options later. Google
classes it as a restricted scope, the same tier as gmail.modify. The narrower
drive.file — access limited to files this app created or that the user explicitly picked
in a Google file picker — is not restricted and would keep the verification story
simpler. But drive.file cannot see files you already have, which means drive_search
over your existing Drive would return nothing. That is the whole trade-off: searchable
Drive, or an easier path through Google's review.
Full drive is the deliberate choice here. Switching is a one-line change in
src/auth/oauth.ts (DRIVE_SCOPES) followed by re-authorising your
accounts.
contacts is read and write because contacts_create and contacts_update exist;
contacts.readonly would do if you only ever look. It does not cover Google’s "other
contacts" — the addresses Gmail collects by itself — which sit behind a separate scope this
server does not ask for.
Configuring the server
1. Credentials
cp .env.example .envGOOGLE_CLIENT_ID=1234567890-abcdefg.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-secret-here.env is git-ignored. You can also skip this file entirely — npm run setup will ask for
the credentials and store them in ~/.gmail-multi-mcp/config.json. If both exist, the
environment wins.
2. Connect your accounts
npm run setup gmail-multi-mcp
Gmail, Drive and Calendar. One Google Cloud project, as many accounts as you need.
config C:\Users\you\.gmail-multi-mcp\config.json
tokens C:\Users\you\.gmail-multi-mcp\tokens
✔ OAuth credentials found in the environment.
What now?
─────────
1 Add a Google account
2 List accounts
3 Remove an account
4 Show MCP client configuration
q QuitChoose 1. A browser opens and you sign in. Google’s consent screen lists Gmail, Drive,
Calendar and Contacts; grant them, and the tab says "Account connected". The CLI then asks for an optional alias —
work, personal, billing — which you can use instead of the full address from then on.
⚠️ Adding a second account
Sign out of Google first, or use a private/incognito window.
Otherwise the browser reuses the session you already have, Google skips the account chooser, and you connect the same account twice without noticing.
Repeat for each account, then check the result with option 2.
3. Register the server with Claude Code
claude mcp add gmail-multi-mcp \
--scope user \
-e GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com \
-e GOOGLE_CLIENT_SECRET=GOCSPX-your-secret \
-- node /absolute/path/to/gmail-multi-mcp/dist/index.jsOn Windows the path looks like D:\Dev\gmail-multi-mcp\dist\index.js. Verify with
claude mcp list, then restart Claude Code so it picks the server up.
{
"mcpServers": {
"gmail-multi-mcp": {
"command": "node",
"args": ["/absolute/path/to/gmail-multi-mcp/dist/index.js"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "GOCSPX-your-secret"
}
}
}
}The env block is optional if the credentials are already in
~/.gmail-multi-mcp/config.json. Account tokens are always read from there — they never go
in the client configuration.
Option 4 in the setup CLI prints this snippet with the correct absolute path already filled in.
⚠️ Upgrading from a Gmail-only version? Re-authorise
If you connected your accounts when this server only did Gmail, they will keep working for Gmail and fail for everything else. Drive and Calendar tools will answer:
MISSING_SCOPE — <account> is authorised, but not for Drive.A refresh will not fix this. Google binds a refresh token to the exact set of scopes granted when it was issued, so no amount of restarting, re-refreshing or waiting will add Drive and Calendar. Only a fresh consent will.
The fix takes a minute per account:
npm run setupThe CLI tells you which accounts are affected before you do anything:
! 3 accounts need re-authorising:
· you@company.com
· billing@company.com
· you@gmail.com
They were connected before Drive and Calendar support existed, so they can only
do Gmail right now. A refresh cannot add the missing scopes: Google binds a
refresh token to the scopes it was issued with. Choose 1 and add each account
again — it re-consents and overwrites in place, so nothing has to be removed.Choose 1 and add each account again. You do not have to remove them first: the flow always asks for fresh consent and overwrites the stored token in place, keeping the alias.
Your assistant can check this for itself at any time — list_accounts reports
missingScopes and needsReauthorization per account, and a warning summarising them.
And do not forget step 2 of the Google Cloud setup: an account with the right scopes still fails if the Drive or Calendar API is not enabled on the project.
Usage
Ask in natural language. A few that exercise different corners:
What you say | What happens |
"Any unread invoices this week?" |
|
"Bin that newsletter" |
|
"Find the Q3 budget spreadsheet in any of my Drives" |
|
"Read me that spreadsheet" |
|
"Draft a reply to Ana with the numbers from it" |
|
"Write those notes up as a Google Doc in my work account" |
|
"Share it with marta@client.com as a commenter" |
|
"What's on my calendars tomorrow?" |
|
"Find me two free hours next week across all my accounts" |
|
"Book it as 'Budget review' and invite Marta" |
|
"Move Thursday's stand-up an hour later" |
|
"What's Marta's phone number?" |
|
"Add her to my work contacts with that email" |
|
Habits worth having:
Ask for a draft unless you really want the mail sent.
replyandsend_messagego out immediately and there is no undo.Name the account ("in my work account") when you mean one; leave it out when you want all of them.
calendar_delete_eventanddrive_shareare the two that change things you cannot take back from here. Be explicit with them.
The tools
account accepts an email address or an alias. Where it is optional, omitting it
means "every connected account, merged".
Accounts
Tool | Description | Parameters |
| Configured accounts, their scopes, and which need re-authorising |
|
Gmail
Tool | Description | Parameters |
| Gmail query syntax. Without |
|
| A full conversation, every message in order |
|
| One message: decoded body plus attachment metadata |
|
| Saves a draft without sending |
|
| Replies in-thread — handles |
|
| Composes and sends a new email. No undo. |
|
| Labels of an account, with their ids |
|
| Adds and/or removes labels; accepts names or ids |
|
| Moves a message to the bin. Reversible, not a permanent delete |
|
| Takes a message back out of the bin, restoring its labels |
|
Useful system labels for label_message: UNREAD (remove it to mark as read), STARRED,
IMPORTANT, SPAM.
On the bin. trash_email is the tool for it; adding the TRASH label by hand does the
same thing less clearly. Neither is a permanent delete — but "not permanent" deserves its
footnote: Gmail empties the bin by itself after about 30 days, so a trashed message is
recoverable for a month and then it is gone. search_emails with in:trash shows what is
still in there. There is no permanent-delete tool at all: users.messages.delete needs
the full-mailbox scope this server deliberately does not request.
Attachment metadata is returned (filename, MIME type, size); attachment contents are not downloaded.
Drive
Tool | Description | Parameters |
| Without |
|
| Reads a file as text |
|
| Lists a folder, sub-folders first |
|
| Uploads a file. Exactly one of |
|
| Creates a real Google Doc from plain text |
|
| Grants access. Gives away real data. |
|
drive_read conversions: Google Docs → text/plain, Sheets → text/csv, Slides →
text/plain. Ordinary text files are downloaded as they are. Binary files (PDF, images,
archives) are refused with a link instead — this server does not download them. Content
over max_chars is cut and says so; files over 10 MB are refused.
drive_share types: user and group need email_address; domain needs domain;
anyone makes the file readable by anyone with the link. Roles are reader,
commenter or writer — ownership transfer is deliberately not offered. No notification
email is sent unless notify is true.
Contacts
Contacts are handled through Google’s People API.
Tool | Description | Parameters |
| Without |
|
| One contact in full: every email and phone with its label, organisation, addresses, links, notes |
|
| Pages through an address book, most recently changed first |
|
| Adds a person. At least one field required |
|
| Changes a person. Each named field is replaced, not merged |
|
resource_name looks like people/c1234567890 and comes from contacts_search or
contacts_list.
⚠️ contacts_update replaces whole fields. Sending emails: ["new@x.com"] leaves the
contact with exactly that one address and deletes the rest. Fields you do not name are left
alone — so read the contact first and send back the complete list of whatever you are
editing. The current version is re-read for its etag before writing, so an edit made on a
phone in the meantime cannot be silently overwritten.
The same person in two accounts is returned twice, on purpose. Each copy has its own
resource_name in its own address book; merging them would produce an id that edits the
wrong one.
Paging: contacts_list returns nextPageToken when there is more. Pass it back as
page_token.
Calendar
Tool | Description | Parameters |
| Without |
|
| One event in full, with attendees and their responses |
|
| Creates an event |
|
| Changes an event. |
|
| Deletes an event. Irreversible. |
|
| Gaps free on every account asked about |
|
Times must carry an explicit offset — 2026-09-10T09:00:00+02:00 or a Z. A bare
2026-09-10T09:00:00 is refused, on purpose: it would be read in whatever zone the
server happens to run in, and the resulting answer looks completely normal while being
hours wrong.
All-day events use plain dates (YYYY-MM-DD) with all_day: true. Google treats the
end date as exclusive, so a one-day event ends on the following day.
send_updates defaults to none — attendees are added to the event but not emailed
unless you ask. Values: all, externalOnly, none.
The calendar_find_free_time contract
This is the one tool where a wrong answer looks exactly like a right one, so its rules are worth stating:
time_min/time_maxmust carry an explicit offset. Maximum range: 62 days.Working hours (
workday_start,workday_end, default09:00–18:00) are interpreted intimezone, an IANA name likeEurope/Madrid. It defaults to the zone the server runs in, and is always echoed back in the result so you can see what was assumed. Daylight saving is handled; the offset is resolved per day, not fixed once.weekdays_onlydefaults to true.A busy block that overlaps a candidate window only partially still removes the overlapping part. Half a busy hour is not free.
Returned slots are the free segments of at least
minimum_minutes(default 30), in UTC, not a grid of fixed start times.If any calendar cannot be read, no slots are returned at all. The result carries
incomplete: true, thefailures, and an explanation. A gap computed without one person's calendar is a double-booking, not an answer.
Troubleshooting
MISSING_SCOPE — <account> is authorised, but not for Drive
The account was connected before Drive and Calendar support existed. See Re-authorise above. A refresh cannot fix it; only a fresh consent can.
Access denied by Drive: Request had insufficient authentication scopes
The scope gate let this through, so the stored token claims the scope but Google disagrees — usually a half-finished re-authorisation. Add the account again.
Access denied by Drive / by Calendar / by Contacts (HTTP 403)
The API is not enabled on the Google Cloud project. Enabling one does not enable the others — see step 2. For contacts the one you need is the People API.
contacts_search finds nothing, but the person is definitely there
Google’s contact search reads a server-side index that has to be warmed with an empty query before it answers anything. This server does that on the first search per account and retries once after a short pause, so you should not meet it — but if a brand new search comes back empty, ask again. A second attempt against a warm cache is the difference between "no such person" and "not ready yet".
A contact lost its other email addresses after an update
contacts_update replaces each field it is given. Passing one address removes the
others. Read the contact with contacts_get first and send the full list back.
HTTP ERROR 431 (Request Header Fields Too Large) on the callback
Fixed in current versions — if you see it, you are on an old build. The cause is worth
knowing: the redirect used to go to localhost, which is a shared cookie origin. Every
dev server you have ever run on any localhost port can leave cookies there, and the
browser sends all of them to the OAuth callback. Past roughly 16 KB that exceeds Node's
default header limit — and it fails after Google has already granted consent, so the
authorisation code is lost.
The redirect now targets 127.0.0.1, which does not receive localhost cookies, and the
callback server accepts 64 KB of headers. If it somehow still happens, clear cookies for
127.0.0.1 and retry.
Accounts stop working after 7 days
Your OAuth consent screen is still in Testing, and Google expires refresh tokens for apps in that state after seven days. Two options, neither free:
Stay in Testing and re-run
npm run setuponce a week.Publish the app (Google Auth Platform → Audience → Publish app). Refresh tokens stop expiring. In exchange, an app that has not been through Google's review shows an "unverified app" warning screen on every consent and is capped at a limited number of users.
Two of the four scopes here are restricted (gmail.modify, drive), the category
Google reviews most closely, and that review policy changes. Check the current requirements
on the consent screen itself before assuming publishing will be waved through.
Unknown account "..."
The reference did not match any configured email or alias. Run npm run setup and choose
2, or ask your assistant to call list_accounts.
Google did not return a refresh token
Google issues one only on first consent. Revoke the app at myaccount.google.com/permissions, then authorise again.
time_min must be ISO 8601 with an explicit UTC offset
Working as intended. Send 2026-09-10T09:00:00+02:00, not 2026-09-10T09:00:00.
You connected the same account twice
The browser reused an existing Google session. Remove the duplicate with option 3, then add the other account from a private window.
Your MCP client shows a JSON parse error and disconnects
Something wrote to stdout. On the stdio transport, stdout is the protocol. If you are
modifying this server, every diagnostic must go to stderr — never console.log.
A cross-account search returns fewer results than expected
Look at the failures field of the response. An account whose token was revoked is
reported there rather than silently skipped.
Security
Tokens never leave your machine. They live in
~/.gmail-multi-mcp/tokens/, one file per account,chmod 0600on Unix. Nothing is sent anywhere except Google.Nothing sensitive is in the repository.
.gitignorecovers.envand every.env.*except the example.CSRF-protected callback. A random
stateis generated per flow and compared in constant time; a callback that does not match is rejected.Loopback only. The callback server binds
127.0.0.1, on an ephemeral port, and shuts down as soon as the code arrives.Header injection blocked. CR/LF is stripped from every outgoing mail header, so a crafted subject or recipient cannot inject extra headers.
No permanent mail deletion. The Gmail scope cannot do it and no tool offers it.
About the client secret: for Desktop app clients Google does not treat it as confidential — an installed application cannot keep a secret, which is why this flow is designed not to depend on one. Keep it out of your repository anyway.
Removing an account deletes the local token but does not revoke Google's grant. Revoke it fully at myaccount.google.com/permissions.
⚠️ What you are handing an assistant, and the risk that comes with it
This server can read untrusted text (any email anyone sends you, any document shared with you) and, in the same session, read local files, upload them to Drive, and share them publicly. Those two capabilities together are an exfiltration path if the model acts on instructions it finds inside the content it is reading.
Nothing in this server can prevent that, because from the API's point of view a malicious instruction in an email body and a genuine request from you look identical. What it does instead is make the dangerous steps loud and deliberate:
drive_shareis annotated as destructive, defaults to sending no notification, refuses ownership transfer, and requirestype: "anyone"spelled out to create a public link.drive_uploadwill not read a local file unless given an explicitlocal_path.calendar_delete_eventis annotated as destructive and reads the event first so the answer says what it removed.
Use an MCP client that asks before running write tools, and read what it is about to do. Treat "an email told me to share this file" as the red flag it is.
How it works
MCP client ──stdio──▶ src/index.ts ──▶ src/server.ts 28 tools, zod-validated
│
┌──────────────┬────────────────────┼────────────────────┬──────────────┐
▼ ▼ ▼ ▼
src/gmail/ src/drive/ src/calendar/ src/contacts/
search, MIME queries, exports, events, free/busy People API,
parsing and uploads and and time-zone search warm-up,
building sharing arithmetic etag-guarded writes
└──────────────┴────────────────────┼────────────────────┴──────────────┘
▼
src/core/
AccountClientCache · the SCOPE GATE · error taxonomy
cross-account runner · bounded concurrency
│
▼
src/auth/oauth.ts
loopback consent, automatic refresh
│
▼
src/auth/token-store.ts
~/.gmail-multi-mcp/Path | Contents |
| OAuth client (if not in the environment) and the account list |
| One refresh token per account, with the scopes it was granted |
The scope gate lives in one place — AccountClientCache in
src/core/google-client.ts. Every Drive and Calendar call
has to go through a client, and every client comes from that class, so an account missing a
scope is refused by construction rather than by each of twenty-eight handlers remembering to
ask. A check spread across handlers has a blind spot the moment someone adds one more.
Auto-refresh is handled by Google's auth client: an expired access token is renewed
transparently, and a tokens listener writes the new one back to disk — preserving the
refresh token, which Google omits from refresh responses.
Development
npm install
npm run build # tsc → dist/
npm run typecheck # no emit
npm run dev # run the server from source with tsx
npm run setup # run the setup CLI from sourceSmoke-test the server without an MCP client:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| node dist/index.jsLayout
src/
index.ts entry point: server by default, `setup` for the CLI
server.ts MCP server and all twenty-eight tool definitions
core/
errors.ts the error taxonomy and Google's HTTP failures mapped onto it
google-client.ts per-account client cache AND the scope gate
accounts.ts run one operation across every account, failures as values
concurrency.ts bounded parallelism
auth/
oauth.ts scopes, consent flow, loopback callback, automatic refresh
token-store.ts config.json and per-account token files
gmail/
client.ts authenticated clients, MIME parsing and building
search.ts single- and multi-account search
types.ts Gmail domain types
drive/
client.ts queries, exports, uploads, sharing
types.ts Drive domain types
calendar/
client.ts events and free/busy arithmetic
timezone.ts wall-clock time in a named zone, and interval maths
types.ts Calendar domain types
contacts/
client.ts People API: search warm-up, etag-guarded writes
types.ts Contacts domain types
cli/
setup.ts interactive setup (chalk)
assets/ the project icon: PNG and SVG, with and without wordmarkThe SVG in assets/ is the master. Its geometry is also inlined in
src/auth/icon.ts as the favicon of the local OAuth callback page — the only web
page this server ever serves.
TypeScript runs with strict plus noUncheckedIndexedAccess, noImplicitReturns,
noUnusedLocals and verbatimModuleSyntax. There is no any in the source.
A note on the name
The project is still called gmail-multi-mcp, and that name is now too small for it.
Gmail is one of three services. google-workspace-mcp or google-multi-mcp would describe
it better.
The rename is deliberately not done yet, because it is wider than it looks. Its surface includes:
the package name, the binary name and the repository;
~/.gmail-multi-mcp/— the config and token directory, which means existing users would have to move their credentials or the server would look freshly installed;GmailMcpError, the error class every layer throws, including Drive and Calendar;SERVER_NAME, and the literal string"gmail-multi-mcp setup"in a dozen user-facing error messages.
Worth doing in one deliberate pass, with a migration for the token directory — not piecemeal.
Contributing
Issues and pull requests are welcome.
Fork the repository and create a branch:
git checkout -b feature/what-it-does.Make your change.
npm run typecheckandnpm run buildmust both pass.Smoke-test the server as shown above.
Open a pull request describing what changed, why, and how you verified it.
Please keep the five rules that hold the design together:
Nothing writes to stdout outside
src/cli/and the--help/--versionpaths. Stdout is the MCP transport.No
any. Google's types are noisy; convert them at the boundary in each service'sclient.tsand keep the rest of the codebase on the domain types.New service? Go through
AccountClientCache. That is where the scope gate lives. A client built any other way skips it.Cross-account operations report their failures. Never return a shorter list and stay quiet about the account that did not answer.
Nothing unrecoverable.
trash_emailis reversible; a permanent-delete counterpart is not, and does not belong behind an agent. The Gmail scope this server requests cannot do it, and that is the point — do not widen the scope to add one.
Good first contributions: attachment download, Google Docs formatting via the Docs API,
recurring-event rules, contact groups and labels, Google’s "other contacts", mark_read /
archive convenience tools, or shared-drive support.
Reporting a security issue
Please do not open a public issue. Contact the maintainers privately first.
License
MIT — do what you like, no warranty.
This server cannot be deployed
Maintenance
Related MCP Connectors
Multiple Google accounts (Gmail, Calendar, Drive, Contacts, Tasks) in one Claude connector.
Multiple Gmail accounts, editable Google Sheets & Docs for AI agents. Deny-by-default access rules.
Permissioned access to Gmail, Drive and Calendar via the user's own Google account
- alfred_OAuthai.get-alfred
Your real Gmail, Outlook and calendars, worked as you: read, draft, send, schedule, organize.
Related MCP Servers
- FlicenseAqualityBmaintenanceConnects AI assistants to multiple Gmail accounts simultaneously, enabling search, read, draft, send, and reply operations with per-account permission controls.54-
- AlicenseNot gradedqualityAmaintenanceGoogle Workspace MCP server enabling AI assistants to manage Docs, Drive, Calendar, Gmail, Sheets, Slides, Tasks, Chat, and People across multiple accounts.1201Apache 2.0
- FlicenseNot gradedqualityBmaintenanceEnables AI agents to access multiple Gmail/Google Workspace accounts for searching and reading mail, calendars, and attachments.-
- AlicenseNot gradedqualityCmaintenanceConnects Gmail to AI assistants via the MCP protocol, enabling search, read, send, and manage emails across multiple Google accounts simultaneously.MIT