Repository avatar
AI Tools
v1.22.2
active

workspace-operator

io.github.Artoxem/workspace-operator

Google Workspace automation for AI agents: 132 tools, safe bulk operations, undo.

Documentation

Workspace Operator

Deep automation for Google Workspace through the Model Context Protocol

Drive · Docs · Sheets · Slides · Gmail · Calendar · Apps Script — one local MCP server with 132 tools, staged writes and server-side tool profiles.

Python MCP Tools PyPI Antigravity License: MIT GitHub stars Downloads

English · Русский


Workspace Operator runs on your machine, talks to Google over your own OAuth client, and gives an AI agent the operations that turn "read my inbox" into "do the work and let me undo it": staged Apps Script updates, file transfer in both directions, presentation assembly, sheet formatting, threaded replies and attachments.

132 tools · 3 resources · 3 resource templates · 8 prompts, every tool annotated with readOnlyHint / destructiveHint.

Nothing here is tied to a specific account, spreadsheet or document: every tool takes identifiers and ranges, and all examples use synthetic data.

How it compares

Google Workspace MCP (remote)Workspace Operator
TransportStreamable HTTP, Google-hostedstdio + Streamable HTTP, local or Docker
AvailabilityDeveloper Previewpublic, MIT
Data pathGoogle MCP endpointyour machine, your OAuth client
Apps Script—create · read · stage · execute · cancel · restore · run function
Local files—upload, download, export to pdf/docx/xlsx/pptx
Slides—create, add slides, fill text, insert images
Sheets formatting—bold, colours, number formats, widths, freeze
Gmail threadssingle messagesthreads, attachments, in-thread replies
Server-side tool profilesIAM / admin allow-listread-only / read-write / full + allow-deny

Official servers are the right default for "ask my mail a question". Workspace Operator is for "change the workspace and be able to roll it back".

What you get

GroupToolsHighlights
🔎 Cross-search1One query over Drive, Gmail and Calendar, merged by date
📦 Bulk operations7plan → confirm → run → undo for Drive, Gmail, Docs, Slides and Sheets
📁 Drive16Search, metadata, rename, trash/restore, share, permissions
💬 Comments4Threads on Docs, Sheets and Slides: read, write, reply, resolve
💾 Drive files3Upload/download/export, conversion to native Google formats
📊 Sheets25Read/write, formatting, conditional rules, charts, filter views, CSV
📄 Docs8Read (tables included), append, find & replace, templates, PDF export
🖼 Slides11Create decks, add/delete slides, replace text, insert images
⚙️ Apps Script10Staged updates with backups, rollback, run functions
📧 Gmail22Threads, attachments, replies, drafts, labels, batch delete
📅 Calendar10Events, free slots, meetings, update/delete, invite responses
🧩 Composite5report from sheet, digest, table diff, template clone, table → deck
✅ Tasks4lists, tasks, create with due date, complete
📝 Forms4create, add questions, read structure, list responses
📮 Postmaster2verified domains, deliverability stats

Plus 3 resources, 3 resource templates and 8 prompts — see the tool catalog.

Who it is for

Workspace Operator is a single-user server: one process, one Google account, one tool profile. "Several clients" does not mean "several users".

You haveDoes it fit?What happens
One person with several MCP clientsYesEach client can use its own bearer token (http_tokens); they share the account, profile and data
A small team on one Google accountWorks, without isolationEveryone sees the same Drive/Gmail; bulk jobs and the audit log are shared; use the read-only profile for people you do not fully trust
Several Google accountsOne instance per accountSeparate config, OAuth token, data directory and port or container per person
Public service or multi-tenant SaaSNoNo per-user authentication, quotas, job ownership or data separation

A bearer token only identifies a client in the audit log; it does not create a separate user. Anyone holding a valid token can call every exposed tool and cancel any bulk job in that instance. Read SECURITY.md before sharing an instance with other people.

Quick start

0 — Choose how to run it

