Skip to main content
Glama
YerayRodri
by YerayRodri
README.md
# google-sheets-mcp

MCP server to read and edit Google Sheets. Built for editorial calendars,
task trackers and any agency-style spreadsheet workflow.

## Tools (50)

### Reading

| Tool | What it does |
|---|---|
| `describe_sheet` | Structural X-ray of a tab: header row, first data row, first free row, allowed dropdown values per column, merged cells, protected ranges and conditional-format rules. **Call this before writing to any tab you don't already know** |
| `get_spreadsheet_info` | Title, tabs and dimensions of the spreadsheet |
| `get_sheet_data` | All data of a tab as an array of rows |
| `read_range` | Read a specific A1 range |
| `read_multiple_ranges` | Read several ranges in a single API call — more efficient than calling `read_range` repeatedly |
| `get_hyperlinks` | Extract hyperlink URLs from cells in a range — useful when a cell displays text that links elsewhere |

### Writing

| Tool | What it does |
|---|---|
| `write_range` | Write data into a range (overwrites) |
| `update_cell` | Update a single cell |
| `batch_update_cells` | Update multiple individual cells in a single API call |
| `append_rows` | Append rows at the end of a tab — the safe way to add data, never overwrites |
| `find_replace` | Find and replace text across the whole spreadsheet |
| `clear_range` | Clear a range |

### Rows and columns

| Tool | What it does |
|---|---|
| `insert_rows` | Insert N blank rows starting at a given row, pushing existing content down |
| `delete_rows` | Delete a range of rows |
| `move_rows` | Move rows to another position keeping their values, format, dropdowns and notes — a real move, not a copy+delete |
| `insert_columns` | Insert blank columns, pushing the rest to the right |
| `delete_columns` | Delete columns |
| `sort_range` | Sort a range by a column (ascending or descending) |
| `auto_resize_columns` | Auto-fit column widths to content |

### Tabs and spreadsheets

| Tool | What it does |
|---|---|
| `add_sheet` | Create a new tab |
| `rename_sheet` | Rename a tab |
| `copy_sheet` | Duplicate a tab within the same spreadsheet |
| `delete_sheet` | Delete an entire tab |
| `create_spreadsheet` | Create a new empty spreadsheet |
| `duplicate_spreadsheet` | Clone an entire spreadsheet (all tabs and data) |

### Task-sheet blocks (months, sections)

| Tool | What it does |
|---|---|
| `insert_block` | Create a new block (e.g. a month section) copying the look of existing ones — separator row, repeated header and blank rows ready to fill |

### Data validation and conditional formatting

| Tool | What it does |
|---|---|
| `set_data_validation` | Create a dropdown on a range: the cell only accepts the given values |
| `add_conditional_format` | Auto-color cells that match a condition (e.g. green for DONE) |
| `delete_conditional_format` | Remove a conditional-format rule by index |

### Borders, merging, banding

| Tool | What it does |
|---|---|
| `set_borders` | Add borders (outer and/or inner) to a range |
| `merge_cells` | Merge cells, e.g. for a block title spanning several columns |
| `unmerge_cells` | Undo merged cells in a range |
| `add_banding` | Apply alternating row colors ("zebra stripes") |

### Data cleanup

| Tool | What it does |
|---|---|
| `delete_duplicates` | Remove duplicate rows from a range, keeping the first occurrence |
| `trim_whitespace` | Strip leading/trailing/extra whitespace from cells — a stray space breaks exact-match dropdowns and formulas |
| `text_to_columns` | Split a text column into several by a separator |
| `auto_fill` | Continue a series automatically (dates, numbers, formulas), like dragging the corner handle |

### Organization

| Tool | What it does |
|---|---|
| `group_rows` | Group rows so they can be collapsed/expanded with the +/- in the margin |
| `ungroup_rows` | Undo a row grouping |
| `set_rows_visibility` | Hide or show rows |
| `set_column_width` | Fix the width of one or more columns, in pixels |
| `freeze_rows` | Freeze the top N rows |
| `set_filter` | Turn on the header filter (the funnel icons for sort/filter) |
| `clear_filter` | Remove the filter from a tab |

