Releases: taylorwilsdon/google_workspace_mcp
Release list
v1.26.0
Release v1.26.0
Feature release. Calendar finally paginates and can carry attachments, Chat can edit a message it already sent, Gmail labels take colors, Docs gains tab-scoped reads and anchors that survive an index shift, Sheets can manage its tabs, and deployments can cap how much any download is allowed to buffer into memory. Special thank you to @DrFaust92 who didn't get a credit here but did the leg work on one of these.
What's Changed
- auth/OAuth: the OAuth 2.1 protocol path now falls back to the client secrets file instead of insisting on
GOOGLE_OAUTH_CLIENT_ID/GOOGLE_OAUTH_CLIENT_SECRET, so a deployment configured only withGOOGLE_CLIENT_SECRET_PATHno longer dies at startup onOAuth 2.1 requires GOOGLE_OAUTH_CLIENT_SECRET; env vars still win per value, and a path that is set but missing or malformed raises a clear error instead of falling through by @gnomed in #1076 - gmail:
manage_gmail_labeltakes optionalbackground_colorandtext_colorhex strings oncreateandupdate, validated up front against Gmail's fixed palette so a bad value names the offending field rather than coming back as a bare 400 (#351) by @ConnorMoss02 in #1083 - core: new
WORKSPACE_MCP_MAX_FILE_BYTESbounds how many bytes Drive, Gmail, Chat, and Docs downloads may buffer into process memory; unset or0keeps today's uncapped behavior, and an oversized download returns a Drive link and alternative tools instead of OOMing the server by @SlayerBirden in #996 - gchat:
send_messageaccepts an optionalmessage_nameand patches that message in place instead of only ever creating, so a typo, a stale link, or a post pitched at the wrong room can be reworded; left unset, the create path is byte for byte unchanged and the tool count stays the same by @TidyMaze in #1092 - gcalendar:
get_eventsandlist_calendarsare paginated.gcalendarwas the last package still droppingnextPageToken, so the 25-event default returned 25 events with nothing to indicate a 26th existed; callers who pass neither parameter send the same request as before (#1089) by @ConnorMoss02 in #1090 - gdocs:
inspect_doc_structurereports paragraph layout stats in both modes: empty-paragraph counts, up to 100 paragraph ranges with a truncation flag, and the last paragraph's list and empty status, with list membership and object anchors in detailed output. Object anchors do not count as empty, and ranges can cover protected newlines, so the docs spell out the cleanup constraints by @kryan007 in #1080 - gdocs/gsheets/gcalendar/gslides/auth: the document-index and delegation batch, closing #1084, #1052, #1085 and #1086 by @taylorwilsdon in #1098
- gdocs:
get_doc_content,get_doc_as_markdownandinspect_doc_structuretake atab_id, so the read tools can finally target the tab the write tools already accepted - gdocs:
inspect_doc_structuretakespreview_chars(0orNonefor untruncated text), so the index workflow its own docstring describes can actually be completed past the old 100-character cap - gdocs: insert operations accept
after_heading,before_heading, oranchor_textplusanchor_positionin place of a rawindex, resolved against the document at execution time, which removes the class of bug where a shifted index lands an edit on unrelated content without failing - gsheets: new
manage_sheet_tabrenames, deletes, hides, unhides, and reorders tabs within a spreadsheet - gcalendar:
create_eventandmodify_eventtakeattachmentsas Drive file IDs or URLs; onmodify_eventthe list replaces the event's existing attachments rather than appending to them - gslides:
get_presentationandget_pagetakeinclude_geometryto report each element's placement - auth: service-account mode substitutes the full Drive scope for
drive.file, which under domain-wide delegation can never reach a pre-existing file because a DWD service account never passes through the Drive picker; authorize it in the Admin console DWD scope list - gmail:
send_gmail_messagestates plainly that Gmail's REST API has no send-time parameter, so an agent stops telling users a message was scheduled when it was sent immediately
- gdocs:
New Contributors
- @gnomed made their first contribution in #1076
- @ConnorMoss02 made their first contribution in #1083
- @TidyMaze made their first contribution in #1092
- @kryan007 made their first contribution in #1080
Full Changelog: v1.25.2...v1.26.0
A very gracious thank you to those who have sponsored,
@rajivpant + @123andy being a solo maintainer in open source can be a thankless job and your thank you's mean the world!
v1.25.2
Release v1.25.2
Fixes Gmail reply threading and label handling, adds recurring-event series support to Calendar, hardens Drive's inline base64 uploads with integrity checks and zip-bomb protection, and improves Word document text extraction fidelity.
What's Changed
- gappsscript:
list_deploymentsnow surfaces the boundversionNumberand reads descriptions fromdeploymentConfig, so deployment state is verifiable from the API (#922) by @WIZARD221 in #1063 - gdrive: inline base64 uploads are validated before hitting the Drive API, with SHA-256 integrity checks, a 100 MB payload ceiling, and zip-bomb / malformed-container rejection for Office and ODF files by @taylorwilsdon in #1066
- gmail: replies and reply drafts now target the latest non-draft, non-trash message in a thread by default, fixing cases where replies landed on an older message or an existing draft by @floriangaldo in #1065
- gmail: the
add_labels/remove_labelsschema is corrected so MCP clients render a proper array input instead of a broken text field by @Tethys0 in #1058 - gcalendar:
get_eventsgains asingle_eventsparameter, letting callers view recurring events as a collapsed series or as individual occurrences, with correct handling of exceptions, cancellations, and events that started in the past by @taylorwilsdon in #1067 - gdrive: Word document text extraction now joins runs without spurious spaces, separates paragraphs with newlines, and reads headers, footers, footnotes, and endnotes by @taylorwilsdon in #1071
- gmail:
list_gmail_labelsaddsprefix,compact, andinclude_systemparameters for efficient label-tree filtering (a 355-label account's 72 KB response drops to under 1 KB with a prefix filter) by @erikholz in #1041 - auth: OAuthProxy token-expiry thresholds are now configurable via
WORKSPACE_MCP_OAUTH_PROXY_TOKEN_EXPIRY_THRESHOLD_SECONDSandWORKSPACE_MCP_OAUTH_PROXY_ACCESS_TOKEN_EXPIRY_SECONDS, reducinginvalid_granterrors from refresh-token rotation races by @DrFaust92 in #1061 - auth: external OAuth token validation now runs off the event loop via
asyncio.to_thread, preventing a slow Google userinfo call from blocking the entire server by @pavel-at-stacks in #1046
New Contributors
- @Tethys0 made their first contribution in #1058
- @pavel-at-stacks made their first contribution in #1046
Full Changelog: v1.25.1...v1.25.2
v1.25.1
Release v1.25.1
Bug-fix release improving Gmail accuracy, Calendar timezone handling, Drive shortcut safety, and log hygiene across all services.
What's Changed
- feat(calendar): report and author event boundaries in their own timezones - start and end times can now carry different IANA zones, and unresolvable or empty timezone values are rejected early instead of being forwarded to Google (#1044) by @seidnerj in #1044
- fix(gmail): stop reporting batch label changes that Gmail never made -
batch_modify_labelsnow returns honest results instead of echoing back the requested changes when the API silently no-ops (#1047) by @123andy in #1047 - fix(logging): keep user free-text out of INFO/ERROR logs - queries, find/replace text, subjects, titles, file names, and request URIs are scrubbed from INFO and ERROR entries across all seven search tools, import/create paths, and the Gmail attach path; DEBUG remains reachable for local troubleshooting (#1037) by @123andy in #1037
- fix(drive): preserve shortcuts for metadata updates -
update_file_metadatanow targets the shortcut itself rather than resolving through to the underlying file, so renaming or moving a shortcut no longer silently renames the target (#1051) by @lukaszliniewicz in #1051 - fix(gmail): use default Send-As identity for drafts -
create_draftnow picks the account's configured default Send-As address instead of leaving theFromheader unset, which caused drafts to appear with a bare email rather than the user's display name (#1054) by @floriangaldo in #1054 - fix(gmail): include Reply-To in message and thread header output -
get_messageandget_threadnow surface theReply-Toheader so agents can reply to the address the sender actually intended (#1042) by @mickey-mikey in #1042
New Contributors
- @floriangaldo made their first contribution in #1054
Full Changelog: v1.25.0...v1.25.1
v1.25.0
Release v1.25.0
Large Drive downloads no longer buffer in RAM, stored attachments stop surviving forever across restarts, calendar event times carry an unambiguous weekday, and tools now accept camelCase argument names from clients that mirror Google's API field names.
What's Changed
- enh(gdrive/gmail/core): stream Drive downloads to a temp file in 8 MB chunks and adopt them into attachment storage with the new
save_attachment_from_path()(ashutil.move, so it stays memory-safe across filesystems) instead of buffering the whole payload. A multi-gigabyte download now costs one chunk of RAM rather than a full copy (#994). Stored files also keep the full UUID in their name, closing a case where a truncated 8-char prefix let two attachments silently overwrite each other, and HTML sends now build theirtext/plainalternative withhtml_to_text_preserving_breaks()so non-HTML clients see real paragraph breaks instead ofFirst paragraph.Second paragraph.by @taylorwilsdon in #1022 - fix(attachments): sweep the storage directory by file mtime, on
get_attachment_storage()and as part ofcleanup_expired(), so attachments written before a restart actually expire. Expiry previously lived only in the in-memory metadata dict, which meant a restart orphaned every file already on disk with nothing left to ever delete it by @justadityaraj in #1028 - fix(calendar): annotate
get_eventsstart/end boundaries with the offset-local weekday (2026-08-18T09:00:00-04:00 [weekday: Tuesday; ISO weekday: 2]) and mark all-day ends as exclusive, so a model reading the response stops deriving the wrong day from a raw RFC 3339 string. Values that don't match RFC 3339 (or Google's all-dayYYYY-MM-DD) pass through untouched by @TheMachineAiden in #1025 - fix(core): add
CamelCaseArgumentsMiddleware, which renames camelCase tool arguments (calendarId,timeMin,maxResults, ...) onto their snake_case parameters. Every tool declaresadditionalProperties: false, so callers echoing Google's field names used to fail schema validation before the request ever reached Google (#918). A key is only renamed when it isn't itself a declared parameter, its snake_case form is, and the caller didn't also pass the snake_case key, so existing callers and genuinely invalid arguments behave exactly as before by @Bortlesboat in #921
New Contributors
- @TheMachineAiden made their first contribution in #1025
Full Changelog: v1.24.1...v1.25.0
v1.24.1
Release v1.24.1
Bug-fix release focused on OAuth 2.1: the ChatGPT connector can complete its token exchange again, and OAuth 2.1 mode now fails fast with an actionable message instead of dying at the last step of the browser flow. Also adds a per-tool block list for trimming individual tools out of any tier.
What's Changed
- fix(oauth21): require
GOOGLE_OAUTH_CLIENT_SECRETin OAuth 2.1 mode and fail at startup with an actionable error instead of letting the browser flow die oninvalid_request: client_secret is missingat/oauth2callback. Google enforces the secret on the authorization code exchange even for public clients sending a valid PKCEcode_verifier, so the documented "secretless" launch block could never complete end to end. MCP clients still connect with PKCE and no secret — the secret is only used by this server for its own upstream exchange with Google, and it doubles as the JWT signing key material, soFASTMCP_SERVER_AUTH_GOOGLE_JWT_SIGNING_KEYis no longer needed. Deployments where a separate IdP performs the code exchange are exempt viaEXTERNAL_OAUTH21_PROVIDER=true. Fixes #1008 by @taylorwilsdon in #1019 - fix(deps): bump
fastmcpto>=3.4.7to restore the ChatGPT connector (developer mode), which authenticates via CIMD withprivate_key_jwtclient assertions. FastMCP 3.4.4's OAuthProxy validated those assertions against a doubled-slash audience (https://host//token) when the server is deployed at a bare origin, rejecting ChatGPT's correctly signedaud: https://host/tokenwithinvalid_client: Invalid JWT assertion— sign-in and consent completed, then the token exchange failed with a generic "There was a problem connecting". Fixes #1015 by @taylorwilsdon in #1018 - enh(tools): add
--disabled-tools <name> ...andWORKSPACE_MCP_DISABLED_TOOLS(comma-separated) to block individual tools by name. Every other filter is an allowlist; this one is subtractive, so it composes with--tools,--tool-tier,--read-only, and--permissions, and block wins over allow — you can dropsend_gmail_messagewhile still running--tool-tier core. Unmatched names log a warning rather than failing startup, since a name is legitimately absent when its service isn't loaded. Works for plugin users who can't pass CLI args, and the resolved block list is shown in the startup configuration banner by @taylorwilsdon in #990
Upgrade notes
If you were running OAuth 2.1 in the README's old secretless configuration, the server will now refuse to start until you set GOOGLE_OAUTH_CLIENT_SECRET (use a Web application client). That flow never actually completed against Google, so this converts a confusing mid-flow 500 into a startup error that tells you what to set. FASTMCP_SERVER_AUTH_GOOGLE_JWT_SIGNING_KEY becomes optional once the secret is set — the JWT key is derived from it — but an explicit key is still honored if you'd rather set one.
Note that --disabled-tools doesn't narrow the OAuth scopes requested at consent; scopes are derived from the loaded services, not from individual tools. Use --read-only or --permissions when the goal is scope reduction rather than a smaller tool list.
Full Changelog: v1.24.0...v1.24.1
v1.24.0
Release v1.24.0
Gmail's send path finally gets the reply machinery the draft path already had, Drive file-content updates become first-class (append/prepend, no forced Docs conversion), and Slides speaker notes are readable and editable. Rounded out with reliability fixes across Gmail batching, Drive uploads and Docs styling.
Gmail
- feat(gmail):
send_gmail_messagecan now build a reply the waydraft_gmail_messagealready could —In-Reply-To/Referencesderived fromthread_id, opt-inreply_allrecipients (excluding your own address andSend Asalias), andquote_originalhistory, all off a single thread fetch;tobecomes optional whenreply_allcan derive it, and a non-reply send still fetches nothing (#898) by @vyakunin in #1004 - gmail: opt-in
include_headersonsearch_gmail_messagesputs Subject, From and Date on each result row viaformat="metadata", so triaging hits no longer needs a second content call — default output is byte-identical to before by @erikholz in #1017 - fix(gmail): batch fetches retry rate-limited
429/5xxsub-requests instead of quietly returning fewer messages than you asked for by @lukaszliniewicz in #1010 - fix(gmail): attachments nested under
multipart/signednow resolve their real filename rather than the enclosing part's by @bcelary in #1013 - fix(gmail):
draft_gmail_messagereply handling — fixes the reply-threading crash, actually attachesthreadIdso drafted replies land in the original conversation, and stops signature line breaks from collapsing by @erikholz in #917
Drive
- gdrive: file-content updates gain
replace/append/prependmodes, keep non-Google targets (.md,.txt,.pdf) in their original format instead of forcing a Docs conversion, accept text-like JSON/XML payloads, and serialize concurrent updates to the same file behind a per-file lock; downloads also fixed for shared-drive files by @taylorwilsdon in #1016 - fix(gdrive): resumable uploads treat
308 Resume Incompleteas the continuation signal it is instead of failing the upload by @jdowning10 in #1000 - fix(gdrive): Shared Drive permissions are read through
permissions.list, so files on a shared drive report their actual sharing state by @drew-miraxis in #1002
Docs & Slides
- slides: speaker notes are now first class — retrieve the notes text plus its editable shape ID, and replace, append or clear notes through presentation updates, with slides that have no notes handled cleanly by @taylorwilsdon in #1003
- fix(gdocs): fixes the styling payload bugs behind #998 and adds per-edge border control for table cells and paragraphs by @ssbn in #999
Auth & Docs
- fix(auth): new
WORKSPACE_MCP_ALLOW_NULL_ORIGIN_CONSENTflag lets reverse-proxy deployments accept theOrigin: nullconsent POST some setups produce; off by default, and CSRF protection still rides on theSameSite=LaxMCP_CONSENT_STATEcookie by @enthouan in #925
New Contributors
- @erikholz made their first contribution in #1017
- @lukaszliniewicz made their first contribution in #1010
- @ssbn made their first contribution in #999
- @bcelary made their first contribution in #1013
- @drew-miraxis made their first contribution in #1002
- @enthouan made their first contribution in #925
- @jdowning10 made their first contribution in #1000
- @vyakunin made their first contribution in #1004
Full Changelog: v1.23.1...v1.24.0
v1.23.1
Release v1.23.1
Bug-fix release focused on multi-user auth correctness and CLI argument handling.
What's Changed
- Auth identity no longer leaks between requests —
AuthInfoMiddlewarere-derives the caller's identity on every request instead of relying on FastMCP's session store, so clients that open a new session per tool call always get the right user. Thanks @SlayerBirden in #992 - Quieter, clearer logs on streamable-HTTP with legacy auth — no more noisy warnings when running the older auth path. by @taylorwilsdon in #991
workspace-clistops eating your quotes — a value likequery='"exact phrase"'was parsed as JSON and silently unquoted, breaking Gmail phrase searches. Values are now only JSON-decoded when they actually look like JSON (3,[["a"]],true); everything else is passed through verbatim. by @CapitalX in #983
New Contributors
Full Changelog: v1.23.0...v1.23.1
v1.23.0 - Gateway Auth, Tool Enhancements, Apps Script Overhaul
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, andaudalways 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_emailis hidden from tool schemas and auto-filled from the verified principal, so a caller can't act as another account by passing one. A configuredUSER_GOOGLE_EMAILdefault is ignored - the principal always wins. - The Google consent flow binds its OAuth state to that principal, and
/oauth2callbackrejects 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_filesnow appendstrashed=falseby default, matchinglist_drive_itemsand the Drive web UI. An explicittrashedclause in your own query still wins, quoted literals aren't mistaken for one, andinclude_trashed=Trueopts back in. inspect_doc_structureworks on tabbed Docs without atab_id(#976 by @Ofori01). It was reading headers and footers from the first tab but leavingbodypointed at the empty top-level document, so structure inspection came back blank. Body and named ranges now come from the same tab.- Both
get_eventspaths emit identical metadata (#980 by @wdrwilson). The single-event lookup returnedcolorId, recurrence, creator, and organizer while the ranged listing didn't. Shared formatting incalendar_helpersnow drives both branches, with tests asserting parity - andeventType/statusare omitted at their API defaults so ordinary meetings stay compact. read_sheet_valuesclamps 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.emailspan attribute (#974 by @DrFaust92). Tool-call traces can now be attributed to a person rather than only the opaqueenduser.idFastMCP derives from the token. It's PII, so it's strictly off unless the operator setsWORKSPACE_MCP_OTEL_USER_EMAIL, and it no-ops cleanly when OpenTelemetry isn't installed. - Reworked startup output. A new
core/startup_ui.pycentralizes the ANSI palette, section rules, and column alignment that were scattered throughmain.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-suppliedemitso the stdout-stays-clean-for-JSON-RPC rule lives in one place.
New Contributors
- @martiant - #978
- @Ofori01 - #976
- @wdrwilson - #980
- @sebastianrosch - #972
- @SlayerBirden - #986
Full Changelog: v1.22.2...v1.23.0
v1.22.2
Release v1.22.2
A maintenance release focused on ending the hourly OAuth 2.1 re-auth loop, returning complete data from Sheets and Gmail, and adding optional OpenTelemetry tracing. Also includes a security dependency bump and new Google Docs table header controls.
Authentication
Fixed: OAuth 2.1 proxy sessions no longer force a re-auth every hour (#942, fixes #886 and #775)
When running behind the FastMCP OAuth proxy, the server was building Google credentials without an upstream refresh token, so every session died when Google's ~1 hour access token expired and clients were bounced back through the full auth flow.
The session store now walks FastMCP 3.x's real token chain — the inbound reference JWT's jti → _jti_mapping_store → _upstream_token_store — to recover the upstream access and refresh tokens, producing credentials that refresh transparently. ensure_session_from_access_token() is now async (it awaits the proxy's key-value stores); callers in auth_info_middleware and service_decorator were updated accordingly. Requires fastmcp>=3.4.4.
Google Sheets
read_sheet_valuesno longer stops at 50 rows (#955) — the tool fetched everything inrange_namebut silently truncated the rendered output to the first 50 rows with an "... and N more rows" footer. It now returns every row it fetched; callers bound the volume withrange_name(defaultA1:Z1000).- Reject cell references passed as column letters (#964, thanks @syf2211) —
_column_to_indexaccepted inputs likeA1and quietly computed a garbage index from the digits. Column parsing is now strict (^[A-Za-z]+$), and A1 range/sheet-title parsing switched from.match()to.fullmatch()so trailing junk raises a clearUserInputErrorinstead of being ignored.
Gmail
- Untruncated message export (#939, thanks @123andy) —
get_gmail_message_contentgained afull: boolparameter that returns the complete message with no truncation limit. Over HTTP the body is written to attachment storage and handed back as a download URL; over stdio you get the file path; in stateless mode (no storage) it's inlined and explicitly labeled as complete. Works with all threebody_formatvalues, includingraw, which saves the byte-exact RFC 5322 message as.eml. Encoding and file writes run on a worker thread so large exports don't block the event loop. get_gmail_thread_contentnow includes To/Cc (#970, fixes #693) — recipient headers were dropped from thread output, making it impossible to tell who was on a message. Thread formatting now goes through_extract_headersand always printsTo:/Cc:, marking them[not present in Gmail response]when Gmail genuinely omits them rather than leaving the reader guessing. RepeatedTo/Ccheaders are merged instead of last-one-wins, and thread ownership analysis matches header names case-insensitively (Gmail does not guarantee casing).
Google Docs
Table header row support (#947, thanks @pmgagne) — two new batch_update_doc operations:
pin_table_header_rows— repeat the leading N rows of a table after every page break (0unpins). This is the writable Docs API request for the header state thatTableRowStyle.tableHeaderonly reports; sendingtableHeaderonupdateTableRowStylereturns a 400.update_table_row_style— setmin_row_heightfor specific rows.
create_table_with_data also accepts header_rows to pin headers at creation time, and now reports PARTIAL SUCCESS (with the effective index) when the table is created but a follow-up styling step doesn't fully apply.
Telemetry
Optional OpenTelemetry tracing (#938, thanks @DrFaust92) — FastMCP is already instrumented, but its spans are no-ops without a configured TracerProvider. The new core/telemetry.py wires one up from the standard OTEL_* environment variables so tool-call spans export to an OTLP collector.
pip install "workspace-mcp[otel]" # already in the Docker image
export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc" # or "http/protobuf"Strictly opt-in and off by default: with no endpoint set nothing is configured, a provider installed by auto-instrumentation is left alone, and a missing SDK logs a hint instead of failing startup. The README's security and licensing sections were updated to reflect that tracing exports only to an endpoint you configure yourself.
Dependencies
- Security bumps (#956) —
mcp1.28.1,pyasn10.6.4,pypdf6.14.2. fastmcpraised to>=3.4.4(required by the OAuth fix above).httplib20.31.2 → 0.32.0 (#969, @dependabot).
Docs
Upgrade notes
ensure_session_from_access_token()is now a coroutine. If you import it fromauth.oauth21_session_storein your own code, addawait.read_sheet_valuesoutput can be much larger than before. If you relied on the implicit 50-row cap, narrowrange_nameexplicitly.
New Contributors
As always, thanks to our new contributors!
- @pmgagne made their first contribution in #947
- @chrstphe made their first contribution in #965
- @syf2211 made their first contribution in #964
- @123andy made their first contribution in #939
Full Changelog: v1.22.1...v1.22.2
v1.22.1
Release v1.22.1
Bug-fix release focused on Apps Script deployments, Slides page inspection, Gmail reply threading, and a concurrency-safe startup path.
What's Changed
- fix(gappsscript): wrap the deployment update body in
deploymentConfig(withscriptId) soprojects.deployments.updateno longer rejects it with400 Unknown name "description"; also adds an optionalversion_numberto repoint a deployment at a new script version (#836) by @asif786ka in #894 - slides:
get_pagenow surfacessheetsChart,image,video, andwordArtelements instead of collapsing them to "Type: Unknown" — linked charts expose their sourcespreadsheetId/chartId, making it possible to find the backing sheet and refresh the chart by @CharlesMcquade in #904 - fix(core): make the credentials-dir permission check concurrency-safe — use
makedirs(exist_ok=True)and a unique temp-file probe (no shared probe file, normdir) so personal and work servers starting together can no longer race and crash startup by @seidnerj in #879 - fix(gmail): attach reply drafts to their existing thread via
message.threadIdso drafted replies appear inline in the original conversation instead of spawning a new thread by @kenjimax in #889 - fix(gmail): preserve
<br>boundaries in_HTMLTextExtractorso multi-line signatures no longer run together when extracting plain text by @sebastianlungu in #902 - test(forms): align the
set_publish_settingsupdateMaskassertion with the shippedpublishStatemask, fixing the red main branch that was failing every open PR's pytest check by @ameet-rajababa in #883 - chore(deps): bump
joserfcfrom 1.6.4 to 1.6.8 in the uv group by @dependabot[bot] in #906
New Contributors
- @asif786ka made their first contribution in #894
- @CharlesMcquade made their first contribution in #904
- @kenjimax made their first contribution in #889
- @sebastianlungu made their first contribution in #902
- @ameet-rajababa made their first contribution in #883
Full Changelog: v1.22.0...v1.22.1