You wantUseWhat it means
One desktop client (Claude Desktop, Cursor, DSH, Windsurf, Cline)stdioThe client starts the server as a child process. No port, no bearer header, no background service; the server stops when the client stops.
One server shared by several clients or agentsDocker / HTTPA long-running container on 127.0.0.1:8765. Every client connects by URL with a bearer token.
Change the codesource checkoutSame as stdio, installed from requirements.txt.

Both paths need Google OAuth once. The HTTP path additionally needs a local MCP_AUTH_TOKEN string; it is not a Google credential and has nothing to do with your Google account.

1 — Install

stdio (recommended for a single desktop client)

pip install workspace-operator-mcp
workspace-operator version

Docker / HTTP (recommended for several clients)

docker pull ghcr.io/artoxem/workspace-operator:1.22.1

From source (development)

git clone https://github.com/Artoxem/workspace-operator.git
cd workspace-operator
python -m venv .venv && .venv\Scripts\activate    # Windows
pip install -r requirements.txt

Python 3.10 or newer. The package installs the workspace-operator command; python -m workspace_operator works from a source checkout too.

2 — Google OAuth (once)

  1. Create or pick a project in Google Cloud Console.
  2. Enable the APIs you need: Drive, Docs, Sheets, Slides, Gmail, Calendar, Apps Script, plus Tasks and Forms. Postmaster Tools is optional and only useful for domains you have verified.
  3. Configure the OAuth consent screen: External, add your account as a test user while the app is in Testing.
  4. Create an OAuth client ID of type Desktop app, then download the JSON and save it as oauth.keys.json in ~/.google/.
  5. Authorize once:
workspace-operator doctor

The first run opens a browser. Sign in, allow the scopes, and the token is saved to ~/.google/token.json.

No browser (server, SSH, Docker host)? Authorize on a machine with a browser, then copy token.json next to oauth.keys.json on the target and mount that directory read-write into the container. The server refreshes the token in place, so a read-only mount breaks it.

Changed scopes or upgraded a major version? Delete token.json and run workspace-operator doctor again. The doctor prints exactly which scopes are missing instead of failing silently.

Step by step: docs/AUTH_SETUP.md · docs/ru/AUTH_SETUP.md

3 — Configure

cp config.example.yaml config.yaml
client_secrets_file: "/home/you/.google/oauth.keys.json"
token_file: "/home/you/.google/token.json"
backup_dir: "/home/you/.google/backups"
mcp_auth_token: "put-a-long-random-string-here"
tool_profile: "read-write"      # full | read-write | read-only

On Windows use forward slashes (C:/Users/you/.google/oauth.keys.json) or double backslashes in YAML. Every key can be overridden by an environment variable; see docs/SECURITY.md.

Two different secrets, do not mix them up:

SecretWhat it isWho creates it
token.jsonGoogle OAuth token — proof that Google granted the scopesGoogle, after you click Allow in the browser
mcp_auth_tokenLocal bearer string — a gate for the MCP server (and the HTTP Authorization header)You, any random string

mcp_auth_token is not network security by itself; it is an activation key and a per-request bearer for the HTTP transport.

4 — Connect a client

stdio client — the client launches the installed command:

{
  "mcpServers": {
    "workspace-operator": {
      "command": "workspace-operator",
      "args": ["serve"],
      "env": {
        "MCP_CONFIG_FILE": "C:/Users/you/.google/config.yaml",
        "MCP_AUTH_TOKEN": "put-a-long-random-string-here"
      }
    }
  }
}

HTTP client — the container or workspace-operator serve --http is already running:

{
  "mcpServers": {
    "workspace-operator": {
      "url": "http://127.0.0.1:8765/mcp",
      "headers": { "Authorization": "Bearer put-a-long-random-string-here" }
    }
  }
}

Per-client file locations: docs/IDE_SETUP.md · docs/ru/IDE_SETUP.md. Antigravity has its own walkthrough: antigravity/README.md.

If the client cannot start the command, check workspace-operator version in the same shell the client uses — IDEs often have a different PATH.

Tool profiles

A profile is a server-side allow-list: tools outside it are neither listed nor callable. It complements OAuth scopes (what Google allows) and client permissions (what the agent may do without asking).

