@theethosteam/sheets-mcp
v0.1.1
Published
Full-control MCP server (and CLI) for Google Sheets — values, structure, formatting, charts and the complete batchUpdate surface over OAuth, plus Drive lifecycle (find, create, share, move). Runs via npx, locally or in cloud.
Downloads
85
Maintainers
Readme
@theethosteam/sheets-mcp
Full-control Google Sheets MCP server + CLI. Values, tabs, rows/columns,
formatting, data validation, conditional formats, named ranges, the complete
batchUpdate surface — plus the Drive half nobody remembers they need (find a
spreadsheet by name, create one, share it, file it in a client folder).
Runs three ways from one registerTools():
- remote custom connector —
https://mcp.theethosteam.com/api/sheets/mcp(seeapps/connectors-mcp), available in every Claude surface; - stdio MCP server —
npx -y @theethosteam/sheets-mcp; - CLI —
npx -y -p @theethosteam/sheets-mcp sheets <command>.
Env
| Var | Required | Notes |
|---|---|---|
| SHEETS_OAUTH_CLIENT_ID | ✅ | Shared Ethos OAuth client |
| SHEETS_OAUTH_CLIENT_SECRET | ✅ | |
| SHEETS_OAUTH_REFRESH_TOKEN | ✅ | Must carry the spreadsheets + drive scopes |
| SHEETS_SPREADSHEET_ID | — | Optional per-call default; every tool also takes an ID or a URL |
Auth
Same shared OAuth client as GA4/GTM/GSC/Ads, but the Sheets scopes are new — a refresh token minted before this connector existed will 403 on every call until you re-consent:
npx -y -p @theethosteam/google-ads-mcp ads auth:url # requests ALL connector scopes
npx -y -p @theethosteam/google-ads-mcp ads auth:exchange <code>
# or, scoped to Sheets only:
npx -y -p @theethosteam/sheets-mcp sheets auth:url
npx -y -p @theethosteam/sheets-mcp sheets auth:exchange <code>Why full drive and not drive.file. The Sheets API can only act on a
spreadsheet whose ID you already hold. Finding one by name, sharing it, moving
it, trashing it — all Drive. drive.file only ever sees files this OAuth
client itself created, which excludes essentially every spreadsheet a human
made by hand. Full drive is what makes "full control of Google Sheets" true.
The two things that make this usable
1. IDs, URLs and tab names all work. Every tool takes the spreadsheet as an
ID or a pasted docs.google.com/spreadsheets/d/… URL, and takes a tab as its
title ("Invoices") or its numeric sheetId. Omit the tab and you get the
first one.
2. A1 in, GridRange out. A1 notation is 1-based and inclusive; the
GridRange that batchUpdate wants is 0-based and end-exclusive. Getting
that wrong doesn't error — it silently formats, sorts or deletes the wrong row.
Every structural tool here takes A1 ("A1:D50", "B:D", "2:10") and does the
conversion once, correctly. insert_rows_or_columns / delete_rows_or_columns
likewise take 1-based, inclusive indices that match the numbers in the UI.
Write safety
Destructive and outward-facing operations fail closed without confirm:true:
clear_range, delete_sheet, delete_rows_or_columns, delete_named_range,
trash_spreadsheet, find_replace, share_spreadsheet — and batch_update
whenever the payload contains a destructive request type (deleteSheet,
deleteDimension, deleteRange, cutPaste, …). Everything else runs directly.
Tools (34)
Discovery — sheets_whoami · list_spreadsheets · get_spreadsheet · list_sheets
Read — read_range · read_ranges · export_csv
Write — write_range · write_ranges · append_rows · clear_range🔒
Spreadsheet lifecycle — create_spreadsheet · share_spreadsheet🔒 · list_permissions · move_spreadsheet · trash_spreadsheet🔒
Tabs — add_sheet · delete_sheet🔒 · duplicate_sheet · update_sheet_properties · copy_sheet_to
Rows & columns — insert_rows_or_columns · delete_rows_or_columns🔒 · auto_resize · sort_range
Formatting & rules — format_cells · merge_cells · set_data_validation · add_conditional_format · find_replace🔒
Named ranges — add_named_range · delete_named_range🔒
Escape hatches — batch_update (every request type the API has) · raw (any method by dotted path; prefix drive: for Drive)
🔒 = requires confirm:true.
format_cells is additive — it builds a precise field mask from the
properties you pass, so setting a background colour doesn't wipe existing bold,
borders or number formats.
CLI
sheets whoami
sheets ls "Q3" # find spreadsheets by name
sheets tabs <spreadsheet> # tab titles + sheetIds
sheets read "Invoices!A1:D50" -s <spreadsheet>
sheets read "A1:D50" -s <spreadsheet> --csv
sheets append '[["Acme",1200]]' -s <spreadsheet> -t Invoices
sheets write "A1:B2" '[["a",1],["b",2]]' -s <spreadsheet>
sheets create "Q4 Budget" --tabs Oct,Nov,Dec
sheets batch '[{"addSheet":{"properties":{"title":"New"}}}]' -s <spreadsheet>
sheets raw spreadsheets.values.get '{"spreadsheetId":"…","range":"A1:B2"}'Anything not covered by a named tool
batch_update reaches the entire Sheets API write surface — charts, pivot
tables, banding, protected ranges, filter views, dimension groups, developer
metadata, borders. raw reaches the read side, including
spreadsheets.getByDataFilter and spreadsheets.developerMetadata.search, and
Drive via a drive: prefix. There is no operation this connector cannot do.
