google-workspace-mcp
Provides tools to send and manage Gmail messages and drafts, search mail, read messages and threads, modify labels, trash messages, and download attachments across configured Google accounts.
Provides tools to list calendars, search and fetch events, and create, update, or delete timed and all-day events with attendees, locations, time zones, recurrence, and optional Google Meet links across configured Google accounts.
Provides tools to search and inspect Drive files, download or export files, upload and convert files, move, rename, trash files, create folders, and manage sharing and link access across configured Google accounts.
Provides access to Google Tasks as one of the server's supported Google Workspace services, enabling task management across configured Google 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., "@google-workspace-mcp Find the invoice in my work Drive and email it from my personal account"
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.
google-workspace-mcp
One MCP server, all your Google accounts.
A multi-account Google Workspace MCP server for Claude Code — Gmail, Calendar, Drive, Tasks, and Contacts across any number of Google accounts in parallel. Every tool takes an account slug, so a single Claude session can email from one account, file in another's Drive, and book an event in a third. Ships as a Claude Code plugin that is also its own marketplace.
Why google-workspace-mcp
One server, every account. Each tool call passes an
accountslug (e.g.work,personal). The server loads and refreshes that account's OAuth token transparently — no switching, no separate servers.Per-project access control. Accounts are configured at runtime, never baked into code. Each project's
.mcp.jsonscopes it to a subset, so a personal project never even sees your work account.Your own OAuth client. You bring a (free) Google Cloud OAuth client, so you own the access and get the full tool surface — including things the default
claude.aiconnector can't do, like deleting a draft. Nothing is routed through anyone else's infrastructure.Secrets stay out of the tree. The OAuth client and per-account refresh tokens live under
~/.config/google-workspace-mcp/(written0600), never next to code.48 tools across five services — see the catalog below.
Related MCP server: gmail-multi-mcp-server
Table of contents
Install
/plugincommands run inside Claude Code — type them at the prompt.
# 1) Register the marketplace (once per machine)
/plugin marketplace add smk-labs/google-workspace-mcp
# 2) Install the plugin
/plugin install google-workspace-mcp@google-workspace-mcpThis gives you the /google-workspace-setup command, which walks you through everything in Setup — checking prerequisites, creating the OAuth client, defining accounts, authorizing each one in the right browser, and wiring the server into your project.
Some environments don't expose the /plugin slash command. Add the block below to .claude/settings.json at user level (~/.claude/settings.json, applies everywhere) or project level (committed, so teammates get it on trust). The runtime reconciles enabledPlugins at startup and fetches the marketplace automatically:
{
"extraKnownMarketplaces": {
"google-workspace-mcp": {
"source": { "source": "github", "repo": "smk-labs/google-workspace-mcp" }
}
},
"enabledPlugins": {
"google-workspace-mcp@google-workspace-mcp": true
}
}Setup
Requires uv on your PATH. The /google-workspace-setup command automates these steps; here is the manual path.
1. Create your own Google Cloud OAuth client
Each user creates their own client (free):
Go to the Google Cloud Console and create a project (or pick one).
APIs & Services → Enabled APIs & services → + Enable APIs — enable the Gmail, Google Calendar, Google Drive, Google Tasks, and People (Contacts) APIs.
APIs & Services → OAuth consent screen — User type External. Fill the required fields. Under Test users, add every Google account you intend to connect. (Leaving the app in Testing is fine.)
APIs & Services → Credentials → + Create credentials → OAuth client ID → Application type: Desktop app. Download the JSON.
Save that JSON as:
~/.config/google-workspace-mcp/credentials.json
2. Install the server binary
So a project's .mcp.json can launch it by name:
# from GitHub:
uv tool install git+https://github.com/smk-labs/google-workspace-mcp
# …or from a local clone:
uv tool install .This installs two commands: google-workspace-mcp (the server) and google-workspace-authorize (the OAuth helper).
3. Define your accounts
Give each account a short slug. Two ways:
a) A registry file (recommended) — ~/.config/google-workspace-mcp/accounts.json (template: accounts.example.json):
{
"accounts": {
"work": { "email": "you@company.com", "name": "Your Name" },
"personal": { "email": "you@gmail.com", "name": "Your Name" }
}
}name is the display name shown in the From: header on sent mail (set it to "" to send with the bare address).
b) Or inline per project via the GWM_ACCOUNTS env var (see step 5) — handy if you don't want a shared registry file.
4. Authorize each account
This prints a Google consent URL — open it in a browser signed into that account and click Allow:
# account already in accounts.json — just the slug:
google-workspace-authorize work
# or register a brand-new account inline (adds it to accounts.json):
google-workspace-authorize personal you@gmail.com "Your Name"You'll click through the "Google hasn't verified this app" warning (it's your own app) → Continue → Select all → Continue. The refresh token is written to ~/.config/google-workspace-mcp/tokens/<slug>.json.
5. Wire it into a project
Add a .mcp.json at the project root. GWM_ACCOUNTS decides which accounts this project may use:
{
"mcpServers": {
"google-workspace": {
"command": "google-workspace-mcp",
"env": { "GWM_ACCOUNTS": "work,personal" }
}
}
}GWM_ACCOUNTScan be a comma-list of slugs from your registry, or an inline JSON map ({"work":{"email":"…","name":"…"}}) if you skipped the registry.Omit
GWM_ACCOUNTSto expose every account in the registry.
Restart Claude Code (or reconnect MCP) to pick it up, then ask Claude to list accounts to confirm.
Per-project access control
The point of GWM_ACCOUNTS is isolation: a project literally cannot act as an account that isn't in its list — the server refuses the slug. Tokens are stored centrally (authorize once), but each project's .mcp.json chooses its own slice:
Project |
| Can use |
personal site |
| personal only |
work app |
| work + support |
Tools
Every call requires an account slug. accounts_list shows the configured accounts and which have a token file on disk (authorized: true); it does not validate the token with Google.
Gmail
Tool | What it does |
| Send immediately — |
| Create a draft / overwrite its contents. |
| Send a draft / permanently delete one (the thing the default connector can't do). |
| List drafts, with Gmail query syntax. |
| Search messages ( |
| Fetch one message (with decoded plain-text body) / a whole thread. |
| Add/remove labels (e.g. mark read by removing |
| Move to Trash (reversible for 30 days). |
| List all labels. |
| Create a label (nested via |
| Download one attachment to a local path ( |
Calendar
Tool | What it does |
| All calendars the account can access. |
| Events in a time range, with search and recurring-event expansion. |
| Fetch one event. |
| Timed or all-day events — attendees, location, timezone, invitation emails ( |
| Patch only the fields you pass. |
| Delete an event. |
Drive
Tool | What it does |
| List/search with the Drive query language; shared drives included. |
| Full metadata for one file. |
| Download any file; exports Google Docs/Sheets/Slides to e.g. PDF or CSV. |
| Upload a local file; optionally convert |
| Move between folders / rename. |
| Move to trash (reversible). |
| Create a folder. |
| Share with someone by email — role from |
| Toggle "anyone with the link" access on a file you own, and return a |
Tasks
Tool | What it does |
| Manage task lists. |
| List tasks (incl. completed/hidden) / fetch one. |
| New task — notes, due date, as a subtask ( |
| Patch fields / mark completed. |
| Reposition: under a parent and/or after a sibling. |
| Delete a task. |
Contacts
Read-only by design: this server reads people, it never writes back to Google.
Tool | What it does |
| List saved Google Contacts with |
| Search saved contacts by name / email / phone. Retries once past the People search cold cache. |
| Fetch one contact by its |
| Fetch up to 200 contacts by |
| List "other contacts": people you've emailed but never saved. Surfaces real ties missing from the address book. |
| List contact groups (Google's labels map to CRM tags/circles); fetch a group's member |
Configuration & storage
Everything lives under the config home — $GWM_HOME, else $XDG_CONFIG_HOME/google-workspace-mcp, else ~/.config/google-workspace-mcp/:
What | Path | Override |
OAuth client secret |
|
|
Account registry |
|
|
Per-account tokens |
|
|
Per-instance account scope | — |
|
Token files are written 0600.
Scopes
Broad on purpose — these are your own accounts; narrower scopes would force a re-auth every time a tool is added:
https://mail.google.com/https://www.googleapis.com/auth/calendarhttps://www.googleapis.com/auth/drivehttps://www.googleapis.com/auth/taskshttps://www.googleapis.com/auth/contacts.readonly(read-only)https://www.googleapis.com/auth/contacts.other.readonly(read-only)
Adding a scope (as v0.3.0 did for Tasks, and v0.7.0 for Contacts) requires re-running
google-workspace-authorize <slug>for each account.Since v0.7.1 this is no longer urgent: an account that has not re-authorized keeps working on the scopes it already holds, and only the tools needing a new scope report what is missing.
Trust & security
Local only. The server speaks stdio to Claude Code and talks only to Google's APIs. The one listener it ever opens is a temporary
127.0.0.1redirect duringgoogle-workspace-authorize, which closes as soon as consent lands.Your client, your tokens. The OAuth client and tokens stay on your machine, outside this repo. Never commit
credentials.jsonortokens/(the bundled.gitignorerefuses both).Nothing shared between users. Each teammate runs their own OAuth client and authorizes their own accounts.
Treat the config dir as a secret store. Tokens grant broad access to your mail/calendar/drive/tasks —
~/.config/google-workspace-mcp/deserves the same care as~/.ssh/.An MCP server that can send email and share files deserves review before you enable it — the whole surface is ~1,300 lines of Python in
src/google_workspace_mcp/. See SECURITY.md to report a vulnerability.
Repository layout
google-workspace-mcp/
├── .claude-plugin/
│ ├── plugin.json # the plugin manifest
│ └── marketplace.json # single-repo marketplace (source: "./")
├── commands/
│ └── google-workspace-setup.md # /google-workspace-setup — guided setup
├── src/google_workspace_mcp/
│ ├── server.py # the MCP server — all 48 tools
│ ├── auth.py # token load/refresh + Google service builders
│ ├── accounts.py # runtime account registry + GWM_ACCOUNTS scoping
│ └── authorize.py # standalone OAuth consent flow (CLI)
├── accounts.example.json # template for ~/.config/google-workspace-mcp/accounts.json
├── pyproject.toml # uv/hatchling package — the two console scripts
├── tests/ # unit tests (pure functions — no auth, no network)
└── .github/workflows/ci.yml # lint, tests, import smoke test, manifest validationDevelopment
git clone https://github.com/smk-labs/google-workspace-mcp
cd google-workspace-mcp
uv sync # create .venv with locked deps
claude --plugin-dir . # load the plugin without installing
# Quality gates (CI runs the same — see .github/workflows/ci.yml)
uvx ruff check . # lint
uv run python -m py_compile src/google_workspace_mcp/*.py
uv run python -c "from google_workspace_mcp import server" # import smoke test
uv run --group dev pytest # unit tests
claude plugin validate . --strict # validate plugin + marketplace manifestsA user-facing change bumps version in .claude-plugin/plugin.json, both version fields in .claude-plugin/marketplace.json, pyproject.toml, and src/google_workspace_mcp/__init__.py, and adds a CHANGELOG.md entry — installed projects pick it up on /plugin marketplace update.
Contributing
Contributions are welcome. The repo is public, generic, and English-only — no personal emails, real account slugs, home paths, or secrets. See CONTRIBUTING.md for the development setup and checks, and CODE_OF_CONDUCT.md for community expectations.
License
MIT © Ali Rajool.
Available Tools
48 toolsaccounts_listA
List configured accounts and whether each has a token file on disk.
Inputs: no named parameters.
calendar_event_createB
Create an event. start/end are RFC3339 datetimes; date-only ('2026-06-10') makes an all-day event.
Inputs: end (required), start (required), account (required), summary (required), +7 more.
calendar_event_deleteC
Delete an event.
Inputs: account (required), event_id (required), calendar_id, send_updates.
calendar_event_getC
Fetch one event.
Inputs: account (required), event_id (required), calendar_id.
calendar_events_listC
List events. time_min/time_max are RFC3339 (e.g. '2026-06-05T00:00:00-07:00').
Inputs: account (required), query, time_max, time_min, +3 more.
calendar_event_updateB
Patch an existing event — only the fields you pass are changed.
Inputs: account (required), event_id (required), end, start, +7 more.
calendar_listC
List all calendars the account can access.
Inputs: account (required).
contact_getC
Fetch one contact by its resourceName (e.g. 'people/c12345...').
Inputs: account (required), resource_name (required).
contact_group_getB
Fetch one contact group with its member resourceNames. Pair the returned
members with contacts_batch_get to pull a whole segment in two calls.
Inputs: account (required), resource_name (required), max_members.
contact_groups_listC
List the account's contact groups — Google's native labels/segments (e.g. 'Family', 'Work'). These map cleanly to CRM tags/circles.
Inputs: account (required), page_size.
contacts_batch_getA
Fetch many contacts at once by resourceName (up to 200 per call) — one
round trip instead of N contact_get calls. Built for a dedup residue
step, where only the ambiguous merge candidates need re-hydrating.
Inputs: account (required), resource_names (required).
contacts_listB
List saved contacts. Pass next_page_token back as page_token to
walk every page.
Inputs: account (required), page_size, page_token.
contacts_searchB
Search saved contacts by name/email/phone. page_size max is 30
(People API limit). The People search index is cold on the first query
after startup; this retries once automatically if the first result is
empty, so the caller never has to.
Inputs: query (required), account (required), page_size.
drive_file_downloadB
Download a file. For Google Docs/Sheets/Slides, pass export_mime_type
(e.g. 'application/pdf', 'text/plain', 'text/csv').
Inputs: account (required), file_id (required), save_to (required), export_mime_type.
drive_file_getC
Get a file's full metadata.
Inputs: account (required), file_id (required).
drive_file_link_accessA
Toggle "anyone with the link" access on a file the account owns.
Built for zero-bandwidth server-side fetches: some APIs (e.g. a
transcription service's source_url parameter) can download a Drive file
themselves — but only while it is link-accessible. Flow: enable, hand the
returned direct_download_url to the fetching service, then IMMEDIATELY
call again with enabled=false to revoke. Never leave link access on.
Inputs: account (required), enabled (required), file_id (required), role.
drive_file_moveC
Move a file to a new folder.
Inputs: account (required), file_id (required), new_parent_id (required), remove_old_parents.
drive_file_renameD
Rename a file.
Inputs: account (required), file_id (required), new_name (required).
drive_file_trashC
Move a file to trash (reversible).
Inputs: account (required), file_id (required).
drive_file_uploadB
Upload a local file to Drive. convert_to_google_doc=True converts
.docx/.xlsx/.pptx to native Google Docs/Sheets/Slides.
Inputs: account (required), local_path (required), name, mime_type, +2 more.
drive_folder_createC
Create a new folder.
Inputs: name (required), account (required), parent_folder_id.
drive_searchB
List/search files. query uses Drive query language; see
https://developers.google.com/drive/api/guides/search-files
Examples:
"name contains 'budget'"
"mimeType='application/vnd.google-apps.folder'"
"'' in parents"
Inputs: account (required), query, order_by, max_results, +1 more.
gmail_attachment_downloadA
Download one attachment to a local path. Get attachment_id from
the message payload parts (gmail_message_get with format=full).
Inputs: account (required), save_to (required), message_id (required), attachment_id (required).
gmail_draft_createC
Create a Gmail draft. Returns {id, message: {...}}.
Inputs: to (required), body (required), account (required), subject (required), +4 more.
gmail_draft_deleteC
Permanently delete a draft (the thing the default connector can't do).
Inputs: account (required), draft_id (required).
gmail_draft_sendC
Send an existing draft.
Inputs: account (required), draft_id (required).
gmail_drafts_listC
List drafts. query uses standard Gmail search syntax.
Inputs: account (required), query, max_results.
gmail_draft_updateC
Overwrite an existing draft's contents.
Inputs: to (required), body (required), account (required), subject (required), +4 more.
gmail_label_createA
Create a label. Nested labels use 'Parent/Child' names; create each parent level first. Idempotent: an existing label is returned as-is.
Inputs: name (required), account (required), label_list_visibility, message_list_visibility.
gmail_labels_listC
List all labels for the account.
Inputs: account (required).
gmail_message_getC
Fetch one message. format=full includes the body.
Inputs: account (required), message_id (required), format.
gmail_message_modifyC
Add/remove labels on a message (e.g. mark read by removing UNREAD).
Inputs: account (required), message_id (required), add_label_ids, remove_label_ids.
gmail_message_trashB
Move a message to Trash (reversible for 30 days).
Inputs: account (required), message_id (required).
gmail_searchB
Search messages with Gmail's query syntax (e.g. 'from:foo subject:bar').
Inputs: query (required), account (required), label_ids, max_results, +1 more.
gmail_sendA
Send an email immediately from the given account.
For replies, pass thread_id AND in_reply_to_message_id (the RFC822
Message-Id header value, NOT the Gmail message id) so the reply
threads correctly.
Inputs: to (required), body (required), account (required), subject (required), +5 more.
gmail_thread_getC
Fetch a whole thread (all messages).
Inputs: account (required), thread_id (required).
other_contacts_listA
List 'other contacts' — people you've corresponded with (e.g. emailed) but never saved to Google Contacts. High-value for a CRM: it surfaces real ties missing from the address book. Paginated like contacts_list.
Inputs: account (required), page_size, page_token.
task_completeB
Mark a task completed (shortcut for status='completed').
Inputs: account (required), task_id (required), tasklist_id.
task_createA
Create a task. due is RFC3339 (e.g. '2026-06-15T00:00:00Z') — Google Tasks
keeps only the DATE part. parent makes it a subtask of that task id;
previous orders it after that task id.
Inputs: title (required), account (required), due, notes, +3 more.
task_deleteC
Delete a task. Irreversible.
Inputs: account (required), task_id (required), tasklist_id.
task_getC
Fetch a single task.
Inputs: account (required), task_id (required), tasklist_id.
task_listB
List tasks in a list. Find ids via tasklist_list; '@default' is the account's default list.
Inputs: account (required), max_results, show_hidden, tasklist_id, +1 more.
tasklist_createC
Create a new task list.
Inputs: title (required), account (required).
tasklist_deleteB
Delete a task list and all its tasks. Irreversible.
Inputs: account (required), tasklist_id (required).
tasklist_listA
List the account's task lists (each has an id + title).
Inputs: account (required).
task_moveB
Reposition a task: under parent (as a subtask) and/or after previous in the same list.
Inputs: account (required), task_id (required), parent, previous, +1 more.
task_updateA
Patch a task's fields. status='completed' completes it (or use task_complete).
Inputs: account (required), task_id (required), due, notes, +3 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 48 tools
Most tools are clearly separated by service and action, and the descriptions clarify overlaps like gmail_send vs gmail_draft_send or drive_file_get vs drive_search. The main confusion points are task_list vs tasklist_list and singular/plural contact tools, but these are still distinguishable with the descriptions.
All names are snake_case and generally readable, but conventions vary between service_action (gmail_send, drive_search, calendar_list) and service_resource_action (gmail_message_get, drive_file_get, calendar_event_update). Singular/plural inconsistencies (contacts_list vs contact_get, gmail_labels_list vs gmail_label_create) and the near-identical task_list/tasklist_list reduce predictability.
48 tools is very heavy for a single MCP server, even one spanning five Google Workspace services. While many tools are useful, the count is near the extreme end and includes convenience/niche tools such as task_complete, contacts_batch_get, and drive_file_link_access.
Gmail, Calendar, Drive, and Tasks have solid lifecycle coverage for common workflows, including create/update/delete and search/list operations. However, Contacts is essentially read-only with no create/update/delete, and there are notable secondary gaps such as Gmail label deletion/update and Calendar creation/deletion.
Maintenance
Related MCP Connectors
Multiple Google accounts (Gmail, Calendar, Drive, Contacts, Tasks) in one Claude connector.
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.
Gmail, Outlook, Drive, OneDrive and calendars for AI agents. Many accounts, one endpoint, audit log.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables Claude Code to manage Gmail, Google Calendar, and Google Drive via a dedicated Google account, keeping personal data separate.21921 PyPIMIT
- AlicenseNot gradedqualityFmaintenanceEnables connecting Claude to multiple Gmail accounts at once, allowing each tool call to target a specified mailbox for reading, sending, and managing email.MIT
- AlicenseNot gradedqualityBmaintenanceEnables Claude to access and manage multiple Google accounts at once across Gmail, Calendar, Drive, Contacts, and Tasks, with unified search and document text extraction.MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude and Gemini to access and manage multiple Google accounts simultaneously across services such as Gmail, Drive, Calendar, YouTube, Sheets, Analytics, AdSense, and Fitness, with create, read, update, and delete operations. It runs locally without external intermediaries to keep account access private.MIT