ProfileToolsUse it for
full132your own machine, full trust
read-write113daily work — destructive tools hidden
read-only39exploration, demos, shared machines, untrusted agents
tool_profile: "read-write"
# Optional, applied on top. Deny wins over allow.
allowed_tools: ["workspace_search", "find_files", "read_sheet", "read_email"]
denied_tools: ["send_email", "share_file"]

Environment overrides: MCP_TOOL_PROFILE, MCP_ALLOWED_TOOLS, MCP_DENIED_TOOLS. A blocked call returns a clear explanation instead of failing silently.

Operations

python -m workspace_operator doctor     # config, token, scopes, API reachability
python -m workspace_operator doctor --offline   # same, without network probes
python -m workspace_operator tools      # catalog (--profile read-only, --json)
python -m workspace_operator serve      # stdio server
FeatureHow to use itWhat it does
Dry runMCP_DRY_RUN=1 or dry_run: trueEvery non-read-only tool returns a preview instead of executing — safe demos, safe exploration
Audit logMCP_AUDIT_LOG=~/.google/audit.jsonlOne JSON line per call: tool, profile, status, duration, argument names (never values)
RetriesMCP_RETRY_ATTEMPTS=3Transient Google API errors (429/5xx) are retried with exponential backoff
Doctorpython -m workspace_operator doctorChecks secrets, token, granted vs configured scopes and API reachability; exits 1 when a critical check fails

In dry-run the read-only tools still work, so an agent can explore data while every write is turned into an explicit preview.

HTTP transport (localhost first)

python -m workspace_operator serve --http --port 8765
  • binds 127.0.0.1 by default — not reachable from the network;
  • every request needs Authorization: Bearer <mcp_auth_token>; extra per-client tokens can be listed under http_tokens in config.yaml, and the audit log records the token name as client;
  • DNS-rebinding protection is on: only expected Host headers pass, browser origins are denied unless explicitly allowed;
  • GET /healthz is unauthenticated and returns only version and transport info;
  • binding a non-loopback address requires both --allow-network and --insecure; loopback names accept any host port (containers, proxies), anything else needs an explicit --allow-host; --insecure, so network exposure is always deliberate.

Docker

The container speaks HTTP. It cannot open a browser, so authorize Google on the host first:

pip install workspace-operator-mcp   # or use a source checkout
workspace-operator doctor           # browser -> ~/.google/token.json

Then use Compose (recommended) or a single docker run.

Compose

cp .env.example .env                # set MCP_AUTH_TOKEN and GOOGLE_DIR
docker compose up -d
curl http://127.0.0.1:8765/healthz

docker-compose.yml mounts config.docker.yaml, the Google folder read-write, and ./data for the audit log and bulk jobs.

docker run — Linux/macOS

docker run -d --name workspace-operator --restart unless-stopped \
  -p 127.0.0.1:8765:8765 \
  -v "$HOME/.google:/home/app/.google" \
  -v "$PWD/config.docker.yaml:/app/config.yaml:ro" \
  -v "$PWD/data:/home/app/data" \
  -e MCP_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" \
  ghcr.io/artoxem/workspace-operator:1.22.1

docker run — Windows PowerShell

$token = python -c "import secrets; print(secrets.token_urlsafe(32))"
docker run -d --name workspace-operator --restart unless-stopped `
  -p 127.0.0.1:8765:8765 `
  -v "${env:USERPROFILE}\.google:/home/app/.google" `
  -v "${PWD}\config.docker.yaml:/app/config.yaml:ro" `
  -v "${PWD}\data:/home/app/data" `
  -e MCP_AUTH_TOKEN=$token `
  ghcr.io/artoxem/workspace-operator:1.22.1

Point the HTTP client at http://127.0.0.1:8765/mcp with Authorization: Bearer <token>. The Google folder is mounted read-write on purpose: the server refreshes token.json in place.

Healthy container and the public health endpoint

docker compose ps reports healthy and /healthz returns the running version.

stdio clients that only speak stdio can still use the image — the entrypoint takes the same arguments as the CLI:

