v2.3.0
Substantial internal refactor to introduce multi-account support: one running server can now hold OAuth credentials for several Google accounts (e.g. personal + Workspace) and route each tool call to the right identity. The change is strictly additive at the contract level — existing single-account users upgrade with no re-consent, no config changes, and no user-visible behavior change.
Features
- sheets: add
ONE_OF_RANGEtoaddDataValidationcondition types, enabling dropdowns sourced from a cell range (Data validation → "Dropdown (from a range)"). Takes exactly one value — the source range in A1 notation; a leading=is added automatically if omitted. Lets dropdown option lists be maintained in one place (including a separate master spreadsheet via anIMPORTRANGEstaging range) - calendar: surface event
attachmentsingetCalendarEvent/getCalendarEventsresponses, and accept anattachmentsarray (max 25) increateCalendarEvent/updateCalendarEvent(sets thesupportsAttachmentsAPI flag).updateCalendarEventnow also preserves an event's existing attachments instead of silently dropping them whenattachmentsis not supplied (#110) - auth: support Workspace domain-wide delegation via
GOOGLE_DRIVE_MCP_SUBJECT;GOOGLE_DRIVE_MCP_SCOPESis now honored in service-account mode (#107) - auth:
authGetStatusnow verifies the effective Google identity the live Drive client is acting as (via Driveabout.get) and reports the active auth mode (oauth/service_account/external_token) plus which override env vars are set. It warns whenGOOGLE_APPLICATION_CREDENTIALSorGOOGLE_DRIVE_MCP_ACCESS_TOKENin the environment cause a presenttokens.jsonto be silently ignored (the same warning is now logged at startup). This makes "validauth/drivetoken yet every call returns empty" misconfigurations diagnosable instead of invisible (#137) - resources: add
GOOGLE_DRIVE_MCP_DISABLE_RESOURCESenv var (and--no-resourcesflag) to opt out of the MCP resource protocol (gdrive:///listing/reading); tools stay available. For tools-only clients or clients that hang enumerating a large Drive. The env var and--no-resources[=<bool>]accept1/0,true/false,yes/no,on/off;--no-resources=falsere-enables resources, overriding a truthy env value (#115, #128) - drive/docs: add
readTextFileand extendinsertText/deleteRangeto edit rawtext/*files (e.g.text/plain,text/markdown,text/csv) in place, alongside Google Docs. Text files are addressed by a 0-based Unicode code-point (character) offset, so edits and truncation stay safe for content containing emoji/astral characters; deleting a file's entire content correctly empties it; edits keep working under thecontent-editor(drive.file) scope; andtext/*acceptance is consistent acrossreadTextFile/insertText/deleteRange/updateTextFile(Google Docs indexes remain 1-based) (#133, #141) - sheets: add column/row dimension tools —
setColumnWidth,setRowHeight,autoResizeColumns,autoResizeRows, andhideSheetDimension/showSheetDimension— for resizing, auto-fitting, and hiding/showing columns and rows. Indices are 0-based and the interval is half-open[start, end). Ranges are validated client-side (start < end, andpixelSize >= 0for width/height), so reversed or empty ranges are rejected up front with a clear message instead of an opaque Google API400(#134) - auth: add
manage_accountstool withlist,add,remove, andset_defaultactions for managing multiple connected Google accounts in a single process (local OAuth mode only; service-account and external-token modes remain single-identity). - auth: add optional
accountparameter to every non-admin tool so a single call can be routed to a specific connected Google account. Resolution order is: explicitaccount→ global default → sole-eligible account. Write tools refuse ambiguous resolution and point atmanage_accounts set_default. - auth: connect identity discovery —
manage_accounts addrequestsopenidanduserinfo.emailscopes so new accounts record their Google stablesuband email at consent time. Existing accounts are left untouched (no forced re-consent). - auth: atomic-rename writes for
tokens.jsonplus a process-wide write queue that serializes concurrent refreshes from different accounts. - auth: per-alias refresh dedupe — N concurrent tool calls on the same account fire at most one refresh request to Google.
Bug Fixes
- drive/docs: widen Shared Drive coverage on the remaining endpoints that omitted it —
getDocumentInfonow passessupportsAllDrives, andlistFolder,listGoogleDocs,listGoogleSheets,bulkConvertFolderPdfs,authTestFileAccess, and thegdrive:///resource listing now passcorpora=allDrives(alongside the flags they already set) so Shared Drive items surface consistently, matchingsearch(#137) - drive:
addPermissionno longer forcesemailAddressfortype: "anyone"andtype: "domain"— the Drive API rejected the field for those principals, making both unusable. Added adomainparameter so domain-wide grants work, and per-type requirements are now validated up front (emailAddressforuser/group,domainfordomain, neither foranyone). Also added an optionalallowFileDiscoveryflag foranyone/domaingrants —false(default) keeps a file link-only,truemakes it discoverable in search (#131) - resources: raise the
resources/listpage size from 10 to 1000 (Drive API max) so clients that eagerly enumerate the entire Drive (e.g. Gemini CLI) no longer hang during initialization (#111, #128) - docs: honor
tabIdininsertTable,editTableCell,insertSmartChip,createFootnote,applyTextStyle/formatGoogleDocText,applyParagraphStyle/formatGoogleDocParagraph, andcreateParagraphBullets— these previously ignoredtabIdand silently edited the default tab of multi-tab documents while reporting success (#114, #126) - auth: use loopback IP
127.0.0.1instead oflocalhostfor the OAuth callback redirect URI, matching the IPv4-only callback-server bind so the redirect resolves to the bound address on dual-stack hosts (#124). Desktop-app OAuth clients (the recommended type) are unaffected; "Web application" clients that registered ahttp://localhost:<port>redirect must re-register it ashttp://127.0.0.1:<port>or auth fails withredirect_uri_mismatch— see README Troubleshooting
Performance Improvements
- docs:
applyParagraphStylewithtextToFind+tabIdnow resolves the matched range and its enclosing paragraph from a singledocuments.get, instead of two full unmaskedincludeTabsContentfetches of the same document. The non-tab path is unchanged (#114, #127)
Token file format
tokens.json now uses a versioned v2 schema that keys multiple accounts by alias. The upgrade from v1 is automatic on first boot: the v2 file is written in place, the previous file is preserved as tokens.json.v1-backup-<timestamp> in case you need to roll back, and the migrated credentials are registered under the alias default (which is reserved — you cannot re-create it with manage_accounts add). The record is initially marked pendingIdentity: true; its email and Google sub populate the next time you re-consent.
Note: downgrading to 2.2.x or earlier after running 2.3.0+ requires manually restoring the .v1-backup-* file in place of the v2 tokens.json. The new file is not readable by older versions.
Reserved aliases
default, all, *, stdio, service-account, external-token, test cannot be used with manage_accounts add.
Known Limitations
- Cross-account read fanout (
account: string[]) is planned but not yet shipped. - The Streamable HTTP transport shares one active-default account across all sessions on the same process; per-session isolation is a planned follow-up.
manage_accounts removedeletes local credentials but does not yet revoke the refresh token server-side — revoke manually via Google Account Permissions if needed.
Full changelog: v2.2.0...v2.3.0