edit_document
Edit a Microsoft Word (.docx) document: apply a LIST of edits in ONE call.
The client controls whether edits appear as tracked changes or directly in place. The author
comes from developer-controlled _meta, not a tool argument; you cannot choose or change it.
'edits' is an array of { old, new }. For each edit:
'old': an HTML snippet copied VERBATIM from the document's current HTML (as returned by read_document) that matches EXACTLY ONE spot (prefer a whole block: /// heading). Copy the inner markup as-is -- keep any , , nested -- and only shorten class attributes (see LAZY CLASSES).
'new': the full replacement HTML for that block -- its FINAL text, with NO / of your own; the tool handles the configured review representation. Use "" to delete it.
DELETE OR REJECT AN ENTIRE TABLE ROW: use the complete ... as 'old' and "" as 'new'. Never empty the individual cells while preserving the ; that leaves a blank row in the document instead of removing the row.
THE DOCUMENT BODY AND EXPOSED HEADERS / FOOTERS ARE EDITABLE. A referenced running header/footer is emitted once as /; edit its blocks normally. Shared parts are not duplicated, so one edit updates every section/type listed in data-docx-refs.
COMMENTS ARE EDITABLE LIKE CONTENT. To comment, wrap the text in a bare and add a bare
Do NOT anchor on the following non-editable content:
FOOTNOTES / ENDNOTES: these auxiliary parts are read-only.
GLOBAL STYLES: the header at the TOP of the HTML, and adding or renaming a named style. To restyle, reuse an existing style class or apply a local Tailwind class on the block; editing the block is rejected.
MAKE EVERY 'old' UNIQUE. Each edit's 'old' must independently match exactly one location. NEVER reuse an ambiguous 'old' for different targets or rely on edit order to choose among repeated matches. To target duplicated blocks, expand EACH 'old' to a unique neighborhood by including an adjacent heading or block -- a CONSECUTIVE span copied verbatim. Preserve unchanged context in 'new'. A consecutive span can also replace adjacent blocks; NON-adjacent blocks require separate edits.
INSERTION ANCHORS ARE ALSO OOXML EXAMPLES. edit_document localizes only old
to x_old; the reconciler sees that OOXML with old and new, not the rest
of the document. Choose the smallest consecutive old containing the insertion
boundary and a structure the new content should resemble: a same-role list
item, a same-shape row, or one complete prior repeated block. Preserve the
example unchanged in new. Do not substitute an unrelated heading, blank
paragraph, or merged row merely because it is unique, including on retries.
Context is evidence, not permission to edit beyond the user's requested scope.
RETRY SAFETY: a failed edit does not make a weaker anchor valid. Keep the same document part, same-role examples, insertion boundary, and unchanged anchor text. Reduce only the new content when splitting a failed insertion. Do not switch to an unrelated header/footer, blank/title block, or tag-only fragment, and never alter preserved text merely to satisfy a validator. If the structurally correct anchor still fails, stop instead of broadening the edit.
LAZY CLASSES (default): write class="..." (literally three dots) for EVERY class attribute in BOTH 'old' and 'new' -- it means "keep this class". Don't paste the real class lists. Spell a class out only when changing the class IS the edit (e.g. bold), to break a "multiple matches" tie, or on a brand-new INSERTED block (one with no counterpart in 'old') -- it has no existing element to inherit from, so it must SPELL OUT its class, never "...".
class="..." is ALL-OR-NOTHING -- it stands for the ENTIRE class value, so it must be the whole attribute, written EXACTLY as class="..." with nothing else inside or after the quotes. NEVER keep a fragment of the real class beside it: class="..." bg-[#F6F4EF]" is INVALID (the whole class, bg-[#F6F4EF] included, lives INSIDE one class attribute, so this fails to locate). To keep a distinguishing class (e.g. a background), spell the WHOLE class out for that element instead of lazying it.
STYLE-ONLY CHANGES: when changing only a block's named document style, preserve its tag and
every unrelated class. Do NOT change the HTML tag merely because the target style commonly
renders with another tag, or replace a full class list with only the new style token -- the
other classes carry direct spacing, alignment, font, and layout that the request did not ask
you to remove. In 'old', use class="..." normally. In 'new', copy that block's COMPLETE
class value from read_document, remove only its previous named style token if it has one,
add the requested named style token, and leave every Tailwind utility unchanged. Keep
unrelated descendants lazy. Example, to change BodyText to IntenseQuote on
<p class="BodyText text-center mb-[6pt]">Summary</p>:
old: Summary
new: Summary
LISTS: list content is marker-free semantic HTML. Never type bullets, numbers, or
letters into paragraph text. Every item is <li><p>…</p></li>; nest a child
<ol>/<ul> inside its parent <li>. Ordered lists use type="1|a|A|i|I" and
start="…" when a restart matters.
When search_document returns one <li>, it is sufficient for changing or deleting only that
item's content. Before an insertion, reorder, nesting/unnesting, restart, split, merge, or
ordered/bullet conversion, call read_document for that page and inspect the parent <ol>/<ul>,
its type/start, and the affected siblings before building the structural edit.
Choose the edit shape by intent:
A direct
<p>or one<li>is only for changing or deleting that one item's content while keeping its list position, level, marker type, and numbering.An insertion, reorder, nesting/unnesting, restart, split, merge, or ordered/bullet conversion is structural. Put the smallest complete affected
<ol>/<ul>subtree in BOTHoldandnew; do not send loose sibling<li>elements for a new structural edit.A newly inserted block cannot inherit
class="...". Copy the complete formatting of the appropriate neighbouring item, spelling its classes out.
INSERTING INTO A CONTINUING NUMBERED LIST: separate <ol> wrappers do not
automatically continue merely because their classes and types match. If old
includes both the preceding N-1 item and following N item from one sequence,
preserve the following start="N" so that context proves natural continuation.
If only the separately wrapped following start="N" item is included, set it
to start="N+1" in new. Keep its content and formatting unchanged, and do
not seek out or renumber unrelated later fragments.
Examples (classes omitted only to keep these examples short): Insert between items: old: AlphaGamma new: AlphaBetaGamma Delete one item without changing the list structure: old: Obsolete new: "" Nest an item: old: ParentChild new: ParentChild Restart at five: old: First new: First
PREFER BATCHING: send all of a request's edits in one call when it makes sense -- they reconcile IN PARALLEL, so a 10-edit call takes about as long as one edit while 10 separate calls are ~10x slower (max 20 edits per call -- if a task needs more, send several batches of <=20). NO OVERLAPS: every edit must target a DIFFERENT block; two edits that touch the SAME block (or the same consecutive span) overlap and BOTH are dropped -- combine those changes into a SINGLE old/new pair.
KEEP EACH EDIT SMALL. Each edit is applied by a model with a bounded output, so ONE edit carrying a huge 'new' (say beyond ~10,000 characters -- several pages of new text) risks truncation or a timeout and fails the whole edit. When writing a LOT of new content, break it into MULTIPLE edits of a few blocks each: give each chunk its own { old, new } anchored on a DIFFERENT neighboring block (e.g. chunk 1 anchors on the block above and appends the first blocks; chunk 2 anchors on chunk 1's LAST block and appends the next ones -- but note within one batch edits must not overlap, so either anchor each chunk on a distinct existing block, or send the chunks as consecutive calls). Prefer many small edits over one giant edit; batching keeps them fast.
EDITS SUCCEED OR FAIL INDIVIDUALLY. A batch can apply in part: the result's 'count' is how many landed and 'failed_edits' lists the rest as { index, reason } ('index' is 1-based over the 'edits' you sent). The applied ones are ALREADY saved in the document, so NEVER resend the whole batch after a partial result -- that re-applies them and duplicates the text. Resend ONLY the listed index(es), fixed, or tell the user what couldn't be changed. When NOTHING applies you get an error instead and the document is untouched; then fix the listed edits and resend the whole batch.
Some successful edits need no new document mutation because the requested result is already
represented by an existing tracked change. Those return count: 0 plus a notices entry.
Do not retry an edit named by a notice.
SUCCESSFUL EDITS ARE COMPLETE: applied edits are already present in the returned document. Depending on client configuration, they appear as tracked changes or directly in place. If tracked changes are visible, deleted text can remain struck through until accepted; do not repeat a successful edit.
RESOLVE OR MODIFY AN EXISTING TRACKED CHANGE. read_document renders changes already in the
doc as (insertions) and (deletions), each with a data-author. To act on one,
copy its whole block verbatim into 'old' (keep the / and their data-author). Then
write 'new' as that block's final state, under two rules:
RESOLVE ONLY THE CHANGE YOU WERE ASKED ABOUT. A block often carries several pending changes. Every one you were not asked about must appear in 'new' exactly as it stands in 'old' -- wrapper, data-author and text untouched. Pending is the default state: clearing a change nobody mentioned is a WRONG edit even when the resulting words look right. "Mentioned" is semantic, not author-only: a revision is in scope when the request identifies its author, its text, its role in a combined change, or explicitly includes it in the requested final value. Apply the requested action to every such revision, even when several authors' wrappers contribute pieces of one accepted or rejected result.
The only / allowed in 'new' are those ALREADY in 'old'; keep their data-author untouched. Never create one -- the tool handles any new revision markup itself. FINAL ACCEPTED TEXT: read remaining text and omit text. Text requested to "read exactly" must match exactly, including punctuation and spacing, and omit proposal labels or brackets absent from the supplied phrase.
ACCEPT an insertion -> keep its text, drop the wrapper. ACCEPT a deletion -> drop the and its text.
REJECT an insertion -> drop the and its text. REJECT a deletion -> keep its text, drop the wrapper.
MODIFY a change -- anyone's, your own included -> keep its / wrapper AND its data-author exactly as they are, and edit only the TEXT inside. Never unwrap it, re-author it, or hand-write a nested / in it. Examples (author configured as "Dana"): Reject Jane's insertion: old: The quick brown fox new: The fox Accept Jane's insertion; Sam's was not mentioned, so it stays exactly as it was: old: A big red fast car new: A big red fast car Accept a combined version update whose requested final value explicitly includes both pieces: old: Version 2.1 new: Version 2.1 Modify Jane's insertion, dropping "brown " -- edit her text in place; Dana's deletion is recorded inside Jane's insertion automatically: old: The quick brown fox new: The quick fox
BEST PRACTICES:
MATCH THE NEIGHBOURS. Editing or adding a table cell, a list item or a heading? Look at its siblings first -- the other cells in that column, the other bullets in that list, the other headings at that level -- and give yours the SAME formatting, spelled out. Content that is right but plain where its column is bold counts as wrong.
FORM ROLE PRECEDENCE. Infer labels and entry cells from repeated row structure before considering style names or cell widths. A class such as
Answersdoes not make non-empty text a placeholder, and a narrow blank cell can still be its entry cell. When a non-empty cell is paired with an adjacent blank cell, preserve the non-empty cell and fill the blank unless explicitly told otherwise. An -only cell is blank; label padding is not an entry slot.PRESERVE BLANK WORD FIELDS. "Click or tap here to enter text" marks an empty content control. Copy its complete placeholder element, including class and text, only when cloning a same-role peer in the same repeated form structure whose HTML visibly contains that placeholder. When filling an existing field, replace it with the value. Never infer a content control merely from "field" or "line", add one to an ordinary blank cell, or substitute filler.
HIGHLIGHTING COMES FROM WORD'S FIXED PALETTE, not from arbitrary colours: black, blue, cyan, darkBlue, darkCyan, darkGray, darkGreen, darkMagenta, darkRed, darkYellow, green, lightGray, magenta, red, white, yellow. Some are named differently in Word's own menu -- PINK is magenta (#FF00FF), turquoise is cyan (#00FFFF), violet is darkMagenta (#800080), bright green is green (#00FF00), gray 25%/50% are lightGray/darkGray. Write the palette colour's hex (e.g. bg-[#FF00FF] for pink); a colour off the palette gets snapped to the nearest one it can paint, which for a pastel is usually a grey.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| edits | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||