docker run -i --rm \
  -v "$HOME/.google:/home/app/.google" \
  --entrypoint python \
  ghcr.io/artoxem/workspace-operator:1.22.1 -m workspace_operator serve

Something not working? See docs/TROUBLESHOOTING.md.

Something not working? See docs/TROUBLESHOOTING.md.

Tool catalog

🔎 Cross-search (1)
ToolTypeDescription
workspace_searchreadOne query across Drive, Gmail and Calendar, date-sorted, partial failures reported
📁 Drive (16)
ToolTypeDescription
find_filesreadSearch by name or Drive query
drive_search_advancedreadFull Drive query syntax with a limit
drive_list_permissionsreadList permissions of a file
create_folderwriteCreate a folder
move_filewriteMove a file into a folder
drive_copy_filewriteCopy a file
share_filewriteShare with user/group/domain/anyone
drive_revoke_publicdestructiveRevoke public access (confirm=true)
drive_get_metadatareadSize, owner, parents, timestamps and link
drive_rename_filewriteRename without moving or copying
drive_delete_filedestructiveMove to trash (confirm=true)
drive_restore_filewriteRestore a file from the trash
drive_remove_permissiondestructiveRevoke one permission (confirm=true)
drive_empty_trashdestructivePermanently delete trashed files (confirm=true)
drive_transfer_ownershipdestructiveHand a file to another owner (confirm=true)
drive_create_shortcutwriteCreate a shortcut to another file
💾 Drive files (3)
ToolTypeDescription
drive_uploadwriteUpload a local file; convert_to turns docx/xlsx/pptx into native Google files
drive_downloadreadDownload a binary file to disk
drive_exportreadExport a Doc/Sheet/Slide to pdf, docx, xlsx, pptx, csv…
💬 Comments (4)
ToolTypeDescription
drive_list_commentsreadComments and replies on a Doc, Sheet or Slide
drive_create_commentwriteAdd a comment, optionally quoting text
drive_reply_commentwriteReply in an existing thread
drive_resolve_commentwriteMark a thread resolved (confirm=true)
📦 Bulk operations (7)
ToolTypeDescription
bulk_planreadResolve a Drive, Gmail, Docs, Slides or Sheets selector into a reviewed, immutable plan (dry run; 300-item default cap)
bulk_runwriteExecute a reviewed plan in the background (confirm=true, resumable): Drive move/trash/restore/rename/share/revoke_public, Gmail archive/label/trash/restore/mark_read/mark_unread, Docs/Slides replace_text, Sheets find_replace
bulk_statusreadCompact progress: done/total, ok, skipped, failed
bulk_cancelwriteStop a running job; in-flight items finish
bulk_resultreadPage through per-item results (50 by default, 200 max)
bulk_listreadRecent bulk jobs
bulk_undowriteRevert a completed move/rename/trash job from its saved state (confirm=true)
✅ Tasks (4)
ToolTypeDescription
tasks_list_listsreadTask lists with their ids
tasks_listreadTasks in a list; optionally include completed
tasks_createwriteCreate a task with notes and an optional due date
tasks_completewriteMark a task completed
📝 Forms (4)
ToolTypeDescription
forms_createwriteCreate a form with a title and description
forms_add_questionswriteAppend short-answer questions
forms_getreadTitle, responder link and questions
forms_list_responsesreadResponses with answers mapped to question titles
📮 Postmaster (2)
ToolTypeDescription
postmaster_list_domainsreadVerified domains and your permission level
postmaster_traffic_statsreadSpam rate, reputation, DKIM/DMARC/SPF and delivery errors
📊 Sheets (25)
ToolTypeDescription
read_sheetreadRead a cell range
get_spreadsheet_metareadSheet names and IDs
sheet_export_csvreadExport a range as CSV
append_rowwriteAppend a row
update_sheetwriteWrite rows to a range
create_spreadsheetwriteCreate a spreadsheet
add_sheetwriteAdd a tab
sheet_create_filter_viewwriteCreate a filter view
sheet_create_named_rangewriteCreate a named range
sheet_find_replacewriteFind & replace (dry_run=true by default)
sheet_formatwriteBold, colours, number formats, widths, row heights, frozen panes
clear_rangedestructiveClear a range (auto dry-run for large ranges)
sheet_conditional_formatwriteColour cells when a condition matches
sheet_add_chartwriteInsert a COLUMN/BAR/LINE/AREA/PIE chart from a range
sheet_delete_sheetdestructiveDelete a tab (confirm=true)
sheet_insert_rowswriteInsert empty rows before a row number
sheet_delete_rowsdestructiveDelete rows (confirm=true)
sheet_merge_cellswriteMerge or unmerge a range
sheet_insert_columnswriteInsert empty columns before a column
sheet_delete_columnsdestructiveDelete columns (confirm=true)
sheet_sort_rangewriteSort a range by one column
sheet_add_pivot_tablewritePivot table from a range onto a new sheet
sheet_set_data_validationwriteList or number-range validation
sheet_protect_rangewriteWarning or locked protection
sheet_set_borderswriteSolid/dotted/dashed borders
📄 Docs (8)
ToolTypeDescription
read_docreadRead text and tables
doc_export_pdfreadExport as binary PDF
create_docwriteCreate a document
append_to_docwriteAppend text
doc_fill_templatedestructiveFill placeholders (confirm=true)
doc_replace_textdestructiveFind & replace with a preview (confirm=true)
docs_insert_imagewriteInsert an inline image from a public URL
docs_format_textdestructiveBold/italic/size/heading for matched text (confirm=true)
🖼 Slides (11)
ToolTypeDescription
slides_createwriteCreate a presentation
slides_getreadSlide count, IDs and text per slide
slides_add_slidewriteAdd a slide with a predefined layout
slides_replace_textdestructiveReplace placeholders (confirm=true)
slides_insert_imagewriteInsert a public image URL onto a slide
slides_delete_slidedestructiveDelete a slide (confirm=true)
slides_move_slidewriteMove a slide to another position
slides_set_noteswriteReplace the speaker notes
slides_add_tablewriteInsert a native table onto a slide
slides_list_layoutsreadLayouts available in a presentation
slides_add_shapewriteRectangle/ellipse/text box with optional text
⚙️ Apps Script (10)
ToolTypeDescription
get_script_contentreadRead project files
create_script_projectwriteCreate a project
prepare_script_updatewriteStage an update, returns operation_id
execute_operationwriteApply a staged update (auto-backup first)
cancel_operationwriteCancel a staged update
restore_script_backupwriteRestore from a backup
script_run_functiondestructiveRun a function from an API-executable deployment
script_list_deploymentsreadDeployments of a project
script_create_deploymentwriteDeploy a saved version
script_update_deploymentwritePoint a deployment at a newer version
📧 Gmail (22)
ToolTypeDescription
get_gmail_profilereadAuthenticated address
list_emailsreadList with date, sender and subject
read_emailreadHeaders, labels, attachments and decoded body
gmail_search_and_summarizereadSearch with a compact summary
gmail_get_threadreadWhole conversation
gmail_get_attachmentreadSave an attachment to disk
create_draftwriteCreate a draft
send_emailwriteSend or draft (draft_mode=true by default)
send_draftwriteSend an existing draft
gmail_replywriteReply in-thread (draft_mode=true by default)
gmail_label_applywriteApply a label (dry_run=true by default)
gmail_archivewriteArchive (confirm=true)
delete_emaildestructiveDelete (confirm=true)
batch_delete_emailsdestructiveDelete many (dry_run=true by default)
gmail_list_labelsreadLabels with IDs, user and system
gmail_create_labelwriteCreate a label
gmail_send_with_attachmentswriteDraft or send with local files (20 MB)
gmail_list_draftsreadDrafts with recipient and subject
gmail_delete_draftdestructiveDelete a draft (confirm=true)
gmail_list_filtersreadFilters with criteria and actions
gmail_create_filterwriteFilter: match from/subject/query, add or remove labels
gmail_update_draftwriteReplace draft content
🧩 Composite (5)
ToolTypeDescription
report_from_sheetwriteRead a range → column statistics → Doc with native tables or plain text (output=text)
digestwriteLast N hours across Gmail, Calendar and Drive → text or Doc
compare_tableswriteDiff two ranges by a key column; output=doc builds native tables
clone_and_fillwriteCopy any Drive file and replace placeholders in the copy
sheet_to_slideswriteRead a range → deck: title slide + one slide per row group
📅 Calendar (10)
ToolTypeDescription
list_eventsreadUpcoming events with times and locations
calendar_find_free_slotsreadFree slots in a window
create_eventwriteCreate an event
calendar_create_meetingwriteMeeting with attendees (confirm=true)
calendar_update_eventwriteChange any field; only what you pass is modified
calendar_delete_eventdestructiveDelete an event (confirm=true)
calendar_respond_to_invitewriteAccept / decline / tentative (confirm=true)
calendar_list_calendarsreadCalendars with access roles
calendar_move_eventwriteMove an event to another calendar
calendar_set_event_colorwriteColour an event (1–11)

