github taylorwilsdon/google_workspace_mcp v1.23.0
v1.23.0 - Gateway Auth, Tool Enhancements, Apps Script Overhaul

5 hours ago

Release v1.23.0

Adds a trusted-gateway auth mode for deployments behind a reverse proxy, plus fixes for tools that were returning the wrong thing: trashed files in Drive search, empty structure for tabbed Docs, missing metadata on ranged calendar events, and Apps Script pushes that deleted files you didn't touch.

Per-user identity behind a reverse proxy

The big addition is TRUST_GATEWAY_IDENTITY (#981, #891 - huge thanks @123andy for the original implementation), a new auth mode for anyone running the server behind an MCP-aware proxy like Pomerium, Cloudflare Access, oauth2-proxy, or Traefik ForwardAuth.

Previously, running behind a gateway meant the proxy and the MCP server both wanted to own the OAuth handshake. Now the server can verify the proxy's signed identity assertion (JWT, checked against its JWKS) and use the asserted email as the per-request principal - real per-user isolation without terminating MCP OAuth itself.

The mode is deliberately fail-closed:

  • Assertions are verified cryptographically, with the algorithm pinned to block none/alg-confusion, and aud always checked so a token minted for another route can't be replayed.
  • A missing or invalid assertion rejects the request. There's no fallback to bearer tokens or transport-session identity.
  • Like OAuth 2.1 mode, user_google_email is hidden from tool schemas and auto-filled from the verified principal, so a caller can't act as another account by passing one. A configured USER_GOOGLE_EMAIL default is ignored - the principal always wins.
  • The Google consent flow binds its OAuth state to that principal, and /oauth2callback rejects a callback where the account that actually consented doesn't match.

Setup and the full env var reference live in docs/trusted-gateway-identity.md.

Alongside it, external OAuth metadata now aligns with the MCP mount path (#972 by @sebastianrosch) - protected-resource metadata is derived from where FastMCP actually mounts the endpoint, so discovery and the WWW-Authenticate challenge agree instead of advertising a resource URL that doesn't match.

Tools that return what you asked for

Four fixes in the same spirit - each one a case where a tool's output didn't match a reasonable reading of the request:

  • Drive search no longer surfaces trashed files (#978 by @martiant). search_drive_files now appends trashed=false by default, matching list_drive_items and the Drive web UI. An explicit trashed clause in your own query still wins, quoted literals aren't mistaken for one, and include_trashed=True opts back in.
  • inspect_doc_structure works on tabbed Docs without a tab_id (#976 by @Ofori01). It was reading headers and footers from the first tab but leaving body pointed at the empty top-level document, so structure inspection came back blank. Body and named ranges now come from the same tab.
  • Both get_events paths emit identical metadata (#980 by @wdrwilson). The single-event lookup returned colorId, recurrence, creator, and organizer while the ranged listing didn't. Shared formatting in calendar_helpers now drives both branches, with tests asserting parity - and eventType/status are omitted at their API defaults so ordinary meetings stay compact.
  • read_sheet_values clamps oversized reads (#986 by @SlayerBirden). Open-ended or huge A1 ranges are trimmed to 1000 rows before the Sheets request, bounding memory instead of pulling an entire sheet into the response.

Apps Script updates stop being destructive by default

update_script_content used to replace the project's entire file set with whatever you passed, so pushing one file deleted the rest. It now merges by default (#982 by @syf2211, addressing #923): your files overlay the existing project, keyed by (name, type) since Script API names exclude the extension and one project can hold both Code.gs and Code.html.

Ambiguity is rejected rather than guessed - an update missing type only resolves when exactly one existing file carries that name - and file types and the appsscript manifest name are validated up front. Per-script locks serialize concurrent merges within a process. Pass merge=False for the old full-replacement behavior.

Telemetry and startup polish

  • Opt-in user.email span attribute (#974 by @DrFaust92). Tool-call traces can now be attributed to a person rather than only the opaque enduser.id FastMCP derives from the token. It's PII, so it's strictly off unless the operator sets WORKSPACE_MCP_OTEL_USER_EMAIL, and it no-ops cleanly when OpenTelemetry isn't installed.
  • Reworked startup output. A new core/startup_ui.py centralizes the ANSI palette, section rules, and column alignment that were scattered through main.py, so the startup screen reads as one coherent block. Emphasis uses SGR attributes instead of hardcoded greys, keeping it legible on light, dark, and tinted terminals, and everything renders through a caller-supplied emit so the stdout-stays-clean-for-JSON-RPC rule lives in one place.

New Contributors

Full Changelog: v1.22.2...v1.23.0

Don't miss a new google_workspace_mcp release

NewReleases is sending notifications on new releases.