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

# Tools for your agent

> Hand the document to whatever model you already run.

The SDK exposes the document tools from Revise's own agent. Each tool has a
JSON schema, runs without React state or browser focus, and works on document
structure rather than screen positions.

## Tool loop

```ts theme={null}
const definitions = editor.tools.getDefinitions();

const response = await yourModel.createMessage({
  messages,
  tools: definitions.map((tool) => ({
    name: tool.name,
    description: tool.description,
    input_schema: tool.inputSchema,
  })),
});

for (const call of response.toolCalls) {
  const result = await editor.tools.executeDynamic(call.name, call.input);
  messages.push({
    role: "tool",
    tool_call_id: call.id,
    content: result.ok
      ? [
          result.value.message ?? JSON.stringify(result.value.data),
          // Read and search results: the document itself, with block IDs.
          result.value.context?.html,
        ]
          .filter(Boolean)
          .join("\n\n")
      : `${result.error.code}: ${result.error.message}`,
  });
}
```

Send the schemas to the model and return the results. For reads and searches,
`message` is only a summary. The text the model edits is in `context.html`, so
send both. The definitions come from the same source as Revise's production
agent, with the same descriptions and constraints.

## Results

This API and `@reviseio/editor/server` return the same result type, so the same handling
code works in the browser and in Node:

```ts theme={null}
type ReviseToolResult<Name> =
  | { ok: true; value: ReviseToolResponse<Name> }
  | { ok: false; error: ReviseToolFailure<Name> }; // { callId, tool, code, message }

interface ReviseToolResponse<Name> {
  callId: string;
  tool: Name;
  message: string | null; // summary for the model
  data: ReviseToolData<Name>; // typed metadata, per tool
  context: { format: "revise-html"; html: string } | null;
  suggestionIds: string[] | null; // tracked changes this call created
}
```

<Warning>
  A rejected edit returns `ok: false`. It does not throw. Check `ok` and send
  `error.message` back to the model. The message explains why the call failed
  in terms the model can act on. `tools.call()` is the throwing variant for
  application code and raises a typed `ReviseToolError`.
</Warning>

Read and search tools return `context.html`. This HTML keeps block IDs, inline
formatting, tables, and notes. Mutation tools target those block IDs. Mutations
in suggesting mode list the tracked changes they created in `suggestionIds`.
Pass them to `review.acceptSuggestions()`.

## Working with blocks

The model reads part of the document, then edits by block ID:

```ts theme={null}
const read = await editor.tools.call("read_blocks_from_index", {
  index: 0,
  context_notes: "Looking for the liability clause",
});

await editor.tools.call("replace", {
  id: "b12",
  replacements: [
    {
      find: "capped at the fees paid",
      replace: "capped at two years of fees paid",
      occurrence: "unique",
    },
  ],
});
```

`context_notes` is how a model carries what it learned from one read into the
next, and the schemas require it. When your own code calls a tool through
`execute()` or `call()`, you can leave it out; it defaults to `"N/A"`. Calls
through `executeDynamic()` are passed on exactly as the model sent them.

<Tip>
  `replace` works inside **one** block. Edits in two blocks take two calls. A
  single call with finds in more than one block fails without applying any of
  them.
</Tip>

## Edit every match

Searching the active document returns a `search_result_id` for the full match
set, across all pages. Pass it to `replace`, `replace_block`, `style_blocks`,
or `remove_blocks` instead of an `id`. The edit applies to each matched block
separately, in one call:

```ts theme={null}
const search = await editor.tools.call("search_document", {
  queries: ["Acme Corp."],
  page: 0,
  context_notes: "Renaming the counterparty throughout",
});

await editor.tools.call("replace", {
  search_result_id: search.data.search_result_id,
  replacements: [
    { find: "Acme Corp.", replace: "Acme Holdings Ltd.", occurrence: "all" },
  ],
});
```

<Warning>
  A search result ID works only in the editor session that created it. If the
  matched blocks change, the call is rejected and nothing is applied. Run
  `search_document` again and use the new ID.
</Warning>

## Routing to a document

Every tool schema has an optional `document_id`. Omit it to target the active
document, or pass the ID of any ready document in the same editor:

```ts theme={null}
await editor.tools.call("set_title", { title: "Exhibit A" }, {
  documentId: "exhibit-a",
});

// or bind a controller once
const exhibit = editor.tools.forDocument("exhibit-a");
await exhibit.execute("measure_document", {});
```

## Suggestions or direct edits

Tool mutations are tracked changes by default, whatever mode the editor is in
for typing. Pass `directMode` to apply them directly:

```ts theme={null}
await editor.tools.call("style_blocks", input, { directMode: true });
```

See [tracked changes](/developer/docs/developer/docs/editor-sdk/guides/tracked-changes) for when to use it.

## What is not a tool

