workspace-operator-mcp 1.12.0__tar.gz

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 (61) hide show
  1. workspace_operator_mcp-1.12.0/LICENSE +21 -0
  2. workspace_operator_mcp-1.12.0/PKG-INFO +546 -0
  3. workspace_operator_mcp-1.12.0/README.md +511 -0
  4. workspace_operator_mcp-1.12.0/pyproject.toml +51 -0
  5. workspace_operator_mcp-1.12.0/setup.cfg +4 -0
  6. workspace_operator_mcp-1.12.0/tests/test_calendar_writes.py +66 -0
  7. workspace_operator_mcp-1.12.0/tests/test_cli.py +136 -0
  8. workspace_operator_mcp-1.12.0/tests/test_composite.py +140 -0
  9. workspace_operator_mcp-1.12.0/tests/test_deck.py +97 -0
  10. workspace_operator_mcp-1.12.0/tests/test_doc_tables.py +97 -0
  11. workspace_operator_mcp-1.12.0/tests/test_docs.py +212 -0
  12. workspace_operator_mcp-1.12.0/tests/test_files.py +67 -0
  13. workspace_operator_mcp-1.12.0/tests/test_formatting.py +132 -0
  14. workspace_operator_mcp-1.12.0/tests/test_gmail_threads.py +104 -0
  15. workspace_operator_mcp-1.12.0/tests/test_http.py +91 -0
  16. workspace_operator_mcp-1.12.0/tests/test_profiles.py +90 -0
  17. workspace_operator_mcp-1.12.0/tests/test_prompts.py +51 -0
  18. workspace_operator_mcp-1.12.0/tests/test_report.py +142 -0
  19. workspace_operator_mcp-1.12.0/tests/test_runtime.py +100 -0
  20. workspace_operator_mcp-1.12.0/tests/test_script_run.py +51 -0
  21. workspace_operator_mcp-1.12.0/tests/test_slides.py +65 -0
  22. workspace_operator_mcp-1.12.0/tests/test_text.py +145 -0
  23. workspace_operator_mcp-1.12.0/tests/test_workspace_search.py +89 -0
  24. workspace_operator_mcp-1.12.0/workspace_operator/__init__.py +4 -0
  25. workspace_operator_mcp-1.12.0/workspace_operator/__main__.py +5 -0
  26. workspace_operator_mcp-1.12.0/workspace_operator/auth.py +46 -0
  27. workspace_operator_mcp-1.12.0/workspace_operator/cli.py +314 -0
  28. workspace_operator_mcp-1.12.0/workspace_operator/config.py +150 -0
  29. workspace_operator_mcp-1.12.0/workspace_operator/handlers/__init__.py +157 -0
  30. workspace_operator_mcp-1.12.0/workspace_operator/handlers/apps_script.py +279 -0
  31. workspace_operator_mcp-1.12.0/workspace_operator/handlers/calendar.py +267 -0
  32. workspace_operator_mcp-1.12.0/workspace_operator/handlers/calendar_writes.py +192 -0
  33. workspace_operator_mcp-1.12.0/workspace_operator/handlers/compare.py +243 -0
  34. workspace_operator_mcp-1.12.0/workspace_operator/handlers/deck.py +251 -0
  35. workspace_operator_mcp-1.12.0/workspace_operator/handlers/digest.py +216 -0
  36. workspace_operator_mcp-1.12.0/workspace_operator/handlers/doc_tables.py +246 -0
  37. workspace_operator_mcp-1.12.0/workspace_operator/handlers/docs.py +164 -0
  38. workspace_operator_mcp-1.12.0/workspace_operator/handlers/drive.py +288 -0
  39. workspace_operator_mcp-1.12.0/workspace_operator/handlers/files.py +293 -0
  40. workspace_operator_mcp-1.12.0/workspace_operator/handlers/formatting.py +378 -0
  41. workspace_operator_mcp-1.12.0/workspace_operator/handlers/gmail.py +486 -0
  42. workspace_operator_mcp-1.12.0/workspace_operator/handlers/gmail_threads.py +329 -0
  43. workspace_operator_mcp-1.12.0/workspace_operator/handlers/report.py +368 -0
  44. workspace_operator_mcp-1.12.0/workspace_operator/handlers/search.py +301 -0
  45. workspace_operator_mcp-1.12.0/workspace_operator/handlers/sheets.py +446 -0
  46. workspace_operator_mcp-1.12.0/workspace_operator/handlers/slides.py +251 -0
  47. workspace_operator_mcp-1.12.0/workspace_operator/handlers/template.py +147 -0
  48. workspace_operator_mcp-1.12.0/workspace_operator/http_transport.py +163 -0
  49. workspace_operator_mcp-1.12.0/workspace_operator/logging.py +32 -0
  50. workspace_operator_mcp-1.12.0/workspace_operator/operations.py +57 -0
  51. workspace_operator_mcp-1.12.0/workspace_operator/profiles.py +146 -0
  52. workspace_operator_mcp-1.12.0/workspace_operator/runtime.py +123 -0
  53. workspace_operator_mcp-1.12.0/workspace_operator/security.py +24 -0
  54. workspace_operator_mcp-1.12.0/workspace_operator/server.py +2572 -0
  55. workspace_operator_mcp-1.12.0/workspace_operator/text.py +145 -0
  56. workspace_operator_mcp-1.12.0/workspace_operator_mcp.egg-info/PKG-INFO +546 -0
  57. workspace_operator_mcp-1.12.0/workspace_operator_mcp.egg-info/SOURCES.txt +59 -0
  58. workspace_operator_mcp-1.12.0/workspace_operator_mcp.egg-info/dependency_links.txt +1 -0
  59. workspace_operator_mcp-1.12.0/workspace_operator_mcp.egg-info/entry_points.txt +2 -0
  60. workspace_operator_mcp-1.12.0/workspace_operator_mcp.egg-info/requires.txt +7 -0
  61. workspace_operator_mcp-1.12.0/workspace_operator_mcp.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artoxem
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,546 @@
1
+ Metadata-Version: 2.4
2
+ Name: workspace-operator-mcp
3
+ Version: 1.12.0
4
+ Summary: Deep Google Workspace automation for AI agents — Apps Script execution, local file transfer, Slides, document templates, cross-product search, and tool profiles
5
+ Author: Artoxem
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Artoxem/workspace-operator
8
+ Project-URL: Repository, https://github.com/Artoxem/workspace-operator
9
+ Project-URL: Documentation, https://github.com/Artoxem/workspace-operator#readme
10
+ Project-URL: Changelog, https://github.com/Artoxem/workspace-operator/tree/master/docs
11
+ Project-URL: Issues, https://github.com/Artoxem/workspace-operator/issues
12
+ Keywords: mcp,model-context-protocol,google,gmail,drive,sheets,docs,slides,calendar,apps-script,antigravity,gemini-cli
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Environment :: Console
22
+ Classifier: Operating System :: OS Independent
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: google-api-python-client<3,>=2.100.0
28
+ Requires-Dist: google-auth-httplib2>=0.2.0
29
+ Requires-Dist: google-auth-oauthlib>=1.2.0
30
+ Requires-Dist: mcp<2,>=1.0.0
31
+ Requires-Dist: PyYAML>=6.0
32
+ Requires-Dist: uvicorn>=0.30
33
+ Requires-Dist: starlette>=0.40
34
+ Dynamic: license-file
35
+
36
+ <div align="center">
37
+
38
+ # Workspace Operator
39
+
40
+ **Deep automation for Google Workspace through the Model Context Protocol**
41
+
42
+ Drive · Docs · Sheets · Slides · Gmail · Calendar · Apps Script — one local MCP server
43
+ with 59 tools, staged writes and server-side tool profiles.
44
+
45
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue?logo=python&logoColor=white)](https://www.python.org/)
46
+ [![MCP](https://img.shields.io/badge/MCP-1.x-blueviolet?logo=anthropic&logoColor=white)](https://modelcontextprotocol.io)
47
+ [![Tools](https://img.shields.io/badge/tools-67-informational)](#-tool-catalog)
48
+ [![Antigravity](https://img.shields.io/badge/Antigravity-ready-4285F4?logo=google&logoColor=white)](antigravity/README.md)
49
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
50
+
51
+ [English](README.md) · [Русский](README.ru.md)
52
+
53
+ </div>
54
+
55
+ ---
56
+
57
+ Workspace Operator (formerly *yet-another-google-mcp*) runs on your machine, talks to
58
+ Google over your own OAuth client, and gives an AI agent the operations that turn
59
+ "read my inbox" into "do the work and let me undo it": staged Apps Script updates,
60
+ file transfer in both directions, presentation assembly, sheet formatting, threaded
61
+ replies and attachments.
62
+
63
+ **67 tools · 3 resources · 3 resource templates · 8 prompts**, every tool annotated
64
+ with `readOnlyHint` / `destructiveHint`.
65
+
66
+ Nothing here is tied to a specific account, spreadsheet or document: every tool
67
+ takes identifiers and ranges, and all examples use synthetic data.
68
+
69
+ ## How it compares
70
+
71
+ | | Google Workspace MCP (remote) | **Workspace Operator** |
72
+ |---|---|---|
73
+ | Transport | Streamable HTTP, Google-hosted | **stdio, local** |
74
+ | Availability | Developer Preview | **public, MIT** |
75
+ | Data path | Google MCP endpoint | **your machine, your OAuth client** |
76
+ | Apps Script | — | **create · read · stage · execute · cancel · restore · run function** |
77
+ | Local files | — | **upload, download, export to pdf/docx/xlsx/pptx** |
78
+ | Slides | — | **create, add slides, fill text, insert images** |
79
+ | Sheets formatting | — | **bold, colours, number formats, widths, freeze** |
80
+ | Gmail threads | single messages | **threads, attachments, in-thread replies** |
81
+ | Server-side tool profiles | IAM / admin allow-list | **read-only / read-write / full + allow-deny** |
82
+
83
+ Official servers are the right default for "ask my mail a question". Workspace
84
+ Operator is for "change the workspace and be able to roll it back".
85
+
86
+ ## What you get
87
+
88
+ | Group | Tools | Highlights |
89
+ |---|---:|---|
90
+ | 🔎 Cross-search | 1 | One query over Drive, Gmail and Calendar, merged by date |
91
+ | 📁 Drive | 8 | Search, move, copy, share, permissions, revoke public access |
92
+ | 💾 Drive files | 3 | Upload/download/export, conversion to native Google formats |
93
+ | 📊 Sheets | 12 | Read/write, formatting, filter views, named ranges, CSV |
94
+ | 📄 Docs | 5 | Read (tables included), append, templates, PDF export |
95
+ | 🖼 Slides | 5 | Create decks, add slides, replace text, insert images |
96
+ | ⚙️ Apps Script | 7 | Staged updates with backups, rollback, run functions |
97
+ | 📧 Gmail | 14 | Threads, attachments, replies, drafts, labels, batch delete |
98
+ | 📅 Calendar | 7 | Events, free slots, meetings, update/delete, invite responses |
99
+ | 🧩 Composite | 5 | report from sheet, digest, table diff, template clone, table → deck |
100
+
101
+ Plus 3 resources, 3 resource templates and 8 prompts — see the
102
+ [tool catalog](#-tool-catalog).
103
+
104
+ ## Quick start
105
+
106
+ ### 1 — Install
107
+
108
+ ```bash
109
+ git clone https://github.com/Artoxem/workspace-operator.git
110
+ cd workspace-operator
111
+ python -m venv .venv && .venv\Scripts\activate # Windows
112
+ pip install -r requirements.txt
113
+ ```
114
+
115
+ Python 3.10 or newer. The package is not on PyPI yet — install from source.
116
+
117
+ ### 2 — Google OAuth
118
+
119
+ 1. Create an OAuth 2.0 Client ID (type: **Desktop app**) in
120
+ [Google Cloud Console](https://console.cloud.google.com/).
121
+ 2. Enable these APIs: **Drive, Docs, Sheets, Slides, Gmail, Calendar, Apps Script**.
122
+ 3. Download the client JSON — the server will read it on first run.
123
+
124
+ > Step by step: [docs/AUTH_SETUP.md](docs/AUTH_SETUP.md) · [docs/ru/AUTH_SETUP.md](docs/ru/AUTH_SETUP.md)
125
+ > Upgrading? Slides needs the `.../auth/presentations` scope — re-run the OAuth flow once.
126
+
127
+ ### 3 — Configure
128
+
129
+ ```bash
130
+ cp config.example.yaml config.yaml
131
+ ```
132
+
133
+ ```yaml
134
+ client_secrets_file: "C:\\Users\\you\\.google\\oauth.keys.json"
135
+ token_file: "C:\\Users\\you\\.google\\token.json"
136
+ backup_dir: "C:\\Users\\you\\.google\\backups"
137
+ mcp_auth_token: "MySecretToken_2026!"
138
+ tool_profile: "read-write" # full | read-write | read-only
139
+ ```
140
+
141
+ `mcp_auth_token` is a local activation key, not network security: the server refuses
142
+ to handle tools until it is set. Every key can be overridden by an environment
143
+ variable — see [docs/SECURITY.md](docs/SECURITY.md).
144
+
145
+ ### 4 — Connect a client
146
+
147
+ <details open>
148
+ <summary><strong>Google Antigravity</strong> — App, IDE and CLI share one config</summary>
149
+
150
+ `~/.gemini/config/mcp_config.json` (global) or `<workspace>/.agents/mcp_config.json`:
151
+
152
+ ```json
153
+ {
154
+ "mcpServers": {
155
+ "workspace-operator": {
156
+ "command": "python",
157
+ "args": ["D:\\DEV\\workspace-operator\\server.py"],
158
+ "env": {
159
+ "MCP_CONFIG_FILE": "D:\\DEV\\workspace-operator\\config.yaml",
160
+ "MCP_AUTH_TOKEN": "MySecretToken_2026!",
161
+ "MCP_TOOL_PROFILE": "read-write"
162
+ },
163
+ "timeoutSeconds": 300,
164
+ "tools": { "workspace_search": { "eager": true } }
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ Ready-made file: [antigravity/mcp_config.example.json](antigravity/mcp_config.example.json) ·
171
+ walkthrough: [antigravity/README.md](antigravity/README.md) ·
172
+ Agent Skill: [skills/google-workspace/SKILL.md](skills/google-workspace/SKILL.md)
173
+
174
+ </details>
175
+
176
+ <details>
177
+ <summary><strong>Claude Desktop, Cursor, VS Code, Windsurf</strong></summary>
178
+
179
+ ```json
180
+ {
181
+ "mcpServers": {
182
+ "workspace-operator": {
183
+ "command": "python",
184
+ "args": ["D:\\DEV\\workspace-operator\\server.py"],
185
+ "env": {
186
+ "MCP_CONFIG_FILE": "D:\\DEV\\workspace-operator\\config.yaml",
187
+ "MCP_AUTH_TOKEN": "MySecretToken_2026!"
188
+ }
189
+ }
190
+ }
191
+ }
192
+ ```
193
+
194
+ Per-client paths: [docs/IDE_SETUP.md](docs/IDE_SETUP.md) · [docs/ru/IDE_SETUP.md](docs/ru/IDE_SETUP.md).
195
+
196
+ </details>
197
+
198
+ ## Tool profiles
199
+
200
+ A profile is a server-side allow-list: tools outside it are neither listed nor
201
+ callable. It complements OAuth scopes (what Google allows) and client permissions
202
+ (what the agent may do without asking).
203
+
204
+ | Profile | Tools | Use it for |
205
+ |---|---:|---|
206
+ | `full` | 67 | your own machine, full trust |
207
+ | `read-write` | 59 | daily work — destructive tools hidden |
208
+ | `read-only` | 21 | exploration, demos, shared machines, untrusted agents |
209
+
210
+ ```yaml
211
+ tool_profile: "read-write"
212
+ # Optional, applied on top. Deny wins over allow.
213
+ allowed_tools: ["workspace_search", "find_files", "read_sheet", "read_email"]
214
+ denied_tools: ["send_email", "share_file"]
215
+ ```
216
+
217
+ Environment overrides: `MCP_TOOL_PROFILE`, `MCP_ALLOWED_TOOLS`, `MCP_DENIED_TOOLS`.
218
+ A blocked call returns a clear explanation instead of failing silently.
219
+
220
+ ## Operations
221
+
222
+ ```bash
223
+ python -m workspace_operator doctor # config, token, scopes, API reachability
224
+ python -m workspace_operator tools # catalog (--profile read-only, --json)
225
+ python -m workspace_operator serve # stdio server (same as python server.py)
226
+ ```
227
+
228
+ | Feature | How to use it | What it does |
229
+ |---|---|---|
230
+ | **Dry run** | `MCP_DRY_RUN=1` or `dry_run: true` | Every non-read-only tool returns a preview instead of executing — safe demos, safe exploration |
231
+ | **Audit log** | `MCP_AUDIT_LOG=~/.google/audit.jsonl` | One JSON line per call: tool, profile, status, duration, argument **names** (never values) |
232
+ | **Retries** | `MCP_RETRY_ATTEMPTS=3` | Transient Google API errors (429/5xx) are retried with exponential backoff |
233
+ | **Doctor** | `python -m workspace_operator doctor` | Checks secrets, token, granted vs configured scopes, and Drive/Gmail/Calendar reachability |
234
+
235
+ In dry-run the read-only tools still work, so an agent can explore data while every
236
+ write is turned into an explicit preview.
237
+
238
+ ### HTTP transport (localhost first)
239
+
240
+ ```bash
241
+ python -m workspace_operator serve --http --port 8765
242
+ ```
243
+
244
+ * binds `127.0.0.1` by default — not reachable from the network;
245
+ * every request needs `Authorization: Bearer <mcp_auth_token>`; extra per-client
246
+ tokens can be listed under `http_tokens` in `config.yaml`, and the audit log
247
+ records the token name as `client`;
248
+ * DNS-rebinding protection is on: only expected `Host` headers pass, browser
249
+ origins are denied unless explicitly allowed;
250
+ * `GET /healthz` is unauthenticated and returns only version and transport info;
251
+ * binding a non-loopback address requires **both** `--allow-network` and
252
+ `--insecure`, so network exposure is always deliberate.
253
+
254
+ ### Docker
255
+
256
+ ```bash
257
+ docker compose up --build
258
+ ```
259
+
260
+ The compose file publishes `127.0.0.1:8765` only and mounts your OAuth files
261
+ read-only.
262
+
263
+ ## Tool catalog
264
+
265
+ <details>
266
+ <summary>🔎 Cross-search (1)</summary>
267
+
268
+ | Tool | Type | Description |
269
+ |---|---|---|
270
+ | `workspace_search` | read | One query across Drive, Gmail and Calendar, date-sorted, partial failures reported |
271
+
272
+ </details>
273
+
274
+ <details>
275
+ <summary>📁 Drive (8)</summary>
276
+
277
+ | Tool | Type | Description |
278
+ |---|---|---|
279
+ | `find_files` | read | Search by name or Drive query |
280
+ | `drive_search_advanced` | read | Full Drive query syntax with a limit |
281
+ | `drive_list_permissions` | read | List permissions of a file |
282
+ | `create_folder` | write | Create a folder |
283
+ | `move_file` | write | Move a file into a folder |
284
+ | `drive_copy_file` | write | Copy a file |
285
+ | `share_file` | write | Share with user/group/domain/anyone |
286
+ | `drive_revoke_public` | destructive | Revoke public access (`confirm=true`) |
287
+
288
+ </details>
289
+
290
+ <details>
291
+ <summary>💾 Drive files (3)</summary>
292
+
293
+ | Tool | Type | Description |
294
+ |---|---|---|
295
+ | `drive_upload` | write | Upload a local file; `convert_to` turns docx/xlsx/pptx into native Google files |
296
+ | `drive_download` | read | Download a binary file to disk |
297
+ | `drive_export` | read | Export a Doc/Sheet/Slide to pdf, docx, xlsx, pptx, csv… |
298
+
299
+ </details>
300
+
301
+ <details>
302
+ <summary>📊 Sheets (12)</summary>
303
+
304
+ | Tool | Type | Description |
305
+ |---|---|---|
306
+ | `read_sheet` | read | Read a cell range |
307
+ | `get_spreadsheet_meta` | read | Sheet names and IDs |
308
+ | `sheet_export_csv` | read | Export a range as CSV |
309
+ | `append_row` | write | Append a row |
310
+ | `update_sheet` | write | Write rows to a range |
311
+ | `create_spreadsheet` | write | Create a spreadsheet |
312
+ | `add_sheet` | write | Add a tab |
313
+ | `sheet_create_filter_view` | write | Create a filter view |
314
+ | `sheet_create_named_range` | write | Create a named range |
315
+ | `sheet_find_replace` | write | Find & replace (`dry_run=true` by default) |
316
+ | `sheet_format` | write | Bold, colours, number formats, widths, row heights, frozen panes |
317
+ | `clear_range` | destructive | Clear a range (auto dry-run for large ranges) |
318
+
319
+ </details>
320
+
321
+ <details>
322
+ <summary>📄 Docs (5)</summary>
323
+
324
+ | Tool | Type | Description |
325
+ |---|---|---|
326
+ | `read_doc` | read | Read text and tables |
327
+ | `doc_export_pdf` | read | Export as binary PDF |
328
+ | `create_doc` | write | Create a document |
329
+ | `append_to_doc` | write | Append text |
330
+ | `doc_fill_template` | destructive | Fill placeholders (`confirm=true`) |
331
+
332
+ </details>
333
+
334
+ <details>
335
+ <summary>🖼 Slides (5)</summary>
336
+
337
+ | Tool | Type | Description |
338
+ |---|---|---|
339
+ | `slides_create` | write | Create a presentation |
340
+ | `slides_get` | read | Slide count, IDs and text per slide |
341
+ | `slides_add_slide` | write | Add a slide with a predefined layout |
342
+ | `slides_replace_text` | destructive | Replace placeholders (`confirm=true`) |
343
+ | `slides_insert_image` | write | Insert a public image URL onto a slide |
344
+
345
+ </details>
346
+
347
+ <details>
348
+ <summary>⚙️ Apps Script (7)</summary>
349
+
350
+ | Tool | Type | Description |
351
+ |---|---|---|
352
+ | `get_script_content` | read | Read project files |
353
+ | `create_script_project` | write | Create a project |
354
+ | `prepare_script_update` | write | Stage an update, returns `operation_id` |
355
+ | `execute_operation` | write | Apply a staged update (auto-backup first) |
356
+ | `cancel_operation` | write | Cancel a staged update |
357
+ | `restore_script_backup` | write | Restore from a backup |
358
+ | `script_run_function` | destructive | Run a function from an API-executable deployment |
359
+
360
+ </details>
361
+
362
+ <details>
363
+ <summary>📧 Gmail (14)</summary>
364
+
365
+ | Tool | Type | Description |
366
+ |---|---|---|
367
+ | `get_gmail_profile` | read | Authenticated address |
368
+ | `list_emails` | read | List with date, sender and subject |
369
+ | `read_email` | read | Headers, labels, attachments and decoded body |
370
+ | `gmail_search_and_summarize` | read | Search with a compact summary |
371
+ | `gmail_get_thread` | read | Whole conversation |
372
+ | `gmail_get_attachment` | read | Save an attachment to disk |
373
+ | `create_draft` | write | Create a draft |
374
+ | `send_email` | write | Send or draft (`draft_mode=true` by default) |
375
+ | `send_draft` | write | Send an existing draft |
376
+ | `gmail_reply` | write | Reply in-thread (`draft_mode=true` by default) |
377
+ | `gmail_label_apply` | write | Apply a label (`dry_run=true` by default) |
378
+ | `gmail_archive` | write | Archive (`confirm=true`) |
379
+ | `delete_email` | destructive | Delete (`confirm=true`) |
380
+ | `batch_delete_emails` | destructive | Delete many (`dry_run=true` by default) |
381
+
382
+ </details>
383
+
384
+ <details>
385
+ <summary>🧩 Composite (5)</summary>
386
+
387
+ | Tool | Type | Description |
388
+ |---|---|---|
389
+ | `report_from_sheet` | write | Read a range → column statistics → Doc with **native tables** or plain text (`output=text`) |
390
+ | `digest` | write | Last N hours across Gmail, Calendar and Drive → text or Doc |
391
+ | `compare_tables` | write | Diff two ranges by a key column; `output=doc` builds native tables |
392
+ | `clone_and_fill` | write | Copy any Drive file and replace placeholders in the copy |
393
+ | `sheet_to_slides` | write | Read a range → deck: title slide + one slide per row group |
394
+
395
+ </details>
396
+
397
+ <details>
398
+ <summary>📅 Calendar (7)</summary>
399
+
400
+ | Tool | Type | Description |
401
+ |---|---|---|
402
+ | `list_events` | read | Upcoming events with times and locations |
403
+ | `calendar_find_free_slots` | read | Free slots in a window |
404
+ | `create_event` | write | Create an event |
405
+ | `calendar_create_meeting` | write | Meeting with attendees (`confirm=true`) |
406
+ | `calendar_update_event` | write | Change any field; only what you pass is modified |
407
+ | `calendar_delete_event` | destructive | Delete an event (`confirm=true`) |
408
+ | `calendar_respond_to_invite` | write | Accept / decline / tentative (`confirm=true`) |
409
+
410
+ </details>
411
+
412
+ ### Resources
413
+
414
+ | URI | Attaches |
415
+ |---|---|
416
+ | `gdrive://recent` | 20 most recently modified Drive files |
417
+ | `gmail://inbox` | Recent unread inbox messages |
418
+ | `gcalendar://upcoming` | Events for the next 7 days |
419
+ | `gdrive://file/{file_id}` | Metadata for one file |
420
+ | `gsheets://{spreadsheet_id}/{range}` | A sheet range as a table |
421
+ | `gdocs://{document_id}` | Full document text, tables included |
422
+
423
+ ### Prompts
424
+
425
+ | Prompt | What it does |
426
+ |---|---|
427
+ | `summarize_inbox` | Fetch recent mail → ready for a summary |
428
+ | `analyze_spreadsheet` | Read a range → ready for analysis |
429
+ | `plan_week` | Load calendar events → ready for planning |
430
+ | `search_drive` | Search files → ready for organisation advice |
431
+ | `weekly_review` | Last N days of mail, files and agenda → weekly summary |
432
+ | `inbox_triage` | Gmail slice → urgent / waiting / informational / archive |
433
+ | `sheet_brief` | Range → statistics plus a briefing question |
434
+ | `compare_brief` | Two ranges → reconciliation report |
435
+
436
+
437
+ ## Safety by default
438
+
439
+ | Guard | Applies to |
440
+ |---|---|
441
+ | `confirm=true` required | `delete_email`, `gmail_archive`, `calendar_create_meeting`, `drive_revoke_public`, `doc_fill_template`, `slides_replace_text` |
442
+ | `dry_run=true` by default | `batch_delete_emails`, `sheet_find_replace`, `gmail_label_apply` |
443
+ | Draft mode by default | `send_email`, `gmail_reply` |
444
+ | Public sharing blocked | `share_file` refuses `type=anyone` without `allow_public=true` |
445
+ | No silent overwrite | `drive_download`, `drive_export`, `gmail_get_attachment` |
446
+ | Staged Apps Script | `prepare_script_update` → `execute_operation` (backup first) |
447
+ | Tool annotations | every tool carries `readOnlyHint` / `destructiveHint` |
448
+ | Server-side profiles | `read-only` / `read-write` / `full` + allow/deny lists |
449
+
450
+ > ⚠️ An agent can reach everything your OAuth token can. Use read-only scopes and the
451
+ > `read-only` profile for sensitive accounts — [docs/SECURITY.md](docs/SECURITY.md).
452
+
453
+ ## How it works
454
+
455
+ ```
456
+ server.py MCP server: 67 tool definitions, resources, prompts, profiles
457
+ workspace_operator/
458
+ ├─ profiles.py read-only / read-write / full + allow-deny lists
459
+ ├─ config.py config file + environment overrides
460
+ ├─ auth.py OAuth 2.0 credentials and token cache
461
+ ├─ text.py entity/whitespace normalisation, timestamps, error hints
462
+ ├─ operations.py staged-operation store for Apps Script
463
+ ├─ security.py token and email checks
464
+ ├─ runtime.py retries, audit log, dry-run helpers
465
+ ├─ cli.py doctor / tools / serve
466
+ └─ handlers/
467
+ ├─ search.py workspace_search — Drive + Gmail + Calendar
468
+ ├─ files.py drive_upload / drive_download / drive_export
469
+ ├─ formatting.py sheet_format request builders
470
+ ├─ gmail_threads.py threads, attachments, in-thread replies
471
+ ├─ slides.py presentations
472
+ ├─ calendar_writes.py update / delete / invite responses
473
+ ├─ report.py report_from_sheet: stats + document
474
+ ├─ digest.py digest across mail, calendar and drive
475
+ ├─ compare.py table diff by key column
476
+ ├─ template.py clone a file and fill placeholders
477
+ ├─ deck.py table → presentation
478
+ ├─ drive.py sheets.py docs.py
479
+ ├─ gmail.py calendar.py apps_script.py
480
+ tests/ unit, live and round-trip checks
481
+ antigravity/ ready-to-paste mcp_config.json
482
+ skills/google-workspace/ Agent Skill: when to use which tool
483
+ ```
484
+
485
+ Design rules:
486
+
487
+ * every handler returns a string — exceptions never reach the transport;
488
+ * writes are staged, confirmed or drafted, never silent;
489
+ * Apps Script is a two-phase commit with an automatic backup;
490
+ * server-side profiles keep the exposed surface small.
491
+
492
+ ## Testing
493
+
494
+ ```bash
495
+ python -m unittest discover -s tests -v # 240 unit tests, no network
496
+ python tests/live_smoke.py # 51 checks against a real account
497
+ python tests/pptx_roundtrip.py deck.pptx # 5 checks: pptx → Slides → replace
498
+ ```
499
+
500
+ The live suite is self-cleaning: it uploads and trashes a temp file, creates and
501
+ trashes a presentation and a spreadsheet, drafts and deletes a reply, downloads an
502
+ attachment and removes it. `tests/test_docs.py` keeps this README, the docs and the
503
+ code in sync (tool counts, profile numbers, links, examples).
504
+
505
+ ## Documentation
506
+
507
+ ### 🇬🇧 English
508
+ | Document | Description |
509
+ |---|---|
510
+ | [AUTH_SETUP.md](docs/AUTH_SETUP.md) | Google OAuth setup, step by step |
511
+ | [TOOLS_REFERENCE.md](docs/TOOLS_REFERENCE.md) | Every tool, resource and prompt |
512
+ | [SECURITY.md](docs/SECURITY.md) | Scopes, profiles, safety model |
513
+ | [IDE_SETUP.md](docs/IDE_SETUP.md) | Antigravity, Cursor, Windsurf, VS Code |
514
+ | [LEARN.md](LEARN.md) | How this server was built |
515
+ | [docs/](docs/) | Release notes and specs for every version |
516
+
517
+ ### 🇷🇺 На русском
518
+ | Документ | Описание |
519
+ |---|---|
520
+ | [AUTH_SETUP.md](docs/ru/AUTH_SETUP.md) | Пошаговая настройка OAuth |
521
+ | [TOOLS_REFERENCE.md](docs/ru/TOOLS_REFERENCE.md) | Справочник инструментов |
522
+ | [SECURITY.md](docs/ru/SECURITY.md) | Скоупы, профили, модель безопасности |
523
+ | [IDE_SETUP.md](docs/ru/IDE_SETUP.md) | Antigravity, Cursor, Windsurf, VS Code |
524
+
525
+ ## Roadmap
526
+
527
+ - [x] Cross-product search, tool profiles
528
+ - [x] Files, Slides, Apps Script execution
529
+ - [x] Sheet formatting
530
+ - [x] Gmail threads, attachments, replies
531
+ - [x] Calendar update/delete/respond
532
+ - [x] Composite tools: `report_from_sheet`, `digest`, `compare_tables`, `clone_and_fill`
533
+ - [x] Optional HTTP transport (Streamable HTTP, stateless, localhost-first)
534
+
535
+ ## Responsibility
536
+
537
+ You are responsible for what the agent does with your account. Read
538
+ [docs/SECURITY.md](docs/SECURITY.md), start with `read-only`, keep backups on.
539
+
540
+ ---
541
+
542
+ <div align="center">
543
+
544
+ MIT licensed · built on the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)
545
+
546
+ </div>