edit_document applies a list of edits in one call. By default, each edit is
recorded as a Word tracked change so a human can accept or reject it.
Parameters
array
required
The edits to apply. Maximum 20 per call.
_meta under com.vespper/session-id. The
optional com.vespper/author field controls the name Word shows on tracked
changes, and com.vespper/track-changes controls whether new edits are tracked.
Vespper SDK auto-patching supplies these values for you. See
Document sessions.
Control tracked changes behavior
By default,edit_document records new edits as Word tracked changes. To apply
new edits directly, configure auto-patching or set the value manually:
- Vespper SDK
- Raw MCP metadata
Returns
boolean
true whenever the document changed - including a batch where only some edits
landed.integer
How many edits actually applied.
string
The latest cumulative session document, base64-encoded. Decode it to save the
.docx or publish it to a live editor. Subsequent tool calls automatically
use this updated session document.integer
Size of the patched document.
integer
Revision of the cumulative session document returned by this call.
array
Present only on a partial batch. Each entry is
{ index, reason }, where index is 1-based
over the edits you sent.string
Agent-facing summary of what landed and what didn’t.
number
Metered cost of this call.
Edits succeed or fail individually
When nothing applies you get an error instead and the session document is untouched; then fix the listed edits and resend the whole batch.Example
document session not found without changing a document.
Suggestion mode
When the Vespper SDK patches the MCP client withsuggest: true,
edit_document doesn’t change the document. It locates each edit in the
session document and returns it as a suggestion for the user to review. See
Suggestions to enable it and apply the accepted
suggestions.
When the agent proposes changing “grew steadily” to “grew 12%” in
sample.docx, edit_document returns:
boolean
true when at least one edit was proposed.string
Always
"proposed".integer
How many edits were proposed.
array
Edits that could not be proposed. Each entry is
{ index, code, reason },
where index is 1-based over the edits you sent.string
Agent-facing summary. It tells the model the document has not changed, so the
model does not re-read the document or resend the edits.
array
The proposed edits.
string
Stylesheet for rendering the suggestions’ HTML: the document’s styles and the
utility classes the suggestions use. It is unscoped, so scope it to the
element that shows your suggestions.
Keep
suggestions and css out of the model’s context. Mastra and Vercel AI
SDK patching hides them from the model for you. In a native loop, return only
message as the tool output, as the Quickstart does.Anchoring rules
Make old unique
If a block is identical to another in the document - two blank Print Name ____ lines, repeated
checkbox rows, a lone [MAILING ADDRESS] - it matches several spots and the edit fails with
multiple matches. Disambiguate by extending old to include a neighbouring block that is
unique: a run of consecutive blocks copied verbatim in order, with every change in new.
Non-adjacent blocks cannot share one old; give them separate edits.
Lazy classes
Writeclass="..." - literally three dots - for every class attribute in both old and new.
It means “keep this class”, and saves you pasting real class lists.
Spell a class out only when changing the class is the edit, to break a multiple matches tie,
or on a brand-new inserted block: it has no counterpart in old to inherit from, so it must
spell its class out.
What you cannot anchor on
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 roughly 10× slower. Every edit must target a different block. Two edits touching the same block, or the same consecutive span, overlap and both are dropped - combine those changes into a singleold / new pair.
Keep each edit small. One edit carrying a huge new - beyond roughly 10,000 characters - risks
truncation or a timeout and fails the whole edit. When writing a lot of new content, break it
into several edits of a few blocks each, anchored on different neighbouring blocks.
Resolving an existing tracked change
read_document renders pending changes as <ins> and <del> elements carrying a
data-author. To act on one, copy its whole block verbatim into old, keeping the wrappers and
their data-author, then write new as the block’s final state.
Two rules constrain this. First, resolve only the change you were asked about: a block often
carries several pending changes, and every one you were not asked about must appear in
new
exactly as it stands in old. Pending is the default state, so clearing a change nobody
mentioned is a wrong edit even when the resulting words look right. Second, the only <ins> and
<del> allowed in new are the ones already in old - never write one of your own. The tool
handles any new revision markup when tracking is enabled.
Rejecting Jane’s insertion:

