sheets-mcp docs
Tools

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.

ParameterTypeRequiredDescription
titlestringName 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.

ParameterTypeRequiredDescription
spreadsheetIdstringTarget spreadsheet
actionenumadd = create new tab, rename = change tab title, delete = remove tab
sheetIdnumberRequired for rename and delete. Get from list_sheets.
titlestringRequired for add and rename.
confirmDeletebooleanRequired 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.

ParameterTypeRequiredDescription
spreadsheetIdstringSource spreadsheet
sheetIdnumberNumeric sheet ID (from list_sheets)
destinationSpreadsheetIdstringTarget spreadsheet
// Response — returns the new sheet's ID in the destination spreadsheet
{ "sheetId": 912345678 }

Tip:

Typical template workflow: create_spreadsheetcopy_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.

ParameterTypeRequiredDescription
spreadsheetIdstringTarget spreadsheet
requestsobject[]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.

ParameterTypeRequiredDescription
spreadsheetIdstringTarget spreadsheet
actionenumlist = show available snapshots, restore = write a snapshot back
snapshotIdstring (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.

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 state

Warning: 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.

On this page