Skip to main content
Glama

Edit a Google Doc

edit_doc

Replace 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 ![alt](src); 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

NameRequiredDescriptionDefault
pageNowhich header/footer, when a doc defines more than one (default-page, first-page, even-page). Omit to use whichever exists.
pathYesThe 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.
accountNoGoogle account email to use. Defaults to GDOCS_DEFAULT_ACCOUNT, or the sole account.
baseDirNoabsolute dir to resolve relative local image paths and contentFile against
segmentNowhich 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.
suggestNosend the edit as suggestions for a person to accept or reject, instead of applying it (default false)
new_stringNoreplacement markdown (or use contentFile)
old_stringYesexact text to replace (quote a unique slice from read_doc)
confirmLossNothe 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.
contentFileNopath 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_allNoreplace every occurrence (default false)

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed9 schema fields changedv1.0.0
    • addedInput schema / properties / baseDir
      Added value: +{
      +  "description": "absolute dir to resolve relative local image paths and contentFile against",
      +  "type": "string"
      +}
    • addedInput schema / properties / confirmLoss
      Added value: +{
      +  "description": "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.",
      +  "type": "string"
      +}
    • addedInput schema / properties / contentFile
      Added value: +{
      +  "description": "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.",
      +  "type": "string"
      +}
    • removedInput schema / properties / documentId
      Removed value: -{
      -  "description": "Google Doc id",
      -  "type": "string"
      -}
    • changedInput schema / properties / new_string / description
      Previous value: -"replacement text"New value: +"replacement markdown (or use contentFile)"
    • addedInput schema / properties / path
      Added value: +{
      +  "description": "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.",
      +  "type": "string"
      +}
    • addedInput schema / properties / suggest
      Added value: +{
      +  "description": "send the edit as suggestions for a person to accept or reject, instead of applying it (default false)",
      +  "type": "boolean"
      +}
    • removedInput schema / properties / tab
      Removed value: -{
      -  "description": "Target a specific tab by tabId or title (from list_tabs). Defaults to the first tab.",
      -  "type": "string"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "documentId",
      -  "old_string",
      -  "new_string"
      -]New value: +[
      +  "path",
      +  "old_string"
      +]
  2. Changed2 schema fields changedv0.5.0
    • addedInput schema / properties / page
      Added value: +{
      +  "description": "which header/footer, when a doc defines more than one (default-page, first-page, even-page). Omit to use whichever exists.",
      +  "enum": [
      +    "default",
      +    "first",
      +    "even"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / segment
      Added value: +{
      +  "description": "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.",
      +  "enum": [
      +    "body",
      +    "header",
      +    "footer"
      +  ],
      +  "type": "string"
      +}
  3. First observedv0.1.1

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so extensively: markup-tolerant matching, ambiguity handling, element preservation/deletion/creation, atomic refusal for unsupported constructs, table comparison edits, comment-loss confirmation, and suggestion behavior are all disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first sentence is front-loaded and many details are relevant, but the description is a dense block with no paragraph or heading structure, making key rules hard to scan. It is overlong for easy agent consumption even if much of the content is valuable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex, 11-parameter mutation tool with no annotations and no output schema, the description covers creation/insertion, empty-document handoff, headers/footers, table and style editing, refusals, suggestions, and result shape. An agent has enough context to invoke it correctly in the described scenarios.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds substantial meaning for old_string matching, new_string markdown/HTML, suggest, and confirmLoss. It adds little beyond the schema for page, segment, account, baseDir, or replace_all.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a precise verb and resource: replace an exact unique text snippet in a Google Doc. It also distinguishes the operation from write_doc for empty documents and from read_doc by requiring a quoted slice from a read.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It names alternatives in several conditions, such as read_doc for quoting and write_doc for empty docs/tabs/headers, and explains suggest:true for review or non-owned docs. However, the when-to-use guidance is spread throughout rather than stated as a concise upfront rule, and there is no broad exclusion beyond empty content.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.