Google Workspace

io.github.mindstonePublished by GitHub account mindstonev0.5.3Updated Nov 4, 2026

Google Workspace MCP server for Gmail, Calendar, Drive, Docs, Sheets, Slides, Contacts, and more

Overview

AI-generated overview

Local MCP server that gives an assistant 124 tools across Gmail, Calendar, Drive, Docs, Sheets, Slides, Contacts, Chat, Meet, Tasks, Forms and Comments in…

What you can do
  • Search, read, send, reply to and draft Gmail messages, manage labels, filters, attachments and the vacation responder
  • Look up free slots, list and create, update, respond to or delete calendar events
  • List, search, upload, download, copy, move, trash files and folders, manage permissions, revisions and shared drives
  • Read, create, edit, find-and-replace and manage tabs in Docs; read and write Sheets ranges and formatting; read, create and update Slides
  • List, search, create and update contacts; list Chat spaces and messages and send text; read Meet records and transcripts
  • Manage Drive comments, task lists and tasks, read Forms and responses, and list, authenticate or remove workspace accounts
When to use it
Useful when an assistant should work directly with a user's Google Workspace data — triaging and replying to email, scheduling meetings, editing documents and spreadsheets, or archiving and resetting recurring Docs tabs — from a desktop MCP host that already handles Google sign-in.
Requirements
Runs locally via npx (Node.js 20+ recommended) over stdio. Requires a host application that performs the Google OAuth flow and writes accounts.json plus per-account token files. Required environment variables: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, ACCOUNTS_PATH, CREDENTIALS_PATH. Optional: GOOGLE_WORKSPACE_REQUEST_TIMEOUT_MS, MCP_WORKSPACE_PATH. The README notes the npm package is not yet published.
Before you install
Needs a Google OAuth client secret (GOOGLE_CLIENT_SECRET) and read access to on-disk OAuth token files, so treat the credentials directory as sensitive. Many tools write, send or delete: email sending and replies, calendar event deletion, file trash and permission changes, document and spreadsheet edits, tab deletion (which removes sub-tabs), and comment deletion. Sending is not always confirmed, so check whether a message went out before retrying. File reads and writes are limited to…

Installation

In SourceWeft

  1. Open Google Workspace in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

@mindstone/mcp-server-google-workspace

[License: FSL-1.1-MIT]

Google Workspace MCP server — Gmail, Calendar, Drive, Docs, Sheets, Slides, Contacts, Chat, Meet, Tasks, Forms, Comments, and account diagnostics.

Multi-account Google Workspace MCP. Host-orchestrated OAuth (the connector neither runs a callback server nor mints OAuth URLs), atomic per-account credential writes, and a structured auth_required handoff so the host drives the sign-in flow rather than the server.

Status

  • Version: 0.5.3 · npm: not yet published
  • Auth: OAuth (host-orchestrated) (GOOGLE_CLIENT_SECRET)
  • Tools: 124 (Gmail, Calendar, Drive, Docs, Sheets, Slides, Contacts, Chat, Meet, Tasks, Forms, Comments, Account)
  • Surface: cloud-api
  • Machine-readable: STATUS.json

This connector is based on aaronsb/google-workspace-mcp and credited under package.json#attribution.

Why this exists

When we ported this in early 2026, Google had not published a first-party MCP server for the Workspace surface. The community options at the time each got parts of the job right — Aaron Brown's aaronsb/google-workspace-mcp, which we used as the starting point, covered the breadth of Workspace APIs but ran its own browser-callback server during OAuth, which our host application already does. We forked it so that the MCP host can own the entire OAuth flow (the connector returns a structured auth_required response instead of running a callback server or computing OAuth URLs), per-account token files are written atomically with restrictive permissions and symlink rejection, and the connector goes through our own security review before each release. Google's Workspace MCP has since shipped; we continue to maintain this one because it integrates with our host's credential-file layout, host-orchestrated OAuth handoff, and recovery-guidance contract. This is now a hard fork maintained independently in this repository — we do not sync changes from the upstream project.

