Remove stale README mention of the retired workspace worker - #973
Remove stale README mention of the retired workspace worker#973tuongbeo wants to merge 127 commits into
Conversation
…am (~101 tools) - Gmail: 10 tools (batch, labels CRUD, attachments) - Calendar: 8 tools (RSVP, recurrence, color) - Drive: 14 tools (content read, share/permissions, batch share) - Docs: 12 tools (modify, find-replace, structure, comments) - Sheets: 8 tools (format, create sheet tab, list) - Slides: 5 tools (batch update, delete slide) - Chat: 4 tools (search messages) - Tasks: 9 tools (subtasks, move, clear completed, task lists CRUD) - Forms: 5 tools (create form, batch update, get response) - Contacts: 12 tools (search, update, delete, groups CRUD) - Apps Script: 11 tools (full project management + execution) - Custom Search: 3 tools (PSE integration) - types.ts: add GOOGLE_PSE_API_KEY / ENGINE_ID env vars - Split docs_sheets.ts into docs.ts + sheets.ts for clarity
Gmail: +get_gmail_threads_content_batch, +list_gmail_filters, +manage_gmail_filter
Drive: +get_drive_file_download_url, +check_drive_file_public_access
Docs: +search_docs, +list_docs_in_folder, +update_doc_headers_footers,
+create_table_with_data, +debug_table_structure,
+insert_doc_tab, +delete_doc_tab, +update_doc_tab
Sheets: +manage_conditional_formatting
Slides: +get_slide_page, +get_slide_thumbnail
Chat: +create_chat_reaction, +download_chat_attachment
Tasks: +get_task_list
Forms: +set_form_publish_settings
Contacts: +manage_contacts_batch
Apps Script: +create_script_version, +get_script_version, +list_script_versions,
+delete_script_project, +get_script_metrics
- fix insert_doc_tab: wrong API insertTab → addDocumentTab, add icon_emoji param - fix update_doc_tab: wrong API updateTabProperties → updateDocumentTabProperties, add icon_emoji support, proper field mask - fix get_google_doc: add includeTabsContent support, tab_id/include_all_tabs params, render @person elements as @name - new tool get_doc_tabs: list all tabs with full hierarchy (ID, title, emoji, level, parent) - new tool get_doc_tab_content: read text content of a specific tab by tabId - new tool insert_person_mention: insert @mention smart chip by email + display name, supports index/tab_id/at_end - new tool insert_multiple_mentions: batch insert multiple mentions in one batchUpdate call
… build - hono@4.12.8 - @modelcontextprotocol/sdk@1.27.1 - zod@3.25.76 - wrangler@4.73.0 (devDep) - @cloudflare/workers-types@4.20260313.1 (devDep) Cloudflare Workers build requires package.json present in worker directory to resolve npm dependencies during wrangler deploy.
Google Docs API addDocumentTab only accepts { tabProperties: { title, iconEmoji?, parentTabId? } }
No other fields allowed at the addDocumentTab level.
Removing insertion_index param from insert_doc_tab tool schema as well.
New file: cloudflare-worker/src/tools/slides.ts Slide management: - duplicate_slide: duplicate an existing slide - reorder_slides: move slides to a new position - update_slide_background: set background solid color Speaker notes: - get_slide_notes: read speaker notes from a slide - set_slide_notes: replace speaker notes on a slide Text & shapes: - add_text_to_slide: insert text box with custom position/size - delete_page_element: delete any shape/image/table element - update_shape_position: move and/or resize an element (EMU) - replace_all_text: find-and-replace across entire presentation - update_text_style: bold/italic/underline/font/color on text range - update_paragraph_alignment: START/CENTER/END/JUSTIFIED Images: - insert_image: insert image from URL with position/size - replace_all_shapes_with_image: template-style image replacement Tables: - create_table: create table with rows/columns on a slide - update_table_cell_text: set text in a specific cell - insert_table_rows: add rows above/below reference row - delete_table_row: delete a row by index - insert_table_columns: add columns left/right of reference column - delete_table_column: delete a column by index - update_table_cell_style: background color and border styling Total tools: ~130 → ~148
…index isolation docs
Priority 1 — get_doc_tabs: redesign output from human text → structured JSON
- Returns flat array with tab_id, title, index, nested_level, parent_tab_id,
icon_emoji, child_count fields — directly usable by AI agents
- Uses includeTabsContent=false (metadata only, faster response)
- Includes document_title, document_id, tab_count in envelope
Priority 2 — append_to_google_doc: add tab_id parameter
- Uses endOfSegmentLocation (correct API approach — no endIndex computation)
- Backward compatible: omitting tab_id appends to default/first tab
inspect_doc_structure: add tab_id parameter
- Fetches tab body content independently, labels output with tab context
Priority 3 — Content isolation model documented as code comments:
- Each tab has INDEPENDENT index space starting at 1
- endOfSegmentLocation is safest append method (avoids manual endIndex)
- includeTabsContent=true required to read tab body content
- Parent tab deletion cascades to all child tabs
- First tab (index 0) cannot be deleted if it is the only tab
…or real smart chips
Root cause of workaround:
- endOfSegmentLocation is unreliable for insertPerson (API inconsistency)
- No support for inserting context text around chip in one call
- insert_multiple_mentions required manual index calculation
Fixes:
1. Always use location: { index } for insertPerson — remove endOfSegmentLocation
2. Auto-detect safe insertion index when not provided (fetch doc end index)
3. Add prefix_text / suffix_text params — wraps chip in one atomic batchUpdate
e.g. prefix='Assigned to: ' suffix=' please review' → 1 tool call, 3 API ops
4. insert_multiple_mentions: auto-detects base index, tracks running offset
per insertion so indices stay correct across the batch
5. Each mention in batch gets its own line (prepends \n automatically)
Result: No more 2-step workaround. Real smart chips in all scenarios.
- Support per-connector Google OAuth Client ID/Secret - /register: accept client_id/client_secret from Claude.ai, store in DCR record - /authorize: resolve googleClientId from DCR record (priority) > env var - /callback: defer Google token exchange, only store google_code temporarily - /token: perform Google token exchange using client_secret from request body - Fix refresh token stability (prevent permission reset) - Store google_client_id/google_client_secret in KV token record for self-refresh - Only delete KV on permanent errors (invalid_grant/invalid_client), not transient 5xx - getValidAccessToken: credentials from token record, not from function params - Extend DCR TTL from 7 days to 90 days - Rename worker to google-workspace in wrangler.jsonc - GOOGLE_OAUTH_CLIENT_ID/SECRET env vars now optional (fallback only)
- Add withErrorHandler wrapper for uniform error handling across all 164 tools - Add textResult() helper utility - McpServer singleton pattern for performance (getOrCreateServer) - Add slidesRequest() export to google.ts (remove inline duplicate) - Add Cache-Control + CORS headers to /.well-known/* endpoints - Add MCP annotations (readOnlyHint, destructiveHint) to all tools - Add wrangler.qts.jsonc for qts-google-workspace worker Phase 5 - Docs Advanced (docs-advanced.ts, 11 tools): create_named_range, list_named_ranges, delete_named_range insert_footnote, insert_inline_image update_document_style, update_named_style get_doc_suggestions, accept_suggestion, reject_suggestion get_doc_metadata Phase 6 - Drive Version Control (drive-revisions.ts, 6 tools): list_drive_revisions, get_drive_revision, update_drive_revision delete_drive_revision, download_drive_revision, pin_latest_revision Total: 148 -> 164 tools
Gmail: - extractBody: HTML-only email fallback (strip tags to plain text) - get_gmail_message_content: expose In-Reply-To and References headers - create_gmail_draft: reply threading via In-Reply-To/References, quoted original content with signature above quoted block, HTML body support, bcc param Calendar: - get_calendar_events: extract meeting URL from conferenceData + hangoutLink - create_calendar_event: add visibility param (default/public/private/confidential) - update_calendar_event: add visibility param Docs: - apply_doc_text_style: bold/italic/underline/strikethrough/fontSize/color with tab_id support for multi-tab docs - create_bullet_list: convert paragraphs to bullet/numbered list Sheets: - read_sheet_values: include_notes param — fetches cell notes via includeGridData instead of values endpoint Chat: - get_chat_messages: add filter + order_by params (API filter strings) - search_chat_messages: add createTime range filters + space filter Forms: - get_form: expand schema with quiz settings, linked sheet ID, full item type breakdown (CHOICE options, SCALE range, PAGE_BREAK, etc.) Drive: - transfer_drive_ownership: new tool to transfer file ownership
- drive.ts: fix import_to_google_doc - upgrade text/markdown → text/html upload
with full markdown→HTML converter (H1-H6, bold, italic, lists, tables,
code blocks, blockquotes, links, horizontal rules)
Design tokens from Anthropic DOCX skill: Arial font, H1=16pt #111827,
H2=14pt, H3=12pt, body=11pt, table border #D1D5DB, header-bg #F3F4F6
Add input_format param: markdown|html|plain
- sheets.ts: add create_formatted_spreadsheet tool
One-call pipeline: create + write + format
Design tokens from Anthropic XLSX skill:
- 4 color themes (blue/green/gray/orange) with dark headers + alt rows
- Financial number formats: currency $#,##0;($#,##0);"-", 0.0%, 0.0x
- Font: Arial 11pt, borders #D1D5DB, frozen row, auto-resize columns
- workspace.ts: add create_presentation_from_outline tool
Design tokens from Anthropic PPTX skill (SKILL.md + pptxgenjs.md):
- 16:9 layout = 9144000×5143500 EMU
- 7 color palettes: midnight_executive, ocean_gradient, forest_moss,
coral_energy, charcoal_minimal, teal_trust, warm_terracotta
- 4 font pairings: Arial Black/Arial, Georgia/Calibri,
Calibri/Calibri Light, Trebuchet MS/Calibri
- Typography scale: cover=40pt, section=36pt, title=26pt, body=16pt,
bullet=15pt, stat=60pt (from PPTX skill: title 36-44pt, body 14-16pt)
- 6 slide types: cover, section, bullets, two_column, big_number, quote
- Sandwich structure: dark cover/section, light content slides
- No accent lines under titles (PPTX skill rule)
BUG-001 CRITICAL fix (index.ts): Add WWW-Authenticate header to invalid_token 401
- RFC 6750 requires WWW-Authenticate on ALL 401 responses
- Without it, Claude.ai cannot trigger re-authentication flow
- Added error=invalid_token + resource_metadata_url to the header
BUG-002 HIGH fix (index.ts): Handle OPTIONS CORS preflight before auth check
- OPTIONS /mcp returned 401, blocking browser-based MCP clients
- Added app.options('/mcp') returning 204 with full CORS headers
- Exposes: Mcp-Session-Id, Access-Control-Allow-Headers, max-age=86400
BUG-003 HIGH fix (drive.ts): markdownToHtml underscore italic regex corrupts URLs
- Original regex (?<![*_])_(.+?)_(?![*_]) matched underscores inside URLs
- https://x.com/my_file_name → https://x.com/my<em>file</em>name (CORRUPT)
- Fix: boundary-aware pattern (^|\s)_word_(?=\s|$) — only standalone _word_
- Confirmed: 4 test cases pass (URL safe, snake_case safe, intended italic works)
BUG-004 MEDIUM fix (workspace.ts): uid() collision risk in tight loops
- Date.now() returns same ms in tight loops; 3-char random = 46656 combinations
- Confirmed collision at i=168 in 200 same-ms calls test
- Fix: module-level counter _uidC + 8-char random (eliminates collisions)
BUG-005 MEDIUM fix (slides.ts): update_shape_position uses RELATIVE instead of ABSOLUTE
- RELATIVE mode ADDS x/y to existing position instead of SETTING it
- Users expect set_position(x=300) to place at 300, not +300 from current
- Fix: applyMode 'RELATIVE' → 'ABSOLUTE' in updatePageElementTransform
BUG-006 LOW fix (mcp-agent.ts): Stale tool count comments
- registerSheetsTools: 8 → 9 tools (create_formatted_spreadsheet added)
- registerSlidesTools: 5 → 6 tools (create_presentation_from_outline added)
- Total: ~164 → ~167 tools
Also re-applied visual output improvements from previous deploy:
- drive.ts: markdownToHtml converter + updated import_to_google_doc
- sheets.ts: create_formatted_spreadsheet (Anthropic XLSX design tokens)
- workspace.ts: create_presentation_from_outline (Anthropic PPTX design tokens)
Version deployed: 62b57999-34bb-4200-ae1e-d54912de11d0
…KV vs TOKENS_KV) ROOT CAUSE of 'Authorization with the MCP server failed': oauth.ts line 319: storeTokens(sub, ..., env.OAUTH_KV) ← wrote here mcp-agent.ts line 104: getValidAccessToken(sub, env.TOKENS_KV) ← read from here Two different KV namespaces: OAUTH_KV = e51d896daf1f46019c04627a38beac8b (state, auth codes, DCR records) TOKENS_KV = f57a70d271024b669640803bd4a9a2a1 (Google access/refresh tokens) Timeline: 1. User authenticates with Google → /callback → /token succeeds 2. storeTokens writes token: to OAUTH_KV ← BUG 3. Claude.ai calls /mcp with proxy JWT 4. getValidAccessToken looks for token: in TOKENS_KV → NOT FOUND 5. Throws 'No token found for sub=...' → 401 → 'Authorization failed' Fix: env.OAUTH_KV → env.TOKENS_KV in handleToken's storeTokens call Also added missing OAuth scopes: + gmail.settings.basic (required: list_gmail_filters, manage_gmail_filter) + script.projects (required: all Apps Script tools) + script.runs + script.metrics + script.deployments Deployed: version 81af4f77-3f11-4230-9d1f-f9f964d496ab
…oken
Root cause of persistent auth failures:
The 'deferred exchange' design assumed Claude.ai would send Google OAuth
client_secret in /token. But Claude.ai sends the UUID client_secret from
DCR response, not the user's real Google OAuth secret.
Result: Google exchange always failed with invalid_client.
New flow (simplified):
/callback → exchange with Google immediately (has all credentials from DCR)
→ store proxy JWT as pending_jwt:{tempCode} in OAUTH_KV (2min TTL)
→ redirect Claude.ai with tempCode
/token → read pending_jwt:{tempCode} → delete → return JWT
(no more Google exchange in /token)
Changes:
- oauth.ts: rewrote /callback and /token
- oauth.ts: credentials resolved from DCR record at /callback time
- oauth.ts: removed AuthCodeRecord (no longer needed)
- oauth.ts: added console.log at each step for debugging
- oauth.ts: error pages now show meaningful debug info
- types.ts: added TOKENS_KV and CONFIG_KV to Env interface
Deployed: 6ed5f58a-f351-417c-a813-53efded0ad90
Critical: TOKENS_KV was missing from wrangler.jsonc bindings. At runtime env.TOKENS_KV = undefined → storeTokens threw TypeError → 500 Internal Server Error on /callback. Added: TOKENS_KV f57a70d271024b669640803bd4a9a2a1 (Google access/refresh tokens) CONFIG_KV 78cc72f93f34486dab1ff1a3fd5ecd81 (config/flags) Also improved /callback error page with setup instructions.
- handleCallback: wrap entire body in try/catch to prevent 500 Any unexpected error now returns a readable HTML page with details - pending_jwt TTL: 120s → 300s (5 min buffer for Claude.ai /token call)
…n header, higher thresholds
…actor sheets themes
… tab props, filter, protected, batch
…ipt triggers (+8 tools)
…omments/suggestions/revisions/deployments/versions/contact-groups
- Navbar, Footer: Workspace MCP → Workspace Lens - LandingPage: new hero title + subtitle with MCP server description - PrivacyPage, TermsPage: update service name reference
- favicon.svg: new logo (rounded rect + lens iris + gradient) - index.html: title, description, font → Plus Jakarta Sans - index.css: brand colors #3B82F6 blue + #10B981 green, updated surface/border tones
favicon.svg + Navbar SVG now respond to OS color scheme: dark → #0f172a bg, #3B82F6/#10B981 strokes light → #f8fafc bg, #2563eb/#059669 strokes (darker for contrast)
Add check_drive_file_public_access to SKIP_DRIVE because it reads permissions on arbitrary files and requires the drive.readonly scope. Also add list_script_projects to SKIP_APPSSCRIPT with a comment explaining it queries the Drive API and, under drive.file scope, only returns scripts created by this app (leading to misleading empty results). Includes clarifying comments for why these tools are skipped.
A large multi-phase pass over the cloudflare-worker service and its supporting docs, covering four areas: Bug fixes (across tools/*.ts): - write_google_doc append mode inserted new content at doc start instead of the end (docs.ts) - drive.ts called driveRequest() 24 times without importing it — the worker was failing to type-check entirely - Chat: search filter for space_name was malformed (missing `space =` comparator); get_chat_messages double-flipped already-ordered results - Forms: progressBar sent as a wrapped object instead of a boolean; is_quiz changes were dropped from the batchUpdate updateMask - Tasks: update_task left a stale `completed` timestamp when moving a task back to needsAction - Gmail: quoted-reply HTML wasn't escaped, so `<`/`&` in the original message could corrupt the draft - Contacts: batch update used truthy checks instead of `!== undefined`, so clearing a field with "" silently no-opped - Apps Script: deployment updates PUT a partial config, wiping unspecified fields (version/description) on every update - Slides: duplicate_slide accepted insertion_index but never applied it - search.ts: item.link could be undefined and rendered as the literal string "undefined" (caught while adding types) - docs-engine: nested markdown list items were silently dropped by the parser, and the builder's style-cursor drifted out of sync whenever an item had subItems Tech debt / infra: - Added CI (type-check + test) on every PR; fixed the empty dependabot.yml stub; added a deploy:office script - Bumped agents, @modelcontextprotocol/sdk, and @cloudflare/workers-oauth-provider, resolving a duplicate-SDK install that was breaking type-checking on the two McpAgent entry points - Wired token namespace through env.TOKEN_NAMESPACE instead of a hardcoded per-agent string; /health now reports a namespace_mismatch if the wrangler config and the deployed agent ever disagree - Deleted tools/composite.ts (confirmed via git history it was dead code left over from before write_google_doc existed, not unfinished work) and its two no-op call sites - Rewrote README.md, which had described a different (Python/self-host) architecture than what's actually in this repo; added cloudflare-worker/README.md documenting the two-worker topology and delegated-OAuth flow; fixed SECURITY.md's stale contact/versioning info and updated contact email across README/SECURITY/landing pages Test coverage: - Added ~85 tests covering google-tokens.ts (refresh/retry/backoff, lock contention, the 5-minute stale-token grace window), auth/google.ts and workers/shared.ts (delegated OAuth, multi-tenant routing, including a cross-tenant replay-attack regression test), google.ts, index.ts, and the docs-engine nested-list fix - npm run test:coverage now passes; it was failing outright before (0% coverage on 6 files with configured thresholds) Type safety: - Added google-api-types.ts with minimal per-service response interfaces (not full API schemas) and replaced ~76 blind `as any` casts across chat/calendar/forms/tasks/search/gmail/contacts/slides tools with them Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix: bug sweep, tech debt remediation, test coverage, and type safety
Addresses every finding from a full security/correctness audit of cloudflare-worker/src, from critical injection/RCE issues down to maintainability cleanup. Critical: - Fix Drive query injection (list_drive_files/search_drive_files q= building) - Fix SSRF in create_drive_file's source_url (new assertSafeExternalUrl guard) - Add explicit destructive/warning annotations to Apps Script write+run tools, since script execution runs with the full authority of the authorizing account, not this MCP's own OAuth scopes - Fix Sheets formula injection — defuse leading =+-@ in all USER_ENTERED writes; escape quotes in IMAGE() formula construction - Fix nested markdown list parsing (was silently corrupting/dropping content) and the matching cursor-desync bug in the Docs builder's style pass High: - Fix write_google_doc "append" mode inserting at index 1 instead of the end - Fix path/parameter injection across Gmail/Chat/Contacts/Tasks tools (encodeURIComponent or strict resource-name validation) - Fix Gmail header injection (CRLF stripped from to/cc/bcc/subject) - Reject unvalidated redirect_uri in OAuth dynamic client registration - Cap Sheets/Slides/Docs input arrays to prevent batchUpdate/memory blowup - Validate google-auth's `sub` response shape before using it as a KV key Medium/Low: - Mitigate token-refresh TOCTOU race; stop returning known-dead tokens during the grace window; don't mass-delete tokens on invalid_client - Mask PII (email/sub) in logs - get_drive_shareable_link no longer silently makes files public - Parallelize N+1 batch loops (Gmail, Contacts, Drive) - Fix PH_LEN off-by-one and dropped isVi locale format in the doc/sheet engines - Fix CSV parser breaking on embedded newlines inside quoted fields - Remove dead composite.ts (no-op tool registration, ~300 unused lines) - Add fetch timeouts to all outbound Google/auth-service calls - Add URL scheme validation for Docs/Slides hyperlinks and images - Implement previously-dead conditional_rules/indent_rows sheet options Also fixes several pre-existing issues surfaced by `tsc --noEmit` while verifying these changes (confirmed via git stash that they predate this work): drive.ts was missing its `driveRequest` import entirely (every Drive tool call would throw ReferenceError at runtime), OAuthProps was missing the index signature McpAgent's generic constraint requires, completeAuthorization was missing the now-required `metadata` field, and workers/shared.ts's WorkerConfig.agent.serve return type didn't match apiHandler's expected shape. Verified with `tsc --noEmit` (clean), `vitest run` (102/102 passing), and `wrangler deploy --dry-run` (builds successfully). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rebasing onto origin/main surfaced that the "bug sweep, tech debt remediation" PR merged in the meantime touched nearly the same files — several bugs (missing driveRequest import, the nested-list parser bug, the append-mode index-1 bug, the isVi/PH_LEN issues) were independently found and fixed the same way in both. Resolved all conflicts by keeping both sets of improvements: upstream's typed API responses/as-any removal plus this branch's security fixes (encodeURIComponent, resource-name validation, formula defusing, etc.) on the same lines. Also: - Add missing `kind`/`nextPageToken` fields to the GTask/GTaskListResponse types introduced upstream, needed by this branch's pagination and update_task field-stripping logic. - Relax isValidGoogleSub to just check for a non-empty, colon-free string instead of requiring Google's usual numeric format — the upstream google-auth service is independently versioned, and existing test fixtures (and any future non-numeric identifier format) shouldn't be rejected by an assumption that was stricter than the actual bug required. - Update a google-tokens.test.ts case that asserted the old (buggy) grace- window behavior — a permanent invalid_grant failure now correctly propagates instead of returning a token already known to be dead. Verified with `tsc --noEmit` (clean), `vitest run` (180/180 passing, no unhandled rejections), and `wrangler deploy --dry-run` (builds successfully). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…view fix: security and correctness fixes from full cloudflare-worker review
…CP annotation defaults Cross-referenced ~100 non-dependabot upstream PRs (taylorwilsdon/google_workspace_mcp main) against this TypeScript rewrite to find behavior the port had silently dropped since diverging from Python upstream. Security: - Escape the `error` query param before it's interpolated into the OAuth callback error page HTML (reflected XSS in workers/shared.ts). Correctness bugs: - contacts.ts: manage_contact/manage_contacts_batch accepted only a single email/phone string, so updating a contact silently overwrote and dropped any other emails/phones it already had. email/phone now accept a string or an array. - slides.ts: text inside grouped shapes (elementGroup.children) was never read, so get_presentation/get_slide_page/get_slide_notes silently dropped it. Added a recursive extractElementsText helper. - docs.ts: list_document_comments fetched a single page with no way to reach further comments. Added page_token param + nextPageToken passthrough. - gmail.ts: batch/thread/single message reads never surfaced attachment metadata (filename/mimeType/attachment_id), making attachments effectively invisible without already knowing their IDs. - calendar.ts: send_updates wasn't wired into event creation (only update/delete), and get_calendar_event was missing the Creator field. New tools (Sheets): - duplicate_sheet — duplicate a sheet tab via the Sheets API's duplicateSheet request, preserving data/formulas/formatting. - manage_sheet_rows — insert/delete/move rows or columns (dimension-based), filling a gap only reachable before via the raw batch_update_spreadsheet escape hatch. Cross-cutting MCP tool annotations: - 37 of 193 tools registered with no ToolAnnotations at all, which per the MCP spec defaults destructiveHint to true for any non-read-only tool — mislabeling plain reads (get_slide_notes, export_doc_to_pdf, ...) as destructive writes, and ordinary writes (add_document_comment, send_chat_message, ...) as destructive. - Added utils/tool-annotations.ts::withSmartDefaults, a wrapper around McpServer.tool() applied once at each server's construction (mcp-worker.ts, office-agent.ts) that fills in title/readOnlyHint/destructiveHint/ idempotentHint/openWorldHint for any tool that doesn't set them, without touching all 193 call sites. - Explicitly annotated the 20 tools where the wrapper's safe default (destructiveHint: false) would otherwise be wrong: 8 reads that were missing readOnlyHint, and 8 delete/remove/replace-destructively tools that need destructiveHint: true called out explicitly rather than relying on spec-default behavior. Verified: tsc --noEmit clean, 183/183 vitest tests pass (3 new, covering withSmartDefaults), wrangler deploy --dry-run succeeds for both the mcp-google-workspace and mcp-office workers.
…and-tooling Fix XSS + data-loss bugs from upstream audit, add Sheets tools and MCP annotation defaults
Cloudflare KV rejects expirationTtl values below 60, so the 30s refresh lock in getValidAccessToken() failed every write with "KV PUT failed: 400 Invalid expiration_ttl of 30." This blocked token refresh entirely, breaking every Office MCP write tool (write_sheet_values, create_drive_file, etc.) once the access token neared expiry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix: raise refresh-lock KV TTL to Cloudflare's 60s minimum
…ailure Google's token endpoint returns unauthorized_client when the client_id/secret used for a refresh call doesn't match the client the refresh_token was issued to. refreshWithRetry only special-cased invalid_grant and invalid_client, so this error fell into the generic transient-error branch: it retried 3 times with backoff for nothing, and getValidAccessToken's isPermanent check didn't recognize the resulting message, so a refresh failing this way within the 5-minute stale-token grace window would silently hand back the old access token instead of surfacing the real problem. Traced from a live "refresh failed (401): unauthorized_client" report; the actual credential mismatch turned out to be in the separate google-auth service (delegate.ts never persists which client_id/secret a token was issued under), but this repo's error handling should surface any such permanent, client-config-level rejection clearly rather than mask it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…resh fix: treat unauthorized_client as a permanent token-refresh failure
office.lens.io.vn was failing every token refresh with unauthorized_client:
getValidAccessToken() fell back to this worker's own GOOGLE_OAUTH_CLIENT_ID
when refreshing, but the stored TOKENS_KV record was minted by google-auth
under a different Google OAuth client (Client A1) — a client Google never
issued that refresh_token to.
google-auth now exposes POST /delegate/refresh (companion PR in the
google-auth repo) and is the only place holding Google client secrets.
getValidAccessToken() now calls that endpoint instead of talking to Google
directly, switching on a structured {error: ok|reauth_required|config_error
|transient|not_found} response instead of parsing Google's raw error
strings. Removed as dead weight now that refresh is centralized: the local
refresh lock (locking moved server-side), the unused storeTokens() function,
and the google_client_id/secret fields from StoredTokenRecord/makeGetCreds.
GOOGLE_OAUTH_CLIENT_ID/SECRET wrangler secrets are deliberately left
provisioned (unused) as an instant-rollback path for now. Also genericized
doc comments that listed plan/social as example sub-workers — neither is
registered/active or has a wrangler config in this repo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Refresh Google tokens via google-auth's centralized /delegate/refresh
Renames this deployment's identity to keep wrangler.jsonc, the KV token namespace, and the OAuth delegating handler's server name consistent with each other. The defaultHandler's serverName must match TOKEN_NAMESPACE, since it's the identifier this worker is registered under in google-auth's mcp_servers table. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rename Cloudflare worker deployment from workspace to office
wrangler.jsonc pointed at the already-decommissioned "workspace" Cloudflare script and had drifted to a conflicting `name: "office"` — deploying it would have clobbered office's Durable Objects. Its GoogleWorkspaceAgent entry point (src/index.ts, src/mcp-worker.ts) has no live deployment left to serve, so it and its config/tests are removed along with the now-dead GOOGLE_OAUTH_CLIENT_ID/ SECRET and GW_SERVER bindings in Env — refreshing is centralized in google-auth's /delegate/refresh, and the office worker no longer holds a Google OAuth client secret at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Retire legacy workspace worker and unused OAuth fallback secrets
The workspace worker's code was already deleted in 384cc2f; office is the only deployed worker now, so the README should say that instead of describing a worker that no longer exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (10)
📒 Files selected for processing (210)
📝 WalkthroughWalkthroughThe repository transitions from a Python MCP server to a TypeScript Cloudflare Workers platform with delegated OAuth, Google Workspace MCP tools, Docs and Sheets generation engines, shared styling utilities, CI, coverage reporting, and a React landing site. ChangesWorkspace Lens platform
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant OfficeWorker
participant GoogleAuth
participant OAuthProvider
participant OfficeAgent
Client->>OfficeWorker: Start OAuth or send MCP request
OfficeWorker->>GoogleAuth: Delegate authorization or refresh
GoogleAuth-->>OfficeWorker: Return identity or access token
OfficeWorker->>OAuthProvider: Complete OAuth or forward authenticated request
OAuthProvider->>OfficeAgent: Serve MCP tools
OfficeAgent-->>Client: Return tool response
Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Opened by mistake (wrong target repo — meant to open this against a personal fork). Closing. |
Description
officeis now the only deployed Cloudflare Worker for this project — thelegacy
workspaceworker's code was already removed in 384cc2f. The rootREADME still described a "broader
workspaceworker covering Gmail,Calendar, Chat, Contacts, and Custom Search" that no longer exists in the
codebase, which would mislead anyone reading it about what's actually
deployed. This corrects that section to reflect reality.
(This also closes out a related investigation from this session: a reported
"Office MCP shows Connected but Claude says it needs re-auth" bug, traced to
the
workspace→officeKV token-namespace rename leaving pre-renameusers' tokens unreachable. A namespace-fallback fix was drafted and verified
with tests, then deliberately reverted at the user's request once they
confirmed no users are still stuck on the old namespace — so this PR only
carries the doc cleanup, no behavioral change.)
Type of Change
Testing
npm test— 174 passed)tsc --noEmitcleanChecklist
Additional Notes
Doc-only change — no source/runtime code touched.
Summary by CodeRabbit
New Features
Documentation
Chores