Review navigation, comment panel state, focus, viewport, zoom, and canvas
rendering belong to the host. They are not tools in this catalog. The agent
makes document edits, and your UI presents them. A delegated Revise agent can
inspect the rendered canvas with its internal `render_document_pages` tool. See
[architecture](/developer/docs/developer/docs/editor-sdk/concepts/architecture) and [delegation](/developer/docs/developer/docs/editor-sdk/guides/delegated-agent).

The full catalog is in the [tool reference](/developer/docs/developer/docs/editor-sdk/api/agent-tools).

## Server-side editing

`@reviseio/editor/server` binds the same tool definitions and executor to a `Y.Doc` that
you own. No model or React mount is involved. The [backend
reference](/developer/docs/developer/docs/editor-sdk/api/backend) covers conversion, room lifecycle, and the
full session API.

A session:

* is bound to one document, so its schemas have no `document_id`
* keeps search result IDs across calls
* runs every call in arrival order
* must be disposed. Disposing removes Revise's observers and leaves your
  `Y.Doc` intact.

Literal tool names infer their exact input and output types. Use `call()` in
application code: it returns the response and throws `ReviseToolError` when the
tool rejects the call. Use `execute()` for a result object instead, or
`executeDynamic()` for tool names and JSON from a model.

Switch modes with `document.setSuggestingMode()` and
`document.setEditingMode()`. Each call uses the mode that was set when it was
submitted, so switching is safe with calls still queued.

```ts theme={null}
import { readFile, writeFile } from "node:fs/promises";
import {
  createServerDocumentSession,
  decodeYDoc,
  encodeYDoc,
  fileToYDoc,
  ydocToDocx,
} from "@reviseio/editor/server";

const ydoc = await fileToYDoc(
  await readFile("agreement.docx"),
  "agreement.docx",
);
const document = await createServerDocumentSession(ydoc, {
  documentId: "agreement-1",
  mode: "suggesting",
});

const read = await document.tools.call("read_blocks_from_index", {
  index: 0,
  context_notes: "Locating the liability clause",
});
console.log(read.context?.html); // Revise semantic HTML with block IDs

const search = await document.tools.call("search_document", {
  queries: ["fees paid"],
  page: 0,
  context_notes: "Liability cap",
});
const searchResultId = search.data.search_result_id;
if (!searchResultId) throw new Error("The search returned no editable matches");

// Exact-text replacement needs no HTML. Apply this one directly.
document.setEditingMode();
await document.tools.call(
  "replace",
  {
    search_result_id: searchResultId,
    replacements: [{ find: "fees paid", replace: "fees paid or payable" }],
  },
);

// Switch back to produce tracked changes.
document.setSuggestingMode();
const governingLaw = await document.tools.call("search_document", {
  queries: ["Delaware"],
  page: 0,
  context_notes: "Suggesting a governing-law change",
});
const governingLawId = governingLaw.data.search_result_id;
if (!governingLawId) throw new Error("The governing-law text was not found");
const edit = await document.tools.call("replace", {
  search_result_id: governingLawId,
  replacements: [{ find: "Delaware", replace: "New York" }],
});

// Resolve by ID. Mutation results list the tracked changes they created.
// Store these IDs with your review task.
const created = edit.suggestionIds ?? [];
const decision = document.acceptSuggestions(created);
// decision.resolved: settled now. decision.missing: stale or unknown IDs.

// Or list everything pending. Each record has its author, so you can act on
// your agent's suggestions and leave collaborators' suggestions alone:
const mine = document
  .listSuggestions()
  .filter((record) => record.authorType === "ai");
// document.rejectSuggestions(mine.map((record) => record.id));

// These also settle collaborators' pending suggestions. Use them only when
// the host owns the document:
// document.acceptAllSuggestions(); // or document.rejectAllSuggestions()

await writeFile("agreement.yjs", encodeYDoc(ydoc));
const restored = decodeYDoc(await readFile("agreement.yjs"));
await writeFile("agreement-amended.docx", await ydocToDocx(restored));
document.dispose();
```

In a model loop, return expected failures to the model as text:

```ts theme={null}
const result = await document.tools.executeDynamic(call.name, call.input);
const toolMessage = result.ok
  ? (result.value.message ?? JSON.stringify(result.value.data))
  : `${result.error.code}: ${result.error.message}`;
```

### Revise semantic HTML

Read and search tools return `response.context.html` in **Revise semantic
HTML**: document markup with stable block IDs for paragraphs, formatted runs,
tables, comments, and notes where supported. Typed metadata is in
`response.data`. A short summary for the model is in `response.message`.

Structural insertion and whole-block replacement accept the same HTML. Use it
instead of building `DocRoot` or OOXML. It is not general browser HTML and CSS,
and it does not represent Word layout that Revise does not support. The
Yjs-backed Revise model is the source of truth, and DOCX is the import and
export format. Running a tool does not reparse unchanged content through HTML.

Do not mutate `DocRoot` directly. Some conversion calls still return that type
for compatibility. The supported server mutation API is
`(await createServerDocumentSession(ydoc, ...)).tools`.


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