Example interaction

"Find the email thread Alice sent yesterday about Q3 planning and reply with 'thanks, will read tonight'."

Tools the host calls:

  1. search_workspace_emails — searches Gmail for from:alice@... subject:"Q3 planning" newer_than:1d.
  2. reply_to_workspace_email — replies on the returned thread ID with the supplied body.

Response (trimmed):

json
{  "thread": {    "id": "1928a...",    "subject": "Q3 planning",    "from": "[email protected]"  },  "reply": {    "id": "1928b...",    "labelIds": ["SENT"]  }}

Requirements

  • Node.js 20+ (engines.node is >=18, but the build and tests are exercised on 20 and 22)
  • npm
  • A host application that performs the Google OAuth flow and writes accounts.json plus per-account token files for the connector to read.

Quick Start

Install & build

bash
cd <path-to-repo>/connectors/google-workspacenpm installnpm run build

npx (once published)

bash
npx -y @mindstone/mcp-server-google-workspace

Local

bash
node dist/index.js

Configuration

This server is designed to run alongside a host application that performs the Google OAuth flow on its own. The host writes credentials to disk; this server reads them.

Required environment variables

VariableDescription
GOOGLE_CLIENT_IDGoogle OAuth client ID.
GOOGLE_CLIENT_SECRETGoogle OAuth client secret.
ACCOUNTS_PATHPath to accounts.json (host-written index of authenticated accounts).
CREDENTIALS_PATHDirectory containing per-account Google OAuth token files.

Optional environment variables

VariableDefaultDescription
GOOGLE_WORKSPACE_REQUEST_TIMEOUT_MS60000Outbound Google API request timeout in milliseconds (max 300000 = 5 min).

GOOGLE_WORKSPACE_DISABLE_REFRESH=1 may be injected by a host to make the connector return auth_required instead of refreshing tokens. It is intentionally not a user-facing setup variable.

Authentication flow

authenticate_workspace_account returns a structured auth_required response:

json
{  "status": "auth_required",  "user_action": { "id": "google.connect_account" },  "agent_action": {    "instruction": "Connect Google Workspace to continue. The user will be redirected to Google's sign-in."  },  "setupToolName": "authenticate_workspace_account"}

The connector does not run a callback server and does not generate OAuth URLs. The MCP host computes the Google OAuth URL and handles the callback. Once the host has written the account's token file under CREDENTIALS_PATH/ and an entry into ACCOUNTS_PATH, the next tool call succeeds.

Host configuration examples

Claude Desktop / Cursor

json
{  "mcpServers": {    "GoogleWorkspace": {      "command": "npx",      "args": ["-y", "@mindstone/mcp-server-google-workspace"],      "env": {        "GOOGLE_CLIENT_ID": "your-google-oauth-client-id",        "GOOGLE_CLIENT_SECRET": "your-google-oauth-client-secret",        "ACCOUNTS_PATH": "/absolute/path/to/accounts.json",        "CREDENTIALS_PATH": "/absolute/path/to/credentials"      }    }  }}

Until the host has written ${ACCOUNTS_PATH} and the matching per-account token file under ${CREDENTIALS_PATH}/, every tool call returns the structured auth_required response (see the Authentication flow above).

Local development (no npm publish needed)

json
{  "mcpServers": {    "GoogleWorkspace": {      "command": "node",      "args": ["<path-to-repo>/connectors/google-workspace/dist/index.js"],      "env": {        "GOOGLE_CLIENT_ID": "your-google-oauth-client-id",        "GOOGLE_CLIENT_SECRET": "your-google-oauth-client-secret",        "ACCOUNTS_PATH": "/absolute/path/to/accounts.json",        "CREDENTIALS_PATH": "/absolute/path/to/credentials"      }    }  }}

