Manage Tools
create_spreadsheet, manage_sheets, copy_sheet, batch_update_sheet, restore_snapshot.
create_spreadsheet
Creates a new blank spreadsheet and returns its ID and URL.
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | ✓ | Name of the new spreadsheet |
// Response
{ "spreadsheetId": "1BxiMVs0XRA...", "url": "https://docs.google.com/spreadsheets/d/..." }manage_sheets
Add, rename, or delete tabs inside a spreadsheet using the Sheets REST API. No Apps Script required.
| Parameter | Type | Required | Description |
|---|---|---|---|
spreadsheetId | string | ✓ | Target spreadsheet |
action | enum | ✓ | add = create new tab, rename = change tab title, delete = remove tab |
sheetId | number | ✗ | Required for rename and delete. Get from list_sheets. |
title | string | ✗ | Required for add and rename. |
confirmDelete | boolean | ✗ | Required to be true when deleting a non-empty sheet. |
The confirmDelete guard
Before executing a delete, sheets-mcp reads column A of the target sheet to count rows. If the sheet has data and confirmDelete is not true, the delete is blocked:
Delete blocked: sheet "Revenue" contains 243 rows of data.
Pass confirmDelete: true to permanently delete this sheet. THIS CANNOT BE UNDONE.This is an AX safety gate — agents must explicitly acknowledge destructive deletes.
Rename warning (important for agents)
A _warnings field is always returned on successful renames:
{
"ok": true,
"action": "rename",
"_warnings": [
"If other sheets contain formulas referencing the old name, they may be broken.
Google Sheets auto-updates references on rename via the UI but NOT via the API."
]
}Warning: Renaming a sheet via the API does not update cross-sheet formula references like =OldName!A1. Those formulas break silently. Google Sheets only auto-fixes references during in-app UI renames.
Cache invalidation
On every successful manage_sheets call, the sheet_snapshots cache for the spreadsheet is fully cleared so subsequent reads see fresh data.
Examples
// Add a new tab
{ "spreadsheetId": "...", "action": "add", "title": "June 2026" }
// Rename
{ "spreadsheetId": "...", "action": "rename", "sheetId": 812345678, "title": "Q3 2026" }
// Delete a non-empty sheet (requires explicit confirmation)
{ "spreadsheetId": "...", "action": "delete", "sheetId": 812345678, "confirmDelete": true }copy_sheet
Copies an existing tab to another spreadsheet using spreadsheets.sheets.copyTo.
| Parameter | Type | Required | Description |
|---|---|---|---|
spreadsheetId | string | ✓ | Source spreadsheet |
sheetId | number | ✓ | Numeric sheet ID (from list_sheets) |
destinationSpreadsheetId | string | ✓ | Target spreadsheet |
// Response — returns the new sheet's ID in the destination spreadsheet
{ "sheetId": 912345678 }Tip:
Typical template workflow:
create_spreadsheet→copy_sheet(template tab) →write_range(populate data) →format_cells(apply branding).
batch_update_sheet
Raw passthrough for the Google Sheets batchUpdate API. Use for everything not covered by other tools: charts, merges, column resize, data validation, conditional formatting.
| Parameter | Type | Required | Description |
|---|---|---|---|
spreadsheetId | string | ✓ | Target spreadsheet |
requests | object[] | ✓ | Array of raw batchUpdate request objects |
Example — auto-resize all columns
{
"spreadsheetId": "...",
"requests": [{
"autoResizeDimensions": {
"dimensions": { "sheetId": 0, "dimension": "COLUMNS", "startIndex": 0, "endIndex": 10 }
}
}]
}Example — freeze header row
{
"requests": [{
"updateSheetProperties": {
"properties": { "sheetId": 0, "gridProperties": { "frozenRowCount": 1 } },
"fields": "gridProperties.frozenRowCount"
}
}]
}Refer to the Google Sheets batchUpdate reference for the full request type catalogue.
restore_snapshot
Lists or restores pre-write snapshots from the write_history table. Every successful write_range and transform_range call saves the previous range values before writing.
| Parameter | Type | Required | Description |
|---|---|---|---|
spreadsheetId | string | ✓ | Target spreadsheet |
action | enum | ✓ | list = show available snapshots, restore = write a snapshot back |
snapshotId | string (UUID) | ✗ | Required for restore. Get the UUID from list. |
action: "list"
Returns the last 10 snapshots for the spreadsheet, ordered newest-first:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"range_key": "Sheet1!A1:D50",
"tool_name": "write_range",
"created_at": "2026-05-13T07:45:00Z",
"rows": 49
}
]action: "restore"
Writes the before_values back to range_key using valueInputOption: RAW. Runs the same formula safety guards as a normal write — if the range now contains formulas, it will block unless allowFormulaOverwrite is set (not available on restore — resolve conflicts manually first).
Snapshot retention
prune_write_history() is called after every insert. It keeps the last 50 snapshots per user_id + spreadsheet_id. Older snapshots are permanently deleted.
AX recommended restore workflow
1. restore_snapshot(action: "list")
→ find the snapshot ID closest to before the bad write
2. restore_snapshot(action: "restore", snapshotId: "...")
→ range is written back to its previous stateWarning: Snapshots are only saved when the pre-write range is ≤ 1,000 rows and ≤ 2 MB. For large ranges, the snapshot is skipped silently. Check rows in the list output — if 0, no snapshot was taken.