@triffon/google-docs-mcp 1.11.3-triffon

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +567 -0
  3. package/dist/auth.js +327 -0
  4. package/dist/cachedToolsList.js +38 -0
  5. package/dist/clients.js +180 -0
  6. package/dist/config.js +9 -0
  7. package/dist/downloadProxy.js +82 -0
  8. package/dist/driveQueryUtils.js +7 -0
  9. package/dist/firestoreTokenStorage.js +32 -0
  10. package/dist/googleDocsApiHelpers.js +1257 -0
  11. package/dist/googleSheetsApiHelpers.js +849 -0
  12. package/dist/index.js +240 -0
  13. package/dist/landingPage.js +103 -0
  14. package/dist/logger.js +58 -0
  15. package/dist/markdown-transformer/docsToMarkdown.js +259 -0
  16. package/dist/markdown-transformer/index.js +128 -0
  17. package/dist/markdown-transformer/markdownToDocs.js +834 -0
  18. package/dist/remoteWrapper.js +72 -0
  19. package/dist/tools/calendar/createEvent.js +89 -0
  20. package/dist/tools/calendar/deleteEvent.js +49 -0
  21. package/dist/tools/calendar/helpers.js +19 -0
  22. package/dist/tools/calendar/index.js +12 -0
  23. package/dist/tools/calendar/listEvents.js +84 -0
  24. package/dist/tools/calendar/quickAddEvent.js +55 -0
  25. package/dist/tools/calendar/updateEvent.js +84 -0
  26. package/dist/tools/docs/addTab.js +84 -0
  27. package/dist/tools/docs/appendTableRows.js +89 -0
  28. package/dist/tools/docs/appendToGoogleDoc.js +85 -0
  29. package/dist/tools/docs/cloneTable.js +159 -0
  30. package/dist/tools/docs/comments/addComment.js +83 -0
  31. package/dist/tools/docs/comments/deleteComment.js +30 -0
  32. package/dist/tools/docs/comments/getComment.js +45 -0
  33. package/dist/tools/docs/comments/index.js +14 -0
  34. package/dist/tools/docs/comments/listComments.js +43 -0
  35. package/dist/tools/docs/comments/replyToComment.js +35 -0
  36. package/dist/tools/docs/comments/resolveComment.js +55 -0
  37. package/dist/tools/docs/deleteRange.js +61 -0
  38. package/dist/tools/docs/deleteTableRows.js +62 -0
  39. package/dist/tools/docs/findAndReplace.js +54 -0
  40. package/dist/tools/docs/findElement.js +43 -0
  41. package/dist/tools/docs/findSectionsByHeading.js +46 -0
  42. package/dist/tools/docs/formatting/applyParagraphStyle.js +83 -0
  43. package/dist/tools/docs/formatting/applyTextStyle.js +49 -0
  44. package/dist/tools/docs/formatting/batchApplyTextStyle.js +86 -0
  45. package/dist/tools/docs/formatting/index.js +16 -0
  46. package/dist/tools/docs/formatting/updateTableBorders.js +81 -0
  47. package/dist/tools/docs/formatting/updateTableCellStyle.js +85 -0
  48. package/dist/tools/docs/formatting/updateTableColumnWidth.js +51 -0
  49. package/dist/tools/docs/formatting/updateTableRowStyle.js +74 -0
  50. package/dist/tools/docs/getTableStructure.js +48 -0
  51. package/dist/tools/docs/index.js +66 -0
  52. package/dist/tools/docs/insertDateChip.js +82 -0
  53. package/dist/tools/docs/insertImage.js +112 -0
  54. package/dist/tools/docs/insertPageBreak.js +47 -0
  55. package/dist/tools/docs/insertPerson.js +53 -0
  56. package/dist/tools/docs/insertRichLink.js +58 -0
  57. package/dist/tools/docs/insertSectionBreak.js +60 -0
  58. package/dist/tools/docs/insertTable.js +42 -0
  59. package/dist/tools/docs/insertTableWithData.js +125 -0
  60. package/dist/tools/docs/insertText.js +49 -0
  61. package/dist/tools/docs/listDocumentTables.js +47 -0
  62. package/dist/tools/docs/listDocumentTabs.js +59 -0
  63. package/dist/tools/docs/listSmartChips.js +41 -0
  64. package/dist/tools/docs/modifyText.js +147 -0
  65. package/dist/tools/docs/readGoogleDoc.js +164 -0
  66. package/dist/tools/docs/renameTab.js +47 -0
  67. package/dist/tools/docs/replaceTableRowData.js +55 -0
  68. package/dist/tools/docs/smartChipHelpers.js +71 -0
  69. package/dist/tools/docs/structureHelpers.js +250 -0
  70. package/dist/tools/docs/tabFieldMasks.js +48 -0
  71. package/dist/tools/docs/tableRowDataHelpers.js +53 -0
  72. package/dist/tools/docs/updateSectionStyle.js +148 -0
  73. package/dist/tools/drive/copyFile.js +63 -0
  74. package/dist/tools/drive/createDocument.js +105 -0
  75. package/dist/tools/drive/createFolder.js +48 -0
  76. package/dist/tools/drive/createFromTemplate.js +82 -0
  77. package/dist/tools/drive/deleteFile.js +72 -0
  78. package/dist/tools/drive/downloadFile.js +266 -0
  79. package/dist/tools/drive/getDocumentInfo.js +48 -0
  80. package/dist/tools/drive/getFolderInfo.js +48 -0
  81. package/dist/tools/drive/index.js +34 -0
  82. package/dist/tools/drive/listDriveFiles.js +129 -0
  83. package/dist/tools/drive/listFolderContents.js +83 -0
  84. package/dist/tools/drive/listGoogleDocs.js +70 -0
  85. package/dist/tools/drive/moveFile.js +54 -0
  86. package/dist/tools/drive/renameFile.js +39 -0
  87. package/dist/tools/drive/savePathGuard.js +86 -0
  88. package/dist/tools/drive/searchDriveFiles.js +148 -0
  89. package/dist/tools/drive/searchGoogleDocs.js +77 -0
  90. package/dist/tools/drive/setFilePermission.js +68 -0
  91. package/dist/tools/gmail/createDraft.js +58 -0
  92. package/dist/tools/gmail/deleteDraft.js +37 -0
  93. package/dist/tools/gmail/getDraft.js +52 -0
  94. package/dist/tools/gmail/getMessage.js +92 -0
  95. package/dist/tools/gmail/helpers.js +113 -0
  96. package/dist/tools/gmail/index.js +28 -0
  97. package/dist/tools/gmail/listDrafts.js +74 -0
  98. package/dist/tools/gmail/listLabels.js +31 -0
  99. package/dist/tools/gmail/listMessages.js +87 -0
  100. package/dist/tools/gmail/modifyMessageLabels.js +54 -0
  101. package/dist/tools/gmail/sendDraft.js +42 -0
  102. package/dist/tools/gmail/sendEmail.js +56 -0
  103. package/dist/tools/gmail/trashMessage.js +37 -0
  104. package/dist/tools/gmail/triageInbox.js +147 -0
  105. package/dist/tools/gmail/updateDraft.js +60 -0
  106. package/dist/tools/index.js +64 -0
  107. package/dist/tools/script/appsScriptShared.js +96 -0
  108. package/dist/tools/script/createAppsScriptProject.js +74 -0
  109. package/dist/tools/script/getAppsScriptContent.js +56 -0
  110. package/dist/tools/script/index.js +14 -0
  111. package/dist/tools/script/updateAppsScriptContent.js +56 -0
  112. package/dist/tools/sheets/addConditionalFormatting.js +143 -0
  113. package/dist/tools/sheets/addSpreadsheetSheet.js +34 -0
  114. package/dist/tools/sheets/appendSpreadsheetRows.js +44 -0
  115. package/dist/tools/sheets/appendTableRows.js +51 -0
  116. package/dist/tools/sheets/autoResizeColumns.js +67 -0
  117. package/dist/tools/sheets/autoResizeRows.js +63 -0
  118. package/dist/tools/sheets/batchWrite.js +61 -0
  119. package/dist/tools/sheets/clearSpreadsheetRange.js +31 -0
  120. package/dist/tools/sheets/comments/commentAnchor.js +95 -0
  121. package/dist/tools/sheets/comments/createSheetsCellNote.js +33 -0
  122. package/dist/tools/sheets/comments/createSheetsComment.js +168 -0
  123. package/dist/tools/sheets/comments/deleteSheetsComment.js +32 -0
  124. package/dist/tools/sheets/comments/getSheetsComment.js +51 -0
  125. package/dist/tools/sheets/comments/index.js +16 -0
  126. package/dist/tools/sheets/comments/listSheetsComments.js +177 -0
  127. package/dist/tools/sheets/comments/replyToSheetsComment.js +37 -0
  128. package/dist/tools/sheets/comments/resolveSheetsComment.js +53 -0
  129. package/dist/tools/sheets/copyFormatting.js +59 -0
  130. package/dist/tools/sheets/copySheetTo.js +36 -0
  131. package/dist/tools/sheets/createSpreadsheet.js +72 -0
  132. package/dist/tools/sheets/createTable.js +120 -0
  133. package/dist/tools/sheets/deleteChart.js +41 -0
  134. package/dist/tools/sheets/deleteConditionalFormatting.js +46 -0
  135. package/dist/tools/sheets/deleteSheet.js +43 -0
  136. package/dist/tools/sheets/deleteTable.js +56 -0
  137. package/dist/tools/sheets/duplicateSheet.js +53 -0
  138. package/dist/tools/sheets/formatCells.js +122 -0
  139. package/dist/tools/sheets/freezeRowsAndColumns.js +58 -0
  140. package/dist/tools/sheets/getConditionalFormatting.js +98 -0
  141. package/dist/tools/sheets/getSpreadsheetInfo.js +44 -0
  142. package/dist/tools/sheets/getTable.js +48 -0
  143. package/dist/tools/sheets/groupRows.js +62 -0
  144. package/dist/tools/sheets/index.js +84 -0
  145. package/dist/tools/sheets/insertChart.js +225 -0
  146. package/dist/tools/sheets/listGoogleSheets.js +66 -0
  147. package/dist/tools/sheets/listTables.js +55 -0
  148. package/dist/tools/sheets/protectRange.js +59 -0
  149. package/dist/tools/sheets/readCellFormat.js +143 -0
  150. package/dist/tools/sheets/readSpreadsheet.js +36 -0
  151. package/dist/tools/sheets/renameSheet.js +48 -0
  152. package/dist/tools/sheets/setCellBorders.js +94 -0
  153. package/dist/tools/sheets/setColumnWidths.js +43 -0
  154. package/dist/tools/sheets/setDropdownValidation.js +51 -0
  155. package/dist/tools/sheets/setRowHeights.js +63 -0
  156. package/dist/tools/sheets/ungroupAllRows.js +66 -0
  157. package/dist/tools/sheets/updateTableRange.js +51 -0
  158. package/dist/tools/sheets/writeSpreadsheet.js +45 -0
  159. package/dist/tools/utils/appendMarkdownToGoogleDoc.js +95 -0
  160. package/dist/tools/utils/index.js +8 -0
  161. package/dist/tools/utils/replaceDocumentWithMarkdown.js +161 -0
  162. package/dist/tools/utils/replaceRangeWithMarkdown.js +72 -0
  163. package/dist/types.js +208 -0
  164. package/dist/upstreamAuth.js +62 -0
  165. package/package.json +51 -0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ https://opensource.org/license/MIT
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,567 @@
1
+ # Google Docs, Sheets, Drive, Gmail & Calendar MCP Server
2
+
3
+ [![MCP Toplist](https://mcptoplist.com/badge/glama%2Fa-bonus%2Fgoogle-docs-mcp.svg)](https://mcptoplist.com/server/glama%2Fa-bonus%2Fgoogle-docs-mcp)
4
+
5
+ ![Demo Animation](assets/google.docs.mcp.1.gif)
6
+
7
+ Connect Claude Desktop, Cursor, or any MCP client to your Google Docs, Google Sheets, Google Drive, Gmail, and Google Calendar.
8
+
9
+ ---
10
+
11
+ ## Quick Start
12
+
13
+ ### 1. Create a Google Cloud OAuth Client
14
+
15
+ 1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
16
+ 2. Create or select a project
17
+ 3. Enable the **Google Docs API**, **Google Sheets API**, **Google Drive API**, **Gmail API**, and **Google Calendar API**
18
+ 4. Configure the **OAuth consent screen** (External, add your email as a test user, and add the `gmail.modify` and `calendar.events` scopes alongside the Docs/Sheets/Drive scopes)
19
+ 5. Create an **OAuth client ID** (Desktop app type)
20
+ 6. Copy the **Client ID** and **Client Secret** from the confirmation screen
21
+
22
+ > Need more detail? See [step-by-step instructions](#google-cloud-setup-details) at the bottom of this page.
23
+
24
+ ### 2. Authorize
25
+
26
+ ```bash
27
+ GOOGLE_CLIENT_ID="your-client-id" \
28
+ GOOGLE_CLIENT_SECRET="your-client-secret" \
29
+ npx -y @a-bonus/google-docs-mcp auth
30
+ ```
31
+
32
+ This opens your browser for Google authorization. After you approve, the refresh token is saved to `~/.config/google-docs-mcp/token.json`.
33
+
34
+ ### 3. Add to Your MCP Client
35
+
36
+ **Claude Desktop / Cursor / Windsurf:**
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "google-docs": {
42
+ "command": "npx",
43
+ "args": ["-y", "@a-bonus/google-docs-mcp"],
44
+ "env": {
45
+ "GOOGLE_CLIENT_ID": "your-client-id",
46
+ "GOOGLE_CLIENT_SECRET": "your-client-secret"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ The server starts automatically when your MCP client needs it.
54
+
55
+ ### Remote Deployment (Cloud Run)
56
+
57
+ Deploy once for your team -- no local installs required. The server uses MCP OAuth 2.1 so your MCP client handles authentication automatically.
58
+
59
+ ```bash
60
+ gcloud run deploy google-docs-mcp \
61
+ --source . \
62
+ --region europe-west3 \
63
+ --port 8080 \
64
+ --allow-unauthenticated \
65
+ --set-env-vars "^|^MCP_TRANSPORT=httpStream|BASE_URL=https://your-service.run.app|GOOGLE_CLIENT_ID=...|GOOGLE_CLIENT_SECRET=...|TOKEN_STORE=firestore|JWT_SIGNING_KEY=your-secret-key"
66
+ ```
67
+
68
+ Then each user just adds the URL to their MCP client -- no npx, no tokens, no local setup:
69
+
70
+ ```json
71
+ {
72
+ "mcpServers": {
73
+ "google-docs": {
74
+ "type": "streamableHttp",
75
+ "url": "https://your-service.run.app/mcp"
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ Your MCP client will prompt for Google sign-in on first connection. See [Remote Deployment](#remote-deployment) for details.
82
+
83
+ ---
84
+
85
+ ## What Can It Do?
86
+
87
+ Tools across Google Docs, Sheets, and Drive:
88
+
89
+ ### Google Docs
90
+
91
+ | Tool | Description |
92
+ | ----------------------------- | --------------------------------------------------------------------- |
93
+ | `readDocument` | Read content as plain text, JSON, or markdown |
94
+ | `appendText` | Append text to a document |
95
+ | `insertText` | Insert text at a specific position |
96
+ | `deleteRange` | Remove content by index range |
97
+ | `modifyText` | Replace, prepend, or transform text in a document |
98
+ | `findAndReplace` | Find and replace text across a document |
99
+ | `findElement` | Locate text occurrences (with index ranges) or list paragraphs/tables |
100
+ | `listTabs` | List all tabs in a multi-tab document |
101
+ | `addTab` | Add a new tab to a document |
102
+ | `renameTab` | Rename a document tab |
103
+ | `replaceDocumentWithMarkdown` | Replace entire document content from markdown |
104
+ | `replaceRangeWithMarkdown` | Replace a specific range with markdown content |
105
+ | `appendMarkdown` | Append markdown-formatted content |
106
+ | `applyTextStyle` | Bold, italic, colors, font size, links |
107
+ | `applyParagraphStyle` | Alignment, spacing, indentation |
108
+ | `insertTable` | Create an empty table |
109
+ | `insertTableWithData` | Create a table pre-filled with data |
110
+ | `insertPageBreak` | Insert page breaks |
111
+ | `insertSectionBreak` | Insert section break (NEXT_PAGE or CONTINUOUS) |
112
+ | `updateSectionStyle` | Update section style: flip orientation, margins |
113
+ | `insertImage` | Insert images from URLs or local files |
114
+
115
+ ### Comments
116
+
117
+ | Tool | Description |
118
+ | ---------------- | -------------------------------------- |
119
+ | `listComments` | View all comments with author and date |
120
+ | `getComment` | Get a specific comment with replies |
121
+ | `addComment` | Create a comment anchored to text |
122
+ | `replyToComment` | Reply to an existing comment |
123
+ | `resolveComment` | Mark a comment as resolved |
124
+ | `deleteComment` | Remove a comment |
125
+
126
+ ### Google Sheets
127
+
128
+ | Tool | Description |
129
+ | ----------------------------- | --------------------------------------------------------------------- |
130
+ | `readSpreadsheet` | Read data from a range (A1 notation) |
131
+ | `writeSpreadsheet` | Write data to a range |
132
+ | `batchWrite` | Write to multiple ranges in one call |
133
+ | `appendRows` | Add rows to a sheet |
134
+ | `clearRange` | Clear cell values |
135
+ | `createSpreadsheet` | Create a new spreadsheet |
136
+ | `addSheet` | Add a sheet/tab |
137
+ | `deleteSheet` | Remove a sheet/tab |
138
+ | `duplicateSheet` | Duplicate a sheet within the same spreadsheet |
139
+ | `copySheetTo` | Copy a sheet into another spreadsheet |
140
+ | `renameSheet` | Rename a sheet/tab |
141
+ | `getSpreadsheetInfo` | Get metadata and sheet list |
142
+ | `listSpreadsheets` | Find spreadsheets |
143
+ | `formatCells` | Bold, colors, alignment, vertical alignment, wrap strategy on ranges |
144
+ | `copyFormatting` | Copy formatting from one range to another |
145
+ | `readCellFormat` | Read formatting details of a cell range |
146
+ | `setCellBorders` | Set per-side borders (top/bottom/left/right/inner) with style & color |
147
+ | `freezeRowsAndColumns` | Pin header rows/columns |
148
+ | `setDropdownValidation` | Add/remove dropdown lists on cells |
149
+ | `setColumnWidths` | Set column widths in pixels |
150
+ | `setRowHeights` | Set row heights in pixels |
151
+ | `autoResizeColumns` | Auto-fit column widths to content |
152
+ | `autoResizeRows` | Auto-fit row heights to content |
153
+ | `protectRange` | Lock a range or entire sheet (warning-only or fully locked) |
154
+ | `addConditionalFormatting` | Add a conditional formatting rule |
155
+ | `getConditionalFormatting` | List conditional formatting rules with their index (JSON) |
156
+ | `deleteConditionalFormatting` | Delete conditional formatting rules by index |
157
+ | `groupRows` | Group rows for collapsible sections |
158
+ | `ungroupAllRows` | Remove all row groupings |
159
+ | `createSheetsComment` | Create a spreadsheet comment, optionally with a direct cell link |
160
+ | `createSheetsCellNote` | Create a native cell note attached to a cell or range |
161
+ | `insertChart` | Create a chart from data |
162
+ | `deleteChart` | Remove a chart |
163
+
164
+ ### Google Sheets Tables
165
+
166
+ | Tool | Description |
167
+ | ------------------ | ---------------------------------------------- |
168
+ | `createTable` | Create a new named table with column types |
169
+ | `listTables` | List all tables in a spreadsheet or sheet |
170
+ | `getTable` | Get detailed table metadata by name or ID |
171
+ | `deleteTable` | Delete a table (optionally clear data) |
172
+ | `updateTableRange` | Modify table dimensions (add/remove rows/cols) |
173
+ | `appendTableRows` | Append rows to a table (table-aware insertion) |
174
+
175
+ ### Google Drive
176
+
177
+ | Tool | Description |
178
+ | ---------------------------- | ------------------------------------------- |
179
+ | `listDocuments` | List documents, optionally filtered by date |
180
+ | `searchDocuments` | Search by name or content |
181
+ | `getDocumentInfo` | Get document metadata |
182
+ | `createDocument` | Create a new document |
183
+ | `createDocumentFromTemplate` | Create from an existing template |
184
+ | `createFolder` | Create a folder |
185
+ | `listFolderContents` | List folder contents |
186
+ | `getFolderInfo` | Get folder metadata |
187
+ | `moveFile` | Move a file to another folder |
188
+ | `copyFile` | Duplicate a file |
189
+ | `renameFile` | Rename a file |
190
+ | `deleteFile` | Move to trash or permanently delete |
191
+ | `listDriveFiles` | List any file type in Drive with filters |
192
+ | `searchDriveFiles` | Search all Drive files by name or content |
193
+ | `downloadFile` | Download a file's content |
194
+
195
+ ### Gmail
196
+
197
+ | Tool | Description |
198
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
199
+ | `listMessages` | List or search messages using Gmail query syntax (`is:unread`, `from:`, `newer_than:`, etc.) |
200
+ | `getMessage` | Fetch a single message with decoded headers, plain-text body, HTML body, and attachment metadata |
201
+ | `sendEmail` | Send a plain-text email. Supports cc/bcc and threaded replies via `replyToMessageId` |
202
+ | `trashMessage` | Move a message to Trash (reversible, same as clicking Delete in the Gmail UI) |
203
+ | `modifyMessageLabels` | Add or remove labels on a message — use to star, archive (remove `INBOX`), mark read (remove `UNREAD`) |
204
+ | `listLabels` | List all system and custom labels with their IDs |
205
+ | `createDraft` | Compose a draft instead of sending immediately — for compose/review/send workflows |
206
+ | `listDrafts` | List existing drafts with recipient, subject, and snippet |
207
+ | `getDraft` | Fetch a single draft with full headers and body |
208
+ | `updateDraft` | Replace the contents of an existing draft (full replace, not patch) |
209
+ | `sendDraft` | Send an existing draft by ID |
210
+ | `deleteDraft` | Permanently delete a draft (not moved to Trash — gone) |
211
+ | `triageInbox` | **Composite:** fetch unread messages with content + heuristic flags (newsletter, meeting, action) for one-shot inbox triage |
212
+
213
+ ### Google Calendar
214
+
215
+ | Tool | Description |
216
+ | --------------- | -------------------------------------------------------------------------------------------------- |
217
+ | `listEvents` | List or search events with `q`, `timeMin`, `timeMax`, `maxResults` (defaults to primary calendar) |
218
+ | `createEvent` | Create an event with title, start/end, description, location, attendees, optional Google Meet link |
219
+ | `updateEvent` | PATCH-style update — only the fields you pass change. Use to reschedule, retitle, change attendees |
220
+ | `deleteEvent` | Permanently delete an event. Optional `sendUpdates` emails cancellations to attendees |
221
+ | `quickAddEvent` | Natural-language event creation: `"Lunch with Sarah tomorrow 12pm"` — Google parses the rest |
222
+
223
+ ### Apps Script
224
+
225
+ Automate the automation: create and edit the Apps Script behind a Doc or Sheet — `onEdit` triggers, custom menus, scheduled jobs — instead of telling the user to paste code into the editor by hand.
226
+
227
+ | Tool | Description |
228
+ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
229
+ | `createAppsScriptProject` | Create a project, optionally **bound** to a Doc/Sheet/Slides/Form via `parentId`, and write its initial files in the same call |
230
+ | `getAppsScriptContent` | Read a project's files — pass `includeSource: false` for a quick listing, or `versionNumber` to read a saved version |
231
+ | `updateAppsScriptContent` | Write files. `merge` (default) replaces same-named files and keeps the rest; `replace` makes the project exactly the files you pass |
232
+
233
+ **Requires two things beyond the usual setup:**
234
+
235
+ 1. The Apps Script API must be enabled **per Google account** at <https://script.google.com/home/usersettings> — it is off by default, and a 403 with "Apps Script API" in the message means this step was missed.
236
+ 2. The `script.projects` scope, which is included in `SCOPES`. Existing installs must re-authorize once to pick it up.
237
+
238
+ ---
239
+
240
+ ## Usage Examples
241
+
242
+ ### Google Docs
243
+
244
+ ```
245
+ "Read document ABC123 as markdown"
246
+ "Append 'Meeting notes for today' to document ABC123"
247
+ "Make the text 'Important' bold and red in document ABC123"
248
+ "Replace the entire document with this markdown: # Title\n\nNew content here"
249
+ "Insert a 3x4 table at index 50 in document ABC123"
250
+ ```
251
+
252
+ ### Google Sheets
253
+
254
+ ```
255
+ "Read range A1:D10 from spreadsheet XYZ789"
256
+ "Write [[Name, Score], [Alice, 95], [Bob, 87]] to range A1 in spreadsheet XYZ789"
257
+ "Create a new spreadsheet titled 'Q1 Report'"
258
+ "Format row 1 as bold with a light blue background in spreadsheet XYZ789"
259
+ "Freeze the first row in spreadsheet XYZ789"
260
+ "Add a dropdown with options [Open, In Progress, Done] to range C2:C100"
261
+ "Create a table named 'Tasks' in range A1:D10 with columns: Task (TEXT), Status (DROPDOWN: 'Not Started','In Progress','Done'), Priority (NUMBER)"
262
+ "Add a medium solid border around A1:D10 in spreadsheet XYZ789"
263
+ "Protect the header row so collaborators can't accidentally edit it"
264
+ "Auto-fit row heights for rows 2–50 after wrapping text"
265
+ ```
266
+
267
+ ### Google Drive
268
+
269
+ ```
270
+ "List my 10 most recent Google Docs"
271
+ "Search for documents containing 'project proposal'"
272
+ "Create a folder called 'Meeting Notes' and move document ABC123 into it"
273
+ ```
274
+
275
+ ### Gmail
276
+
277
+ ```
278
+ "Show me my 20 most recent unread emails"
279
+ "Search Gmail for messages from alice@example.com in the last 7 days"
280
+ "Read the full body of message ID 18c3f4a2b1d9"
281
+ "Send an email to bob@example.com with the subject 'Weekly update' and this body..."
282
+ "Reply to message 18c3f4a2b1d9 with 'Thanks, confirmed.'"
283
+ "Star message 18c3f4a2b1d9 and archive it"
284
+ "Move message 18c3f4a2b1d9 to Trash"
285
+ "List all my Gmail labels"
286
+ "Draft a reply to that email but don't send it yet — let me review first"
287
+ "Show me my drafts, then send the one to bob@"
288
+ "Triage my unread inbox: tell me which 20 emails need attention and which are noise"
289
+ ```
290
+
291
+ ### Google Calendar
292
+
293
+ ```
294
+ "What's on my calendar this week?"
295
+ "Create an event titled 'Project review' tomorrow from 2pm to 3pm Pacific time"
296
+ "Quick add: lunch with Alex Friday 12:30"
297
+ "Reschedule event abc123 to next Monday at 10am"
298
+ "Delete the 'Standup' event tomorrow"
299
+ "List all events on my calendar between April 15 and April 22"
300
+ "Schedule a 30-minute meeting with bob@example.com next Wednesday at 11am with a Google Meet link"
301
+ ```
302
+
303
+ ### Markdown Workflow
304
+
305
+ The server supports a full round-trip markdown workflow:
306
+
307
+ 1. Read a document as markdown: `readDocument` with `format='markdown'`
308
+ 2. Edit the markdown locally
309
+ 3. Push changes back: `replaceDocumentWithMarkdown`
310
+
311
+ Supported: headings, bold, italic, strikethrough, links, bullet/numbered lists, horizontal rules.
312
+
313
+ ### Live Docs Verification
314
+
315
+ The repository includes an opt-in live integration test for `cloneTable` against the real Google Docs API. It is skipped by default.
316
+
317
+ Requirements:
318
+
319
+ - valid `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`
320
+ - an authorized token already stored via `npx -y @a-bonus/google-docs-mcp auth`
321
+
322
+ Run it with:
323
+
324
+ ```bash
325
+ GOOGLE_DOCS_LIVE_TESTS=1 npm run test:live:docs
326
+ ```
327
+
328
+ This test creates temporary source/target Google Docs, verifies `cloneTable`, and then deletes the test files.
329
+
330
+ ---
331
+
332
+ ## Remote Deployment
333
+
334
+ Deploy the server centrally on Google Cloud Run (or any container host) so your team can use it without local installs. The server uses **MCP OAuth 2.1** with FastMCP's built-in `GoogleProvider` -- MCP clients handle the auth flow automatically.
335
+
336
+ Visit the server root URL (`/`) for setup instructions and a ready-to-copy client config.
337
+
338
+ ### Environment Variables
339
+
340
+ | Variable | Description |
341
+ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
342
+ | `MCP_TRANSPORT` | Set to `httpStream` to enable remote mode (default: `stdio`) |
343
+ | `BASE_URL` | Public URL of the deployed server (required for OAuth redirects) |
344
+ | `GOOGLE_CLIENT_ID` | OAuth client ID (Web application type) |
345
+ | `GOOGLE_CLIENT_SECRET` | OAuth client secret |
346
+ | `MCP_TOOL_GROUPS` | Optional comma-separated tool groups to register: `docs`, `drive`, `sheets`, `utils`, `gmail`, `calendar`, `script`, or `all` |
347
+ | `ALLOWED_DOMAINS` | Comma-separated list of allowed Google Workspace domains (optional) |
348
+ | `PORT` | HTTP port (default: `8080`) |
349
+ | `TOKEN_STORE` | Set to `firestore` for persistent token storage (default: in-memory) |
350
+ | `JWT_SIGNING_KEY` | Fixed signing key so tokens survive restarts (auto-generated if not set) |
351
+ | `REFRESH_TOKEN_TTL` | Refresh token lifetime in seconds (default: `2592000` / 30 days) |
352
+ | `GCLOUD_PROJECT` | GCP project ID for Firestore (required when `TOKEN_STORE=firestore`) |
353
+ | `MCP_STATELESS` | Set to `true` for serverless deployments (Cloud Run, etc.) — disables session tracking to survive scale-to-zero |
354
+ | `GOOGLE_DOCS_MCP_DOWNLOAD_ROOTS` | Directories `downloadFile` may write to in stdio mode, delimited by `:` (`;` on Windows). Defaults to the working directory |
355
+
356
+ ### Setup
357
+
358
+ 1. Create a GCP project and enable Docs, Sheets, and Drive APIs
359
+ 2. Create an OAuth client (**Web application** type, not Desktop)
360
+ 3. Set the authorized redirect URI to `{BASE_URL}/oauth/callback`
361
+ 4. Deploy to Cloud Run:
362
+
363
+ ```bash
364
+ gcloud run deploy google-docs-mcp \
365
+ --source . \
366
+ --region europe-west3 \
367
+ --port 8080 \
368
+ --allow-unauthenticated \
369
+ --set-env-vars "^|^MCP_TRANSPORT=httpStream|BASE_URL=https://your-service.run.app|ALLOWED_DOMAINS=yourdomain.com|GOOGLE_CLIENT_ID=...|GOOGLE_CLIENT_SECRET=...|TOKEN_STORE=firestore|JWT_SIGNING_KEY=your-secret-key"
370
+ ```
371
+
372
+ > **Note:** The `^|^` prefix changes the env var delimiter from `,` to `|` because `ALLOWED_DOMAINS` contains commas.
373
+
374
+ ### How It Works
375
+
376
+ - By default, OAuth sessions are stored in memory and lost on restart
377
+ - For production, set `TOKEN_STORE=firestore` and `JWT_SIGNING_KEY` for persistent auth across deploys and cold starts
378
+ - On serverless platforms (Cloud Run, etc.), set `MCP_STATELESS=true` — MCP sessions are held in memory, so scale-to-zero wipes them. Stateless mode disables session tracking entirely; each request authenticates independently via the JWT/Firestore token flow
379
+ - `ALLOWED_DOMAINS` restricts access to specific Google Workspace domains
380
+ - Access tokens refresh automatically; inactive sessions expire after 30 days
381
+ - Users can revoke access at any time via [Google Account permissions](https://myaccount.google.com/permissions)
382
+
383
+ ### Updating Your Deployment
384
+
385
+ Merging changes to `main` does **not** automatically update your Cloud Run service. Each deployment is independent — you need to redeploy manually when you want new features or fixes.
386
+
387
+ **To update:**
388
+
389
+ 1. Pull the latest code:
390
+
391
+ ```bash
392
+ git pull origin main
393
+ ```
394
+
395
+ 2. Redeploy to Cloud Run:
396
+ ```bash
397
+ gcloud run deploy your-service-name --source . --region your-region
398
+ ```
399
+ Your existing environment variables are preserved — no need to pass `--set-env-vars` again.
400
+
401
+ **When to redeploy:**
402
+
403
+ - **Bug fixes and security patches** — redeploy as soon as possible
404
+ - **New features** — redeploy at your convenience
405
+ - **Breaking changes** — check the release notes before redeploying
406
+
407
+ You can check your current version against the latest release on the [releases page](https://github.com/a-bonus/google-docs-mcp/releases).
408
+
409
+ ---
410
+
411
+ ## Authentication Options
412
+
413
+ ### OAuth (Default)
414
+
415
+ Pass your Google Cloud OAuth client credentials as environment variables:
416
+
417
+ | Variable | Description |
418
+ | ---------------------- | --------------------------------------------- |
419
+ | `GOOGLE_CLIENT_ID` | OAuth client ID from Google Cloud Console |
420
+ | `GOOGLE_CLIENT_SECRET` | OAuth client secret from Google Cloud Console |
421
+
422
+ ### Service Account (Enterprise)
423
+
424
+ For Google Workspace with domain-wide delegation:
425
+
426
+ | Variable | Description |
427
+ | ------------------------- | ------------------------------------------- |
428
+ | `SERVICE_ACCOUNT_PATH` | Path to the service account JSON key file |
429
+ | `GOOGLE_IMPERSONATE_USER` | Email of the user to impersonate (optional) |
430
+
431
+ ```json
432
+ {
433
+ "mcpServers": {
434
+ "google-docs": {
435
+ "command": "npx",
436
+ "args": ["-y", "@a-bonus/google-docs-mcp"],
437
+ "env": {
438
+ "SERVICE_ACCOUNT_PATH": "/path/to/service-account-key.json",
439
+ "GOOGLE_IMPERSONATE_USER": "user@yourdomain.com"
440
+ }
441
+ }
442
+ }
443
+ }
444
+ ```
445
+
446
+ ### Token Storage
447
+
448
+ OAuth refresh tokens are stored in `~/.config/google-docs-mcp/token.json` (respects `XDG_CONFIG_HOME`). OAuth client IDs and client secrets are not stored in the token file. To re-authorize, run the `auth` command again or delete the token file.
449
+
450
+ ### Multiple Google Accounts
451
+
452
+ Set `GOOGLE_MCP_PROFILE` to store tokens in a profile-specific subdirectory. This allows using different Google accounts for different projects:
453
+
454
+ | Variable | Description |
455
+ | -------------------- | -------------------------------------------------- |
456
+ | `GOOGLE_MCP_PROFILE` | Profile name for isolated token storage (optional) |
457
+
458
+ ```json
459
+ {
460
+ "mcpServers": {
461
+ "google-docs": {
462
+ "command": "npx",
463
+ "args": ["-y", "@a-bonus/google-docs-mcp"],
464
+ "env": {
465
+ "GOOGLE_CLIENT_ID": "...",
466
+ "GOOGLE_CLIENT_SECRET": "...",
467
+ "GOOGLE_MCP_PROFILE": "work"
468
+ }
469
+ }
470
+ }
471
+ }
472
+ ```
473
+
474
+ Tokens are stored per profile:
475
+
476
+ ```
477
+ ~/.config/google-docs-mcp/
478
+ ├── token.json # default (no profile)
479
+ ├── work/token.json # GOOGLE_MCP_PROFILE=work
480
+ ├── personal/token.json # GOOGLE_MCP_PROFILE=personal
481
+ ```
482
+
483
+ Without `GOOGLE_MCP_PROFILE`, behavior is unchanged.
484
+
485
+ ---
486
+
487
+ ## Known Limitations
488
+
489
+ - **Comment anchoring:** Programmatically created comments appear in the comment list but aren't visibly anchored to text in the Google Docs UI. This is a Google Drive API limitation.
490
+ - **Sheets comment anchoring:** API-created spreadsheet comments can store anchor metadata, but Google Workspace editors treat Drive API anchors as unanchored comments. Use `createSheetsComment` with `includeCellLink=true` for a clickable link to the target cell, or `createSheetsCellNote` when the review text must be attached to the cell itself.
491
+ - **Comment resolution:** Resolved status may not persist in the Google Docs UI.
492
+ - **Converted documents:** Docs converted from Word may not support all API operations.
493
+ - **Markdown images:** Not yet supported in the markdown-to-Docs conversion.
494
+ - **Deeply nested lists:** Lists with 3+ nesting levels may have formatting quirks.
495
+ - **Gmail hard delete:** `trashMessage` moves messages to Trash (reversible). Permanent deletion requires the broader `https://mail.google.com/` scope and is not exposed in v0.1.
496
+ - **Gmail attachments:** `getMessage` returns attachment metadata but does not download attachment bytes yet.
497
+ - **Gmail HTML email send:** `sendEmail` sends plain-text only. For HTML bodies, paste HTML into the `body` field — it will be delivered as text, not rendered.
498
+ - **Calendar scope:** `calendar.events` permits event CRUD on existing calendars but cannot create or delete entire calendars themselves.
499
+ - **Apps Script API is off by default:** every account must enable it once at <https://script.google.com/home/usersettings>. The tools cannot turn it on for you.
500
+ - **Apps Script deployments:** the tools edit project source. Creating versions, deployments and installable triggers is not exposed yet — simple triggers such as `onEdit` and `onOpen` work without any of that.
501
+ - **Calendar recurring events:** `updateEvent` and `deleteEvent` modify the entire recurring series unless you target a specific instance ID returned by `listEvents` with `singleEvents=true`.
502
+
503
+ ## Troubleshooting
504
+
505
+ - **Server won't start:**
506
+ - Verify `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` are set in the `env` block of your MCP config.
507
+ - Try running manually: `npx @a-bonus/google-docs-mcp` and check stderr for errors.
508
+ - **Authorization errors:**
509
+ - Ensure Docs, Sheets, Drive, Gmail, and Calendar APIs are enabled in Google Cloud Console.
510
+ - Confirm your email is listed as a Test User on the OAuth consent screen and that all required scopes (Docs, Sheets, Drive, `gmail.modify`, `calendar.events`) are added to the consent screen.
511
+ - Re-authorize: `npx @a-bonus/google-docs-mcp auth`
512
+ - Delete `~/.config/google-docs-mcp/token.json` and re-authorize if upgrading — Gmail and Calendar scopes were added in later versions, so existing tokens must be refreshed.
513
+ - Remote (Cloud Run) users must sign out and sign back in from their MCP client so Google reissues consent with the new scope list.
514
+ - **Tab errors:**
515
+ - Use `listTabs` to see available tab IDs.
516
+ - Omit `tabId` for single-tab documents.
517
+ - **"Page not found" on claude.ai during OAuth sign-in (remote deployments):**
518
+ - Symptom: clicking Connect on a custom MCP connector lands on the Claude "Page not found" page instead of the Google sign-in screen.
519
+ - Cause: Cloud Run cold start. The first request to an idle service times out before the container finishes spinning up, and Claude routes the failed redirect to its 404 page.
520
+ - Workaround: hard-refresh the page (`Cmd+Shift+R` on macOS, `Ctrl+Shift+R` on Windows/Linux). The second request hits a now-warm instance and the OAuth flow proceeds normally.
521
+ - Permanent fix: set `--min-instances=1` on your Cloud Run service to keep one instance always warm (`gcloud run services update <service> --region <region> --min-instances=1`). Costs ~$2–3/month for the memory reservation.
522
+ - **Re-authenticated unexpectedly after a redeploy (remote deployments):**
523
+ - Cause: `JWT_SIGNING_KEY` is auto-generated on each container start, so redeploys invalidate all previously issued sessions.
524
+ - Fix: set a stable `JWT_SIGNING_KEY` env var on the Cloud Run service so it survives restarts: `gcloud run services update <service> --region <region> --update-env-vars JWT_SIGNING_KEY=$(openssl rand -hex 32)`. Sessions minted after this change will survive future redeploys.
525
+ - **High CPU with multiple MCP sessions:** Some clients call `tools/list` very often. FastMCP otherwise recomputes JSON Schema for every tool on every request, which can pin a CPU core per process. This server precomputes the payload once before stdio starts and replaces the `tools/list` handler with a cached snapshot. If you still see sustained load, capture a few seconds with `sample <pid> 1 10` (macOS) or `node --cpu-prof` and report it.
526
+
527
+ ---
528
+
529
+ ## Google Cloud Setup Details
530
+
531
+ <details>
532
+ <summary>Step-by-step Google Cloud Console instructions</summary>
533
+
534
+ > For **remote deployment**, create an OAuth client of type **Web application** (not Desktop app). Use Desktop app only for local stdio usage.
535
+
536
+ 1. **Go to Google Cloud Console:** Open [console.cloud.google.com](https://console.cloud.google.com/)
537
+ 2. **Create or Select a Project:** Click the project dropdown > "NEW PROJECT". Name it (e.g., "MCP Docs Server") and click "CREATE".
538
+ 3. **Enable APIs:**
539
+ - Navigate to "APIs & Services" > "Library"
540
+ - Search for and enable: **Google Docs API**, **Google Sheets API**, **Google Drive API**, **Gmail API**, **Google Calendar API**
541
+ 4. **Configure OAuth Consent Screen:**
542
+ - Go to "APIs & Services" > "OAuth consent screen"
543
+ - Choose "External" and click "CREATE"
544
+ - Fill in: App name, User support email, Developer contact email
545
+ - Click "SAVE AND CONTINUE"
546
+ - Add scopes: `documents`, `spreadsheets`, `drive`, `gmail.modify`, `calendar.events`
547
+ - Click "SAVE AND CONTINUE"
548
+ - Add your Google email as a Test User
549
+ - Click "SAVE AND CONTINUE"
550
+ 5. **Create Credentials:**
551
+ - Go to "APIs & Services" > "Credentials"
552
+ - Click "+ CREATE CREDENTIALS" > "OAuth client ID"
553
+ - Application type: "Desktop app"
554
+ - Click "CREATE"
555
+ - Copy the **Client ID** and **Client Secret**
556
+
557
+ </details>
558
+
559
+ ---
560
+
561
+ ## Contributing
562
+
563
+ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, architecture overview, and guidelines.
564
+
565
+ ## License
566
+
567
+ MIT -- see [LICENSE](LICENSE) for details.