> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vespper.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Suggestions

> Propose an agent's edits for the user to review before they change the document.

By default, `edit_document` changes the session document. In suggestion mode,
the Vespper SDK proposes the edits instead. The document stays unchanged, and
your code receives each edit as a suggestion. Your client shows the
suggestions, the user reviews and optionally modifies them, and your API
applies only the ones the user accepts.

## How it works

1. Patch the MCP client with `suggest: true`.
2. When the agent calls `edit_document`, the SDK resolves each edit against the
   session's current document without applying it, and returns it as a
   suggestion.
3. Your client shows each suggestion. The user can modify its HTML, then apply
   or discard it.
4. Your API applies the accepted suggestions with `applyEdits()` or
   `apply_edits()`.

## Enable suggestion mode

Pass `suggest` when you patch the MCP client:

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  await client.patchMCPTools({
    mcp,
    sessionId,
    suggest: true,
  });
  ```

  ```python Python theme={"system"}
  await client.patch_mcp_tools(
      mcp=mcp,
      session_id=session_id,
      suggest=True,
  )
  ```
</CodeGroup>

`read_document` and `search_document` work as before. `edit_document` no
longer changes the document: it only locates each edit in the document and
returns it as a suggestion.

For example, when the agent proposes changing "grew steadily" to "grew 12%" in
[`sample.docx`](/samples/sample.docx), `edit_document` returns:

```json theme={"system"}
{
  "ok": true,
  "status": "proposed",
  "count": 1,
  "failed_edits": [],
  "message": "Proposed 1 edit(s) for the user's review. The document does not change until the user accepts them, so do not re-read it to verify and do not resend these edits.",
  "suggestions": [
    {
      "index": 1,
      "old": "<p class=\"leading-[115%]\">This report summarizes our Q4 performance. Revenue grew steadily across all regions, and operating margins improved compared to the prior quarter.</p>",
      "new": "<p class=\"leading-[115%]\">This report summarizes our Q4 performance. Revenue grew 12% across all regions, and operating margins improved compared to the prior quarter.</p>",
      "part": "word/document.xml"
    }
  ],
  "css": "body,table{font-family:var(--minorHAnsi);font-size:11pt;...}\n.leading-\\[115\\%\\] {\n    line-height: 115%\n}"
}
```

See [Suggestion mode](/primitives/edit-document#suggestion-mode) in the API
reference for every field.

## Apply accepted suggestions

Send the accepted suggestions to `applyEdits()` or `apply_edits()` as
`old`/`new` pairs. A suggestion sent back unchanged applies exactly the proposed
edit. If the user modified a suggestion, send their HTML as `new`.

Edits apply in parallel, and each outcome's `position` is the edit's index in
the list you passed. `applyEdits()` accepts any session ID, so the request that
applies suggestions doesn't have to be the one that ran the agent.

This example proposes one edit to [`sample.docx`](/samples/sample.docx), then
applies it. In your app, the agent makes the `edit_document` call, and the user
reviews the suggestion before your API applies it.

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  import "dotenv/config";
  import { writeFileSync } from "node:fs";
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
  import Vespper, { type SuggestEditResult } from "vespper";

  const OLD =
    '<p class="...">This report summarizes our Q4 performance. Revenue grew steadily across all regions, and operating margins improved compared to the prior quarter.</p>';

  const client = new Vespper();
  const sessionId = await client.openSession("./sample.docx");
  const mcp = new Client({ name: "suggestions-example", version: "0.1.0" });
  await mcp.connect(
    new StreamableHTTPClientTransport(new URL("https://mcp.vespper.com/mcp"), {
      requestInit: {
        headers: { Authorization: client.authorizationHeader },
      },
    }),
  );
  await client.patchMCPTools({ mcp, sessionId, suggest: true });

  // Your agent makes this call. With suggest: true, it only locates the edit
  // in the document and returns it as a suggestion. Nothing is applied.
  const result = await mcp.callTool({
    name: "edit_document",
    arguments: {
      edits: [{ old: OLD, new: OLD.replace("grew steadily", "grew 12%") }],
    },
  });
  const { suggestions } = result.structuredContent as SuggestEditResult;

  // After the user accepts the suggestions, apply them.
  const accepted = suggestions.map((suggestion) => ({
    old: suggestion.old,
    new: suggestion.new,
  }));
  for await (const outcome of client.applyEdits(sessionId, accepted, {
    author: "Vespper Agent",
  })) {
    if (!outcome.ok) console.error(outcome.position, outcome.reason);
  }

  writeFileSync("sample-redlined.docx", client.getSessionDocument(sessionId));
  await mcp.close();
  await client.closeSession(sessionId);
  ```

  ```python Python theme={"system"}
  import asyncio
  from pathlib import Path

  from dotenv import load_dotenv
  from mcp import ClientSession
  from mcp.client.streamable_http import streamablehttp_client
  from vespper import EditPair, SuggestEditResult, Vespper


  load_dotenv()

  OLD = (
      '<p class="...">This report summarizes our Q4 performance. Revenue grew '
      "steadily across all regions, and operating margins improved compared to "
      "the prior quarter.</p>"
  )


  async def main():
      client = Vespper()
      session_id = await client.open_session("./sample.docx")

      async with streamablehttp_client(
          "https://mcp.vespper.com/mcp",
          headers={"Authorization": client.authorization_header},
      ) as (read, write, _):
          async with ClientSession(read, write) as mcp:
              await mcp.initialize()
              await client.patch_mcp_tools(
                  mcp=mcp,
                  session_id=session_id,
                  suggest=True,
              )

              # Your agent makes this call. With suggest=True, it only locates the
              # edit in the document and returns it as a suggestion. Nothing is applied.
              result = await mcp.call_tool(
                  "edit_document",
                  {
                      "edits": [
                          {
                              "old": OLD,
                              "new": OLD.replace("grew steadily", "grew 12%"),
                          }
                      ]
                  },
              )
              proposal = SuggestEditResult.model_validate(result.structuredContent)

      # After the user accepts the suggestions, apply them.
      accepted = [
          EditPair(old=suggestion.old, new=suggestion.new)
          for suggestion in proposal.suggestions
      ]
      async for outcome in client.apply_edits(
          session_id, accepted, author="Vespper Agent"
      ):
          if not outcome.ok:
              print(outcome.position, outcome.reason)

      Path("sample-redlined.docx").write_bytes(
          client.get_session_document(session_id)
      )
      await client.close_session(session_id)
      await client.close()


  asyncio.run(main())
  ```
</CodeGroup>

## Stream suggestions

In suggestion mode, a patched `edit_document` call doesn't change the
document. It only locates each pair in the session document and returns early
with the suggestion, so it's safe to call for each pair while the model is
still writing.

To show suggestions as they stream, parse the `edit_document` arguments as
they arrive. Call the patched tool with each `old`/`new` pair once it's
complete, and send the suggestion to the client.

The
[ONLYOFFICE add-in](/cookbooks/live-editing/onlyoffice-add-in) shows the
complete wiring of suggestions to a UI layer.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.