Skip to content

v2.3.0

Choose a tag to compare

@piotr-agier piotr-agier released this 11 Jul 21:52

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_RANGE to addDataValidation condition 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 an IMPORTRANGE staging range)
  • calendar: surface event attachments in getCalendarEvent/getCalendarEvents responses, and accept an attachments array (max 25) in createCalendarEvent/updateCalendarEvent (sets the supportsAttachments API flag). updateCalendarEvent now also preserves an event's existing attachments instead of silently dropping them when attachments is not supplied (#110)
  • auth: support Workspace domain-wide delegation via GOOGLE_DRIVE_MCP_SUBJECT; GOOGLE_DRIVE_MCP_SCOPES is now honored in service-account mode (#107)
  • auth: authGetStatus now verifies the effective Google identity the live Drive client is acting as (via Drive about.get) and reports the active auth mode (oauth/service_account/external_token) plus which override env vars are set. It warns when GOOGLE_APPLICATION_CREDENTIALS or GOOGLE_DRIVE_MCP_ACCESS_TOKEN in the environment cause a present tokens.json to be silently ignored (the same warning is now logged at startup). This makes "valid auth/drive token yet every call returns empty" misconfigurations diagnosable instead of invisible (#137)
  • resources: add GOOGLE_DRIVE_MCP_DISABLE_RESOURCES env var (and --no-resources flag) 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>] accept 1/0, true/false, yes/no, on/off; --no-resources=false re-enables resources, overriding a truthy env value (#115, #128)
  • drive/docs: add readTextFile and extend insertText/deleteRange to edit raw text/* 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 the content-editor (drive.file) scope; and text/* acceptance is consistent across readTextFile/insertText/deleteRange/updateTextFile (Google Docs indexes remain 1-based) (#133, #141)
  • sheets: add column/row dimension tools — setColumnWidth, setRowHeight, autoResizeColumns, autoResizeRows, and hideSheetDimension/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, and pixelSize >= 0 for width/height), so reversed or empty ranges are rejected up front with a clear message instead of an opaque Google API 400 (#134)
  • auth: add manage_accounts tool with list, add, remove, and set_default actions 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 account parameter to every non-admin tool so a single call can be routed to a specific connected Google account. Resolution order is: explicit account → global default → sole-eligible account. Write tools refuse ambiguous resolution and point at manage_accounts set_default.
  • auth: connect identity discovery — manage_accounts add requests openid and userinfo.email scopes so new accounts record their Google stable sub and email at consent time. Existing accounts are left untouched (no forced re-consent).
  • auth: atomic-rename writes for tokens.json plus 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 — getDocumentInfo now passes supportsAllDrives, and listFolder, listGoogleDocs, listGoogleSheets, bulkConvertFolderPdfs, authTestFileAccess, and the gdrive:/// resource listing now pass corpora=allDrives (alongside the flags they already set) so Shared Drive items surface consistently, matching search (#137)
  • drive: addPermission no longer forces emailAddress for type: "anyone" and type: "domain" — the Drive API rejected the field for those principals, making both unusable. Added a domain parameter so domain-wide grants work, and per-type requirements are now validated up front (emailAddress for user/group, domain for domain, neither for anyone). Also added an optional allowFileDiscovery flag for anyone/domain grants — false (default) keeps a file link-only, true makes it discoverable in search (#131)
  • resources: raise the resources/list page 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 tabId in insertTable, editTableCell, insertSmartChip, createFootnote, applyTextStyle/formatGoogleDocText, applyParagraphStyle/formatGoogleDocParagraph, and createParagraphBullets — these previously ignored tabId and silently edited the default tab of multi-tab documents while reporting success (#114, #126)
  • auth: use loopback IP 127.0.0.1 instead of localhost for 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 a http://localhost:<port> redirect must re-register it as http://127.0.0.1:<port> or auth fails with redirect_uri_mismatch — see README Troubleshooting

Performance Improvements

  • docs: applyParagraphStyle with textToFind + tabId now resolves the matched range and its enclosing paragraph from a single documents.get, instead of two full unmasked includeTabsContent fetches 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 remove deletes 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