Edit a Google Doc
edit_docReplace an exact, unique snippet in a Google Doc to update text, tables, or styles, preserve formatting and comments, and optionally send changes as suggestions.
Instructions
Replace an exact unique snippet of text in a Google Doc (like a local file Edit). old_string is matched markup-tolerantly; ambiguous matches return surrounding context to disambiguate; a table is matched as its rows, in the form read_doc shows it (a pipe table, or the HTML of a styled one). Elements read_doc shows are kept when old_string and new_string both hold them as read (a person chip inside a sentence you reword survives), deleted when new_string leaves them out, and created when new_string adds one: [^label] with a [^label]: note line, Nov 15, 2026 (the text’s shape picks the date format), for a person or for a Drive file, with its line, and --- for a rule. A column break, equation, page-number field, or link chip to anything but a Drive file cannot be made: it is refused before anything is sent, and a chip’s text is Google’s (a chip given other words is refused). A footnote’s note is edited by quoting its [^1]: line, and a dropdown’s list by quoting its line. INSERTING IS AN EDIT: quote the text to add after (or before), write it back in new_string, and add the new content to it. Text from old_string that comes back unchanged is kept as it is, with its style, comments and suggestions, so old_string | Total | $4,200 | and new_string | Total | $4,200 |\n\nPayment due within 30 days. leaves the table alone and adds the paragraph after it; a match that ends in a table puts the new blocks after the whole table. An anchor in the middle of a paragraph splits the paragraph there. A new block carries its own style, not its anchor’s: a paragraph added after a heading, a Title or a list item is Normal text with no bullet unless the markdown says otherwise, while text added inside an existing paragraph keeps that paragraph’s style. An empty doc, tab, header or footer has nothing to anchor on: use write_doc. new_string is the same markdown and inline HTML write_doc takes: headings, lists, tables, images (<img src width height> or ; a centered one is <p style="text-align:center"><img …></p>, local files resolve against baseDir), page breaks, and inline constructs (bold, italic, code, text, <u>, <span style="color:…;font-size:…pt">, <p style="…">, <p class="title">, and a page break as a line of its own: <div style="page-break-after:always"></div>, so new_string Title page\n<div style="page-break-after:always"></div> puts what follows on a new page; old_string can span a break) — the same spelling read_doc emits, so a read can be edited and written back. STYLE IS CSS, and this is the only way to change it. Restyle text you are not otherwise changing by giving the same words with different markup: old_string <p>4. Term, new_string <p style="margin-left:36pt; text-indent:-18pt">4. Term sends only style requests — no delete, no insert, so a long paragraph is never retyped and its words cannot change; markup missing from new_string is cleared (drop the wrapper to remove an indent). To restyle a whole named style, edit its rule in read_doc’s block: old_string p { font-family: Arial; font-size: 11pt, new_string p { font-family: Arial; font-size: 12pt becomes one updateNamedStyle and every paragraph that does not override it follows. An unsupported property (border, float, a px length, a typo) fails the edit before any request is sent, listing every offending line and the supported set. TABLE STYLE is CSS on the table’s HTML, changed the same way: read_doc shows a table with any cell or row style, wide or short columns, pinned rows or merged cells as with , (a minimum), (exactly the pinned rows), (border, padding, background, vertical-align) and colspan or rowspan, one row per line, while a table with nothing but Docs defaults stays a pipe table. old_string <td>Client</td>, new_string <td style="background:#eeeeee">Client</td> sends only the style request, and every cell whose words come back unchanged is kept, not retyped; styling a pipe table is giving the whole table back as HTML with the same words. A table edit that changes rows, columns or merges is a comparison: quote the rows around the change, write the rows you want, and only the difference is sent, an insertTableRow, deleteTableRow, insertTableColumn, deleteTableColumn, mergeTableCells or unmergeTableCells plus the text of the cells that changed, so cells you did not change keep their comments and style. A new row or column copies the style of the row above (the row below when the row above is a header row) or the column to its left (to its right for a new first column), and a style you write on it wins; links are not copied. A row or column you move is a delete and an insert: when comments quote its text the first call is refused with a confirmLoss string, so ask the user and pass it back as confirmLoss after their yes. A change that cannot be read cleanly (a row that could be an edit of either of two new rows, a table that shares no row with the old one, a column added without its line) is refused and says what to quote; a table is never rewritten whole. SUGGEST: pass suggest: true to send the edit as suggestions, for a person to accept or reject in Docs, when the user wants to review the change or the doc is not theirs; the result lists in suggestions the ids of the suggestions that now hold the edited text (a change next to your own pending suggestion joins it, and then its id is the one listed). It defaults to false, and then the edit is applied as live text: if the doc has pending suggestions from other reviewers, tell the user your edit sits alongside them as an accepted change. A change Docs cannot suggest (a table column width, for one) refuses the whole edit before anything is sent.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"path": "<path>",
"old_string": "<old_string>"
},
"name": "edit_doc"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | which header/footer, when a doc defines more than one (default-page, first-page, even-page). Omit to use whichever exists. | |
| path | Yes | The doc or tab: a Drive id or URL, /folder/doc, or /folder/doc/tab (nested: /folder/doc/tab/child). A doc with one tab needs no tab step; a doc with several is refused until one is named, and the refusal lists every tab path. | |
| account | No | Google account email to use. Defaults to GDOCS_DEFAULT_ACCOUNT, or the sole account. | |
| baseDir | No | absolute dir to resolve relative local image paths and contentFile against | |
| segment | No | which content tree to target: body (default), or the page header/footer. Header/footer content is invisible to a body read — a letterhead logo lives there. | |
| suggest | No | send the edit as suggestions for a person to accept or reject, instead of applying it (default false) | |
| new_string | No | replacement markdown (or use contentFile) | |
| old_string | Yes | exact text to replace (quote a unique slice from read_doc) | |
| confirmLoss | No | the confirmLoss string from the refusal of a table edit that moves a row or column whose text comments quote, passed back exactly, after the user has agreed that those comments are lost. Never needed otherwise. | |
| contentFile | No | path to a local markdown/text file to use as new_string, read directly by the server — preferred for long text. Absolute, or relative to baseDir. Mutually exclusive with new_string. | |
| replace_all | No | replace every occurrence (default false) |