Tools (124)

The full list lives under src/tools/definitions/ and is also surfaced in tools-inventory.json. Grouped by domain:

DomainToolsNotes
Gmail23Email search/thread/send/compose, drafts, labels, label filters, attachments, archive/trash/read-state helpers, settings, vacation responder, and send-as aliases/signatures.
Calendar9Current time, free-slot lookup, calendar/event listing, event creation, updates, responses, and deletion.
Drive15List/search/upload/download/copy/move/trash/untrash files, folders, permissions, revisions, shared-drive discovery, and activity queries (what changed, when, by whom).
Docs13Read, create, append, replace, find/replace (whole doc or one tab), insert text at an anchor, tab tree, add/rename/move/delete tabs, copy a tab's content into another tab, and batch updates. Read, append, replace and find/replace can target one tab (tab_id); reads can also cover every tab (all_tabs).
Sheets17Read/write ranges, create spreadsheets, sheet management (add, duplicate, rename, reorder, delete), insert rows or columns formatted like their neighbours, batch operations, find/replace, and formatting.
Slides7Read, create, list/get slides, batch update, thumbnails, and ID extraction.
Labels12Gmail label CRUD and filter rules.
Contacts4List, search, create, and update contacts.
Chat3List spaces, list messages in a space, and send text messages.
Meet3List conference records, list transcripts, and read transcript entries (speaker + text).
Comments5List/create/reply/resolve/delete Drive comments.
Account3List, authenticate, and remove workspace accounts.
Tasks6List task lists, and list/create/update/complete/delete tasks.
Forms4Read-only access to forms and their responses.

Compose send status

In hosts that remember send status, sending an email closes its other compose copies. Reloaded cards show "Email sent" with the send time and hide Reopen because they do not have the sent contents. The copy used to send keeps its posted contents in a read-only sent view, including when confirmation follows a lost reply. After an error or block reply, a later sent status from another copy shows only the sent bar, without this copy's contents. While a send is in progress, other copies show “Sending…” with Send, Retry, Cancel and Save draft (where available) disabled. Replies to this copy’s own requests keep those controls locked until the other send ends. If the send ends without a confirmed outcome, those copies return to editing and keep any earlier uncertainty. A copy waiting for its own Send reply keeps its sending view. Sent views stay read-only, and uncertainty remains until a send is confirmed. After an error or missing confirmation, check whether the email was sent before using Retry; the notice stays visible while editing and returns on Reopen. Collapsing the card does not resolve that uncertainty. Policy-block explanations stay visible while editing with Send disabled. Cancel says "Nothing sent" only when no send may have gone through, including a not-dispatched failure before the request left the device.

When sending is blocked by your settings, your organization or security policy, the card hides Retry and keeps Cancel. If the server is stopped, Retry remains available.

Send requests declare _meta: { 'mcp-app/cardOutcome': 'sent' } to supporting hosts. When your own setting blocks sending, Retry stays unavailable; the Gmail fallback depends on the structured user-disabled reason. Draft saves do not declare a send outcome; Cancel after a draft-save timeout says "Nothing sent" unless an earlier send remains uncertain.

Sheets read limits

read_workspace_spreadsheet_values accepts max_rows and max_cols to keep the first rows and columns of a values read, including value_view: "shaped" and return_json: true. They refer to sheet rows and columns even with major_dimension: "COLUMNS". Omitted limits leave that dimension unchanged. A truncated result reports truncated: true in JSON or a truncation notice in text.

Anchor envelopes keep their first/last windows, summary and continuation token unchanged. Limits apply when reading a continuation page, but truncation creates no new token and does not advance the original token. To retrieve omitted cells, re-read an explicit range or repeat the token read with larger limits (or without limits).

Shared drives

