Skip to main content
Glama
google-workspace-mcp

google-workspace-mcp

by smk-labs

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.

CI License: MIT Plugin version Claude Code plugin Changelog


Why google-workspace-mcp

  • One server, every account. Each tool call passes an account slug (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.json scopes 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.ai connector 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/ (written 0600), never next to code.

  • 48 tools across five services — see the catalog below.

Related MCP server: gmail-multi-mcp-server

Table of contents

Install

/plugin commands 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-mcp

This 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):

  1. Go to the Google Cloud Console and create a project (or pick one).

  2. APIs & Services → Enabled APIs & services → + Enable APIs — enable the Gmail, Google Calendar, Google Drive, Google Tasks, and People (Contacts) APIs.

  3. 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.)

  4. APIs & Services → Credentials → + Create credentials → OAuth client ID → Application type: Desktop app. Download the JSON.

  5. 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_ACCOUNTS can be a comma-list of slugs from your registry, or an inline JSON map ({"work":{"email":"…","name":"…"}}) if you skipped the registry.

  • Omit GWM_ACCOUNTS to 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

.mcp.json GWM_ACCOUNTS

Can use

personal site

personal

personal only

work app

work,support

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

gmail_send

Send immediately — to/cc/bcc, plain or HTML, and correct reply threading via thread_id + in_reply_to_message_id.

gmail_draft_create / gmail_draft_update

Create a draft / overwrite its contents.

gmail_draft_send / gmail_draft_delete

Send a draft / permanently delete one (the thing the default connector can't do).

gmail_drafts_list

List drafts, with Gmail query syntax.

gmail_search

Search messages (from:foo subject:bar), returning sender/subject/date metadata for each result.

gmail_message_get / gmail_thread_get

Fetch one message (with decoded plain-text body) / a whole thread.

gmail_message_modify

Add/remove labels (e.g. mark read by removing UNREAD).

gmail_message_trash

Move to Trash (reversible for 30 days).

gmail_labels_list

List all labels.

gmail_label_create

Create a label (nested via Parent/Child names). Idempotent: an existing label is returned as-is.

gmail_attachment_download

Download one attachment to a local path (attachment_id from a format=full message).

Calendar

Tool

What it does

calendar_list

All calendars the account can access.

calendar_events_list

Events in a time range, with search and recurring-event expansion.

calendar_event_get

Fetch one event.

calendar_event_create

Timed or all-day events — attendees, location, timezone, invitation emails (send_updates), optional Google Meet link.

calendar_event_update

Patch only the fields you pass.

calendar_event_delete

Delete an event.

Drive

Tool

What it does

drive_search

List/search with the Drive query language; shared drives included.

drive_file_get

Full metadata for one file.

drive_file_download

Download any file; exports Google Docs/Sheets/Slides to e.g. PDF or CSV.

drive_file_upload

Upload a local file; optionally convert .docx/.xlsx/.pptx to native Google formats.

drive_file_move / drive_file_rename

Move between folders / rename.

drive_file_trash

Move to trash (reversible).

drive_folder_create

Create a folder.

drive_file_share

Share with someone by email — role from reader to organizer, optional notification message.

drive_file_link_access

Toggle "anyone with the link" access on a file you own, and return a direct_download_url. For server-side fetches by an external API. Revoke immediately after.

Tasks

Tool

What it does

tasklist_list / tasklist_create / tasklist_delete

Manage task lists.

task_list / task_get

List tasks (incl. completed/hidden) / fetch one.

task_create

New task — notes, due date, as a subtask (parent), ordered (previous).

task_update / task_complete

Patch fields / mark completed.

task_move

Reposition: under a parent and/or after a sibling.

task_delete

Delete a task.

Contacts

Read-only by design: this server reads people, it never writes back to Google.

Tool

What it does

contacts_list

List saved Google Contacts with page_token pagination; keeps each phone's canonicalForm (E.164).

contacts_search

Search saved contacts by name / email / phone. Retries once past the People search cold cache.

contact_get

Fetch one contact by its resourceName.

contacts_batch_get

Fetch up to 200 contacts by resourceName in one call. Rehydrates only the ambiguous candidates of a dedup pass.

other_contacts_list

List "other contacts": people you've emailed but never saved. Surfaces real ties missing from the address book.

contact_groups_list / contact_group_get

List contact groups (Google's labels map to CRM tags/circles); fetch a group's member resourceNames to pair with contacts_batch_get.

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

<config-home>/credentials.json

$GWM_CREDENTIALS

Account registry

<config-home>/accounts.json

$GWM_ACCOUNTS_FILE

Per-account tokens

<config-home>/tokens/<slug>.json

$GWM_TOKENS_DIR

Per-instance account scope

—

$GWM_ACCOUNTS

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/calendar

  • https://www.googleapis.com/auth/drive

  • https://www.googleapis.com/auth/tasks

  • https://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.1 redirect during google-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.json or tokens/ (the bundled .gitignore refuses 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 validation

Development

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 manifests

A 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 tools
accounts_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.

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_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_shareC

Share a file with someone by email.

Inputs: email (required), account (required), file_id (required), role, +2 more.

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.

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_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

    C2.6/5.0

    Scored across 48 tools

    Disambiguation4/5

    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.

    Naming Consistency3/5

    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.

    Tool Count2/5

    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.

    Completeness3/5

    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

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      F
      maintenance
      Enables connecting Claude to multiple Gmail accounts at once, allowing each tool call to target a specified mailbox for reading, sending, and managing email.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables 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