Resources

URIAttaches
gdrive://recent20 most recently modified Drive files
gmail://inboxRecent unread inbox messages
gcalendar://upcomingEvents for the next 7 days
gdrive://file/{file_id}Metadata for one file
gsheets://{spreadsheet_id}/{range}A sheet range as a table
gdocs://{document_id}Full document text, tables included

Prompts

PromptWhat it does
summarize_inboxFetch recent mail → ready for a summary
analyze_spreadsheetRead a range → ready for analysis
plan_weekLoad calendar events → ready for planning
search_driveSearch files → ready for organisation advice
weekly_reviewLast N days of mail, files and agenda → weekly summary
inbox_triageGmail slice → urgent / waiting / informational / archive
sheet_briefRange → statistics plus a briefing question
compare_briefTwo ranges → reconciliation report

Safety by default

GuardApplies to
confirm=true requireddelete_email, gmail_archive, calendar_create_meeting, drive_revoke_public, doc_fill_template, slides_replace_text, drive_delete_file, drive_remove_permission, drive_resolve_comment, doc_replace_text, slides_delete_slide, sheet_delete_sheet, sheet_delete_rows, drive_empty_trash, sheet_delete_columns, drive_transfer_ownership, docs_format_text, gmail_delete_draft, calendar_delete_event, calendar_respond_to_invite
dry_run=true by defaultbatch_delete_emails, sheet_find_replace, gmail_label_apply
Draft mode by defaultsend_email, gmail_reply
Public sharing blockedshare_file refuses type=anyone without allow_public=true
No silent overwritedrive_download, drive_export, gmail_get_attachment
Staged Apps Scriptprepare_script_update → execute_operation (backup first)
Tool annotationsevery tool carries readOnlyHint / destructiveHint
Server-side profilesread-only / read-write / full + allow/deny lists