All Drive file, permission, and comment tools work on shared-drive files (the files.* and permissions.* calls pass supportsAllDrives; the Comments API addresses files by ID and defines no such flag). Listing and search default to the user's own corpus plus files shared with them, so shared-drive content the user has access to but has never opened may not surface by default. To work with shared drives explicitly:

  • Discover drives: list_shared_drives returns the shared drives the account can access (id, name, creation time).
  • Target one drive: pass options.driveId to list_drive_files / search_drive_files. The connector forces corpora: 'drive' whenever driveId is set (the Drive API rejects any other pairing), overriding a caller-supplied corpora.
  • Search everything: pass options.corpora: 'allDrives' (no driveId) to search across My Drive and all shared drives in one query. Google recommends this only for targeted searches; prefer a specific driveId for browsing.

Known Drive API limitations for shared-drive files (not fixable in this connector):

  • Files native to a shared drive have no owners[] — ownership belongs to the drive itself. Use lastModifyingUser and createdTime as proxies when attributing files.
  • The Revisions API does not support shared-drive files, so list_file_revisions / download_file_revision only work for My Drive files.

Docs tabs

A Google Doc can hold many tabs, nested up to a few levels deep. Every tab has a tab id such as t.abc123; list_workspace_document_tabs shows the whole tree with each tab's id, parent and position. A Docs link containing ?tab=t.xxxx points at one tab, and t.xxxx is its tab id.

Without tab_id:

  • read_workspace_document returns the first tab and, on a document with several tabs, adds a line saying how many there are. Pass tab_id to read one tab, or all_tabs: true to read every tab under ## Tab: headings with one shared max_chars budget (tabs that don't fit are listed by id).
  • append_to_workspace_document, replace_workspace_document and insert_workspace_document_text act on the first tab. A tool call never inherits the tab an earlier call used, so pass tab_id on every call that should act on another tab.
  • find_and_replace_workspace_document replaces in every tab. Pass tab_id to limit it to one tab (its sub-tabs are separate tabs and are not included).

tab_id: "default" means the first top-level tab for reads, edits, the anchored insert, parent_tab_id when adding a tab, and the copy source. Tools that delete, rename, move or overwrite a tab need a real tab id and refuse "default".

