Write Tools
write_range, append_rows, clear_range, format_cells — modify sheet data and formatting.
Warning: All write tools run safety checks before executing. Writes that would destroy formula cells, overlap Protected Ranges, or cross structured note guards are blocked by default. Formula overwrite blocks include the existing formula, proposed value, and a small diff preview. Pass overrides only when intentionally bypassing the specific guard, and confirm formula previews before passing allowFormulaOverwrite: true.
write_range
Writes a 2D array of values to a specific range, overwriting existing content.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
spreadsheetId | string | ✓ | — | Target spreadsheet |
range | string | ✓ | — | A1 notation, e.g. Sheet1!A1 |
values | any[][] | ✓ | — | 2D array of cell values |
valueInputOption | enum | ✗ | USER_ENTERED | RAW or USER_ENTERED |
allowFormulaOverwrite | boolean | ✗ | false | Bypass formula + ARRAYFORMULA spill protection |
allowNoteGuardOverride | boolean | ✗ | false | Bypass blocking note guards after confirming [WARN], [FORMULA], or [LOCK] notes are safe to override |
valueInputOption
| Value | Behaviour |
|---|---|
USER_ENTERED | Values parsed as if typed by a human — =SUM(A1:A5) becomes a live formula, $100 becomes currency |
RAW | Values stored literally — =SUM(A1:A5) is stored as the text string =SUM(A1:A5) |
Pre-write snapshot
Before writing, write_range fetches the current range values and saves them to write_history. This snapshot can be restored via restore_snapshot. Snapshot is skipped silently if the range exceeds 1,000 rows or 2 MB.
Note guard preflight
Before committing, write tools fetch notes for the target columns' header row and, for small target ranges, exact target-cell notes. Structured notes with [WARN], [FORMULA], [LOCK], [DO_NOT_WRITE], or [NO_WRITE] block the write unless allowNoteGuardOverride: true is passed. [INFO] remains read-visible metadata and does not block.
Example blocking error:
Write blocked — target range intersects cell notes marked as write guards:
Sheet1!B1 header note [FORMULA] Do not write static values here.
Pass allowNoteGuardOverride: true only after confirming the note is safe to override.Response
{ "updatedCells": 12 }
// With protection warnings (non-blocking):
{ "updatedCells": 12, "_warnings": ["⚠ Target range has a user-set protection warning."] }
// With explicit note guard override:
{ "updatedCells": 12, "_warnings": ["⚠ Overriding note guard at Sheet1!B1 header note [FORMULA]: Do not write static values here"] }Formula overwrite blocks
If write_range targets existing formula cells, the write is blocked before any mutation. The error includes the affected addresses plus a preview for each formula cell:
write_range blocked because the target range contains formula cells (1): Sheet1!J1.
Formula overwrite preview: Sheet1!J1 existing formula "=ARRAYFORMULA(...UPPER(...))" -> proposed formula "=ARRAYFORMULA(...LOWER(...))" (replace "UPP" with "LOW").
Pass allowFormulaOverwrite: true only after confirming the existing formula vs proposed value diff is intentional.Use the preview to confirm formula maintenance edits. Do not pass allowFormulaOverwrite just to silence the guard.
ARRAYFORMULA column blocks
If the target range overlaps a column where the schema scan found an ARRAYFORMULA anchor in rows 1-25, the write is blocked before mutation. The error names the generated columns, the anchor cells, and the safest range change:
Write blocked — target range Data!D2:N100 overlaps ARRAYFORMULA-owned column(s): E (anchor Data!E1), F (anchor Data!F1), G (anchor Data!G1), J (anchor Data!J1).
Guard reason: writing static values into generated column(s) E, F, G, J can replace spill output and break downstream formulas.
Safest minimal write: split or narrow the write range to exclude column(s) E, F, G, J.
Pass allowFormulaOverwrite: true only after confirming this formula/spill overwrite is intentional.Cache invalidation
After a successful write, the entire sheet_snapshots cache for the spreadsheet is cleared so subsequent read_range and analyze_range calls see fresh data.
Mid-operation protection conflict
If a 403 or protection-related error occurs during (not before) the write — meaning the sheet's protection changed between the guard check and the actual API call — the error returned is:
Write failed — a protected range conflict was detected mid-operation.
The sheet's protection settings may have changed.
Call read_range to re-inspect the current schema before retrying.append_rows
Appends rows to the bottom of an existing data table without overwriting.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
spreadsheetId | string | ✓ | — | Target spreadsheet |
range | string | ✓ | — | A1 range containing the table (e.g. Sheet1!A1) |
values | any[][] | ✓ | — | 2D array of rows to append |
valueInputOption | enum | ✗ | USER_ENTERED | RAW or USER_ENTERED |
insertDataOption | enum | ✗ | INSERT_ROWS | INSERT_ROWS or OVERWRITE |
allowFormulaOverwrite | boolean | ✗ | false | Bypass ARRAYFORMULA spill protection |
allowNoteGuardOverride | boolean | ✗ | false | Bypass blocking target-column note guards after confirming they are safe |
Tip:
Always use the default
INSERT_ROWS.OVERWRITEreplaces cells at the detected table bottom rather than inserting new rows — which can destroy existing data below the table.
Note
append_rowsalso hasallowFormulaOverwritebecause the append target might land in an ARRAYFORMULA spill zone. The guard is the same aswrite_range.
clear_range
Clears all values in a range while preserving formatting, data validation, and conditional formatting rules.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
spreadsheetId | string | ✓ | — | Target spreadsheet |
range | string | ✓ | — | A1 range to clear, e.g. Sheet1!A1:D10 |
allowNoteGuardOverride | boolean | ✗ | false | Bypass blocking header/cell note guards after confirming they are safe |
Note
clear_rangeonly removes cell values. To also remove formatting, usebatch_update_sheetwith arepeatCellrequest andfields: "userEnteredFormat".
format_cells
Applies visual formatting to a cell range using zero-based grid indexes (not A1 notation).
| Parameter | Type | Required | Description |
|---|---|---|---|
spreadsheetId | string | ✓ | Target spreadsheet |
sheetId | number | ✓ | Numeric sheet ID (from list_sheets) |
startRowIndex | number | ✓ | Zero-based, inclusive |
endRowIndex | number | ✓ | Zero-based, exclusive |
startColumnIndex | number | ✓ | Zero-based, inclusive |
endColumnIndex | number | ✓ | Zero-based, exclusive |
bold | boolean | ✗ | — |
italic | boolean | ✗ | — |
fontSize | number | ✗ | Points |
textColor | { red, green, blue } | ✗ | RGB decimals 0–1 |
backgroundColor | { red, green, blue } | ✗ | RGB decimals 0–1 |
horizontalAlignment | LEFT | CENTER | RIGHT | ✗ | — |
verticalAlignment | TOP | MIDDLE | BOTTOM | ✗ | — |
wrapStrategy | OVERFLOW_CELL | LEGACY_WRAP | CLIP | WRAP | ✗ | — |
numberFormat | { type, pattern } | ✗ | Sheets number format type + pattern |
Index mapping
Row 1 (header) = startRowIndex: 0, endRowIndex: 1. Column A = startColumnIndex: 0, Column B = startColumnIndex: 1.
Color format
Colors are RGB decimals 0–1, not hex:
{ "red": 0.35, "green": 0.62, "blue": 0.56 } // #5A9E8FnumberFormat types
TEXT, NUMBER, PERCENT, CURRENCY, DATE, TIME, DATE_TIME, SCIENTIFIC
// Currency with 2 decimal places
"numberFormat": { "type": "CURRENCY", "pattern": "$#,##0.00" }
// Date
"numberFormat": { "type": "DATE", "pattern": "yyyy-MM-dd" }Full example — bold green header row
{
"spreadsheetId": "...",
"sheetId": 0,
"startRowIndex": 0,
"endRowIndex": 1,
"startColumnIndex": 0,
"endColumnIndex": 4,
"bold": true,
"backgroundColor": { "red": 0.35, "green": 0.62, "blue": 0.56 },
"textColor": { "red": 1, "green": 1, "blue": 1 }
}