⚠️ An agent can reach everything your OAuth token can. Use read-only scopes and the read-only profile for sensitive accounts — docs/SECURITY.md.

How it works

server.py                    MCP server: 132 tool definitions, resources, prompts, profiles
workspace_operator/
├─ profiles.py               read-only / read-write / full + allow-deny lists
├─ config.py                 config file + environment overrides
├─ auth.py                   OAuth 2.0 credentials and token cache
├─ text.py                   entity/whitespace normalisation, timestamps, error hints
├─ operations.py             staged-operation store for Apps Script
├─ security.py               token and email checks
├─ runtime.py                retries, audit log, dry-run helpers
├─ cli.py                    doctor / tools / serve
└─ handlers/
   ├─ search.py              workspace_search — Drive + Gmail + Calendar
   ├─ files.py               drive_upload / drive_download / drive_export
   ├─ formatting.py          sheet_format request builders
   ├─ gmail_threads.py       threads, attachments, in-thread replies
   ├─ slides.py              presentations
   ├─ calendar_writes.py     update / delete / invite responses
   ├─ report.py              report_from_sheet: stats + document
   ├─ digest.py              digest across mail, calendar and drive
   ├─ compare.py             table diff by key column
   ├─ template.py            clone a file and fill placeholders
   ├─ deck.py                table → presentation
   ├─ drive_extra.py         metadata, rename, trash/restore, permissions
   ├─ comments.py            Drive comments: list, create, reply, resolve
   ├─ doc_edit.py            find & replace inside a document
   ├─ sheets_extra.py        conditional formatting, charts
   ├─ sheets_structure.py    tabs, rows, merged cells
   ├─ sheets_more.py         columns and sorting
   ├─ doc_media.py           inline images in documents
   ├─ doc_format.py          bold/italic/heading for matched text
   ├─ sheets_pivot.py        pivot tables
   ├─ gmail_drafts.py        draft list, update and cleanup
   ├─ gmail_filters.py       filters: list and create
   ├─ sheets_validate.py     validation, protection, borders
   ├─ gmail_labels.py        labels and bulk message flags
   ├─ drive.py  sheets.py  docs.py
   ├─ gmail.py  calendar.py  apps_script.py