### Protection, ranges, cell operations

| Tool | What it does |
|---|---|
| `protect_range` | Protect a range from accidental edits |
| `unprotect_range` | Remove a protection |
| `add_named_range` | Name a range so it can be referenced in formulas without breaking when rows move |
| `copy_range` | Copy a range to another location, choosing what to copy (values, format, or both) |
| `cut_paste_range` | Move a range of cells (cut from source, paste into destination, with formatting) |

## Semantic colors for `format_range`

| Name | Use |
|---|---|
| `hecho` | Light green — task done |
| `in_progress` | Light yellow — in progress |
| `por_hacer` | Light grey — pending |
| `alta` | Light red — high priority |
| `media` | Light yellow — medium priority |
| `baja` | Light blue — low priority |
| `header` | Steel blue — table header |
| `white`, `red`, `green`, `blue`, `yellow`, `orange`, `purple`, `grey` | Basic colors |
| `#RRGGBB` | Direct hex |

## Safety

- Every tool carries MCP Tool Annotations from the spec (`readOnlyHint`, `destructiveHint`,
  `idempotentHint`, `openWorldHint`) so a client can tell read-only calls from destructive ones
  without guessing from the name.
- Execution errors propagate as real MCP protocol errors (`isError=true`) — never as a JSON
  payload that looks like a success.
- Risky writes carry a `confirmed: bool = False` parameter. Without it, the tool returns a preview
  of what it would do (and why it's flagged) instead of executing: `write_range`, `update_cell`,
  `append_rows`, `clear_range`, `delete_rows`, `insert_block`, `move_rows`, `delete_duplicates`,
  `cut_paste_range`, `delete_sheet`, `delete_columns`. What's considered risky: overwriting cells
  that already have content, touching frozen header rows, merged or protected cells, or writing a
  value outside a column's dropdown (which silently breaks its conditional-format color). Filling
  empty cells and appending to the end run without friction — they can't destroy anything.

These sheets are treated as **visual documents, not databases**: headers aren't necessarily in row
1, cells can be merged, and color often comes from conditional formatting that triggers on exact
values. Call `describe_sheet` before writing to a tab you don't already know its structure — it
tells you where the header and first free row are, and which values each dropdown column accepts.

## Setup

1. Create a Google Cloud project (or reuse one) and enable the
   **Google Sheets API**, **Google Docs API** and **Google Drive API**
   (the token scopes cover all three, so you can reuse it with the sibling
   `google-docs-mcp` server if you want).
2. Create an OAuth 2.0 Client ID of type "Desktop app" and download it as
   `client_secret.json`.
3. If the app is in "Testing" mode, add your Google account as a test user.
4. Install dependencies:
   ```bash
   python3 -m venv .venv
   source .venv/bin/activate
   pip install -r requirements.txt
   ```
5. Run the OAuth flow once:
   ```bash
   CLIENT_SECRET_PATH=~/.config/google-sheets-mcp/client_secret.json \
     python3 setup_auth.py
   ```
   This opens a browser — log in and grant access.

## MCP client configuration

```json
{
  "mcpServers": {
    "google-sheets": {
      "command": "/path/to/.venv/bin/python3",
      "args": ["/path/to/google-sheets-mcp/server.py"],
      "env": {
        "GOOGLE_WORKSPACE_TOKEN_PATH": "~/.config/google-workspace-mcp/token.json"
      }
    }
  }
}
```

| Env var | Default | Purpose |
|---|---|---|
| `GOOGLE_WORKSPACE_TOKEN_PATH` | `~/.config/google-workspace-mcp/token.json` | Path to the OAuth token |

## Suggested task-sheet structure

Standard columns (row 1 = header):
`OWNER | START | END | TASK | TOPIC | STATUS | PRIORITY | DETAILS`

Suggested status values: `TODO`, `IN PROGRESS`, `DONE`
Suggested priorities: `HIGH`, `MEDIUM`, `LOW`

## License

MIT — see [LICENSE](LICENSE).