Managing tabs: add_workspace_document_tab, update_workspace_document_tab (rename, reorder, or nest under another tab; the API can't move a sub-tab back to the top level) and delete_workspace_document_tab.

  • Tab titles must be unique across the whole document, sub-tabs included; a clash is refused before anything changes.
  • position is 0-based among the tab's siblings (0 = first) and means where the tab ends up after the move. The numbers in list outputs start at 1. Results report the position the document actually shows.
  • Deleting a tab deletes all its sub-tabs with it. A tab that has sub-tabs is refused unless delete_sub_tabs: true is passed.

Inserting text at an anchor: insert_workspace_document_text adds plain text to one tab without rewriting it. anchor_text must appear exactly once in the tab, inside one paragraph. position is after (default) or before the anchor, or next_paragraph_start: the start of the paragraph after the anchor's, which is how to fill an empty bullet under a heading. Anchor on the line directly above that slot; the paragraph there must be empty unless expect_empty_paragraph: false is passed. Newlines start new paragraphs that keep the paragraph's heading or bullet formatting; the inserted text itself is plain (no bold or links picked up from its neighbours).

Copying a tab: copy_workspace_document_tab copies one tab's content into another tab of the same document.

  • Copied: text, heading and paragraph styles, character styles and links, bullets and numbering (mapped to Google Docs' preset list styles), tables (cell text, column widths, cell styles), page breaks and smart chips. A smart chip linking to a file you can't open becomes a titled link; people and date chips become their text. The result lists both.
  • Not copied: headers and footers, sub-tabs, images and other special elements (listed under "Not copied"; inline ones also leave a visible [placeholder]), and table cell merges (cells are copied unmerged; a table whose merged cells can't be copied is refused before anything is written).
  • Not copied faithfully: links to headings inside the copied tab still point at the original tab's headings, so after a reset they no longer resolve.
  • Which target: to archive or duplicate a tab, use new_tab_title. To reset a tab in place from a template tab (keeping its id, position and sub-tabs), use target_tab_id with overwrite_existing: true, which clears that tab first. Never delete a tab in order to replace it: deleting also deletes its sub-tabs.
  • The result starts with a Verification: line comparing text, heading types, paragraph formatting, bullets, list grouping and table shapes with the source. Before deleting or resetting the source, check both that it says passed and that the Not copied line lists nothing you can't lose: verification can pass while, say, a floating image was left out. A failed verification comes back as an error result.
  • The copy is not atomic. If it stops part-way, the connector tries to delete a new tab again and the result says whether that worked (if not, it names the tab to delete); an existing target may be left cleared or partly written (the document's version history can restore it). If it completes but verification finds differences, the tab is kept so it can be checked, and the result lists the first few places it differs. Long tabs can take 10-60 seconds.

Example: a weekly archive-and-reset of a fictional "Acme weekly sync" doc that has a live tab and a Template tab. The business rules (the next meeting date, which lines to keep) are the caller's:

  1. list_workspace_document_tabs to get the live and Template tab ids, then read the live tab with tab_id: <live tab id>. Note the date line and the out-of-office lines to keep before anything changes.
  2. copy_workspace_document_tab with source_tab_id: <live tab id>, new_tab_title: "2031-10-13", new_tab_parent_id: <live tab id> and new_tab_position: 0, archiving it as its first sub-tab.
  3. Stop unless the result says Verification: passed and its Not copied line lists nothing the archive can't do without.
  4. copy_workspace_document_tab with source_tab_id: <Template tab id>, target_tab_id: <live tab id> and overwrite_existing: true, resetting the live tab in place.
  5. find_and_replace_workspace_document the date placeholder with tab_id: <live tab id> (without it the Template's placeholder changes too), and check the result says Replaced 1.
  6. insert_workspace_document_text the kept out-of-office lines with tab_id: <live tab id> (without it the text goes into the first tab, which may be the Template) and position: "next_paragraph_start", anchored on the line directly above the empty bullet.

Vacation responder

update_workspace_vacation_responder merges with the existing settings (the Gmail API otherwise replaces the whole resource): omitted subject/body are kept, an existing HTML body stays HTML, and a pending scheduled end is preserved when end_time is omitted. To remove a scheduled end and make the auto-reply open-ended, pass clear_end_time: true (mutually exclusive with end_time). An already-past end is never carried into a re-enable.

Security notes

  • Token and account writes use temp-file plus rename, restrictive permissions, fsync, and symlink rejection.
  • Token refresh can be disabled by the host so a single authority owns refresh-token rotation (GOOGLE_WORKSPACE_DISABLE_REFRESH=1).
  • User-recoverable failures return host-neutral recovery guidance with action_required and next_step fields.
  • The bundled callback server was removed; OAuth URLs and callback handling stay with the MCP host.
  • Attachment filenames are sanitised to prevent path traversal via crafted upload_workspace_attachment filename arguments.
  • <untrusted-content> envelopes cover Contacts, Calendar, Comments, Forms, Tasks, and JSON-return paths.

Licence

FSL-1.1-MIT — Functional Source License, Version 1.1, with MIT future licence. The software converts to MIT licence on 2030-04-08.

Source: connectors/google-workspace/README.md at commit 5510a4b

Tools

0
SourceWeft started this package once in an isolated sandbox, without credentials, to list its tools. That failed: the server did not start.

Version history

6
  1. v0.5.3LatestNov 4, 2026
  2. v0.5.2Nov 3, 2026
  3. v0.5.1Nov 1, 2026
  4. v0.5.0Oct 15, 2026
  5. v0.4.2Sep 29, 2026
  6. v0.4.1Sep 16, 2026

Score

40% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

    Declare a license in the repository or the registry entry.