tests/                       unit, live and round-trip checks
antigravity/                 ready-to-paste mcp_config.json
skills/google-workspace/     Agent Skill: when to use which tool

Design rules:

  • every handler returns a string — exceptions never reach the transport;
  • writes are staged, confirmed or drafted, never silent;
  • Apps Script is a two-phase commit with an automatic backup;
  • server-side profiles keep the exposed surface small.

Testing

python -m unittest discover -s tests -v     # 328 unit tests, no network
python tests/live_smoke.py                  # 51 checks against a real account
python tests/live_new_tools.py              # 15 checks for the v1.14 batch
python tests/live_phase2.py                 # 8 checks for the v1.15 batch
python tests/live_phase3.py                 # 8 checks for the v1.16 batch
python tests/live_phase4.py                 # 5 checks for the v1.17 batch
python tests/live_phase5.py                 # 7 checks for the v1.18 batch
python tests/pptx_roundtrip.py deck.pptx    # 5 checks: pptx → Slides → replace

The live suite is self-cleaning: it uploads and trashes a temp file, creates and trashes a presentation and a spreadsheet, drafts and deletes a reply, downloads an attachment and removes it. tests/test_docs.py keeps this README, the docs and the code in sync (tool counts, profile numbers, links, examples).

Documentation

🇬🇧 English

DocumentDescription
AUTH_SETUP.mdGoogle OAuth setup, step by step
TOOLS_REFERENCE.mdEvery tool, resource and prompt
SECURITY.mdScopes, profiles, safety model
IDE_SETUP.mdAntigravity, Cursor, Windsurf, VS Code
TROUBLESHOOTING.mdSymptom → cause → fix, including Docker
LEARN.mdHow this server was built
docs/Release notes and specs for every version

🇷🇺 На русском

ДокументОписание
AUTH_SETUP.mdПошаговая настройка OAuth
TOOLS_REFERENCE.mdСправочник инструментов
SECURITY.mdСкоупы, профили, модель безопасности
IDE_SETUP.mdAntigravity, Cursor, Windsurf, VS Code

Limitations

Knowing the edges is part of the contract:

  • Drive: deleting means "move to trash"; drive_empty_trash is the only permanent path and needs confirm=true.
  • Sheets: tabs, rows, columns, merges, sorting, pivot tables, validation, protection and borders are handled; charts cover the basic types and conditional formatting takes one rule per call.
  • Docs: text styling works on matched text; tables are created by the report tool but not edited cell by cell afterwards.
  • Slides: slides, speaker notes and native tables work; shapes, diagrams and per-shape styling do not.
  • One account per server: a single OAuth token, no multi-tenant mode.

Roadmap

  • Cross-product search, tool profiles
  • Files, Slides, Apps Script execution
  • Sheet formatting
  • Gmail threads, attachments, replies
  • Calendar update/delete/respond
  • Composite tools: report_from_sheet, digest, compare_tables, clone_and_fill
  • Optional HTTP transport (Streamable HTTP, stateless, localhost-first)

Responsibility

You are responsible for what the agent does with your account. Read docs/SECURITY.md, start with read-only, keep backups on.


MIT licensed · built on the MCP Python SDK

PYPI
workspace-operator-mcp
Install Command
pip install workspace-operator-mcp
Runtime: uvx