
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
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.
Installation
In SourceWeft
- Open Google Workspace in the dashboard and add it to a workspace.
- 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
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:
search_workspace_emails— searches Gmail forfrom:alice@... subject:"Q3 planning" newer_than:1d.reply_to_workspace_email— replies on the returned thread ID with the supplied body.
Response (trimmed):
Requirements
- Node.js 20+ (
engines.nodeis>=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.jsonplus per-account token files for the connector to read.
Quick Start
Install & build
npx (once published)
Local
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
Optional environment variables
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:
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
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)
Tools (124)
The full list lives under src/tools/definitions/ and is also surfaced in tools-inventory.json. Grouped by domain:
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_drivesreturns the shared drives the account can access (id, name, creation time). - Target one drive: pass
options.driveIdtolist_drive_files/search_drive_files. The connector forcescorpora: 'drive'wheneverdriveIdis set (the Drive API rejects any other pairing), overriding a caller-suppliedcorpora. - Search everything: pass
options.corpora: 'allDrives'(nodriveId) to search across My Drive and all shared drives in one query. Google recommends this only for targeted searches; prefer a specificdriveIdfor 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. UselastModifyingUserandcreatedTimeas proxies when attributing files. - The Revisions API does not support shared-drive files, so
list_file_revisions/download_file_revisiononly 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_documentreturns the first tab and, on a document with several tabs, adds a line saying how many there are. Passtab_idto read one tab, orall_tabs: trueto read every tab under## Tab:headings with one sharedmax_charsbudget (tabs that don't fit are listed by id).append_to_workspace_document,replace_workspace_documentandinsert_workspace_document_textact on the first tab. A tool call never inherits the tab an earlier call used, so passtab_idon every call that should act on another tab.find_and_replace_workspace_documentreplaces in every tab. Passtab_idto 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.
positionis 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: trueis 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), usetarget_tab_idwithoverwrite_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 sayspassedand that theNot copiedline 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:
list_workspace_document_tabsto get the live and Template tab ids, then read the live tab withtab_id: <live tab id>. Note the date line and the out-of-office lines to keep before anything changes.copy_workspace_document_tabwithsource_tab_id: <live tab id>,new_tab_title: "2031-10-13",new_tab_parent_id: <live tab id>andnew_tab_position: 0, archiving it as its first sub-tab.- Stop unless the result says
Verification: passedand itsNot copiedline lists nothing the archive can't do without. copy_workspace_document_tabwithsource_tab_id: <Template tab id>,target_tab_id: <live tab id>andoverwrite_existing: true, resetting the live tab in place.find_and_replace_workspace_documentthe date placeholder withtab_id: <live tab id>(without it the Template's placeholder changes too), and check the result saysReplaced 1.insert_workspace_document_textthe kept out-of-office lines withtab_id: <live tab id>(without it the text goes into the first tab, which may be the Template) andposition: "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_requiredandnext_stepfields. - 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_attachmentfilename 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
0Version history
6- v0.5.3LatestNov 4, 2026
- v0.5.2Nov 3, 2026
- v0.5.1Nov 1, 2026
- v0.5.0Oct 15, 2026
- v0.4.2Sep 29, 2026
- 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.
