0.x postThis post uses the 0.x package names and APIs. For the current release see the 2.x docs.
@docx-editor.dev/agents integrates AI agents with .docx documents. It
provides 14 tools for reading, finding, commenting, suggesting, formatting, and
scrolling. The tools use OpenAI function-calling format. They use the stable
Word paraId, which keeps paragraph references valid across multiple tool
calls. The catalog supports three transports: a React <DocxEditor>, a parsed
buffer in Node, and the Model Context Protocol.
This post covers the supported integration shapes, the paraId addressing model, and the framework adapters.
Common integrations
The integration surface is the same for each agent role. The deployment controls the available tools and the system prompt.
- Contract review: Use
read_document,add_comment, andsuggest_change. An attorney accepts or rejects the tracked changes in Word, Google Docs, or the embedded editor. - Compliance scans: Use read-only tools, such as
include: ['read_document', 'find_text', 'read_comments']. Comments can mark personally identifiable information (PII), policy violations, or missing clauses. - Document assistants: Use the complete catalog. Use
includeorexcludeto limit the tools available through a chat panel. - Word add-in alternative: The toolkit provides web equivalents for
Range.insertComment,comment.reply,body.search, andrange.scrollIntoView. This mapping reduces changes to Office.js call sites.
The tools
The package exports 14 tools as raw definitions and OpenAI function-calling schemas. Anthropic tool use, the Vercel AI SDK, and other compatible clients can use these schemas without conversion.
| Group | Tools | Purpose |
|---|---|---|
| Locate | read_document, read_selection, read_page, read_pages, find_text, read_comments, read_changes | Return paragraphs tagged with paraId. |
| Mutate | add_comment, suggest_change, apply_formatting, set_paragraph_style, reply_comment, resolve_comment | Take a paraId and an optional search phrase. |
| Navigate | scroll | Live transport only. |
Locate tools return paragraphs keyed by Word paraId; mutate tools take that paraId. This gives the agent a stable paragraph anchor across multi-step tool calls instead of relying on raw character offsets.
Building a custom agent
A custom agent uses the required catalog subset and any domain-specific tools. The application also defines the system prompt. The integration uses one React hook and a streaming chat client.
The following Roastmaster demo reads the document and selects three to five passages. It adds a comment to each passage. It uses only read and comment tools, so it cannot edit document text.
Step 1: limit the tools. Roastmaster does not edit text. The
useDocxAgentTools call uses an include allowlist that excludes every
mutation tool except add_comment:
import { useDocxAgentTools } from "@docx-editor.dev/agents/react";
const { tools, executeToolCall, getContext } = useDocxAgentTools({
editorRef,
author: "Roastmaster",
include: [
"read_document", "read_selection", "find_text",
"read_comments", "read_changes", "scroll",
"add_comment", "reply_comment", "resolve_comment",
],
});The same include mechanism can expose a read-only compliance scanner, the
complete catalog, or a review agent. A review agent can use read tools,
add_comment, and suggest_change. Pass custom tools through tools to add
them to the catalog.
Step 2: write the system prompt. The prompt defines the agent behavior.
Roastmaster's prompt limits each turn to five comments. It also requires each
comment to use a unique phrase from its paragraph and defines the review tone.
A complete example is in
examples/agent-chat-demo.
Step 3: run the loop. useChat from @ai-sdk/react handles streaming.
executeToolCall runs each tool call in the client against the live editor. The
server route sends schemas and receives chat and tool-call text. It does not
receive the DOCX buffer.
const chat = useChat({
transport: new DefaultChatTransport({
api: "/api/agent-chat",
prepareSendMessagesRequest: ({ messages }) => ({
body: { messages, context: getContext() },
}),
}),
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
onToolCall: ({ toolCall }) => {
const result = executeToolCall(toolCall.toolName, toolCall.input);
chatRef.current?.addToolResult({
tool: toolCall.toolName,
toolCallId: toolCall.toolCallId,
output: result.success ? String(result.data) : result.error ?? "",
});
},
});getContext() returns the user's selection and page. Pass it through
prepareSendMessagesRequest to provide this context without another tool call.
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls continues
the loop until the model returns a reply without a tool call.
The Live editor docs page contains the full source for the following example and its server route.
"use client";
import { useMemo, useRef, useState } from "react";
import { DocxEditor, AgentChatLog, AgentComposer } from "@eigenpal/docx-js-editor";
import { useDocxAgentTools, getToolDisplayName } from "@docx-editor.dev/agents/react";
import { toAgentMessages } from "@docx-editor.dev/agents/ai-sdk/react";
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from "ai";
export function EditorWithAgent({ buffer }: { buffer: ArrayBuffer }) {
const editorRef = useRef(null);
const { executeToolCall, getContext } = useDocxAgentTools({ editorRef, author: "Agent" });
const chatRef = useRef(null);
const chat = useChat({
transport: new DefaultChatTransport({
api: "/api/chat",
prepareSendMessagesRequest: ({ messages }) => ({
body: { messages, context: getContext() },
}),
}),
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
onToolCall: ({ toolCall }) => {
const result = executeToolCall(toolCall.toolName, toolCall.input);
void chatRef.current?.addToolResult({
tool: toolCall.toolName,
toolCallId: toolCall.toolCallId,
output: result.success ? String(result.data) : (result.error ?? ""),
});
},
});
chatRef.current = chat;
const messages = useMemo(() => toAgentMessages(chat.messages, chat.status), [chat]);
const [input, setInput] = useState("");
return (
<DocxEditor
ref={editorRef}
documentBuffer={buffer}
agentPanel={{
title: "Agent",
render: () => (
<>
<AgentChatLog messages={messages} humanizeToolName={getToolDisplayName} />
<AgentComposer
value={input}
onChange={setInput}
onSubmit={() => {
chat.sendMessage({ text: input });
setInput("");
}}
/>
</>
),
}}
/>
);
}The matching server route uses getAiSdkTools() from /ai-sdk/server and
Vercel AI SDK's streamText({ tools }). The
Live editor docs page contains the full
source.
Try it
The following demo uses the same component on this site. The Roastmaster agent has only read and comment tools, so it cannot edit document text. Select a suggestion or enter a prompt to run it on the sample document.
Scope
- Tracked-change acceptance requires a person. The catalog has no
accept_changeorreject_changetools. A person decides whether to accept each revision. - Formatting operations.
apply_formattingsupports bold, italic, underline, strike, color, highlight, font size, and font family. The toolkit does not support paragraph alignment or spacing operations.
Get started
- Live agent demo: opens the editor with the Roastmaster agent panel enabled. Select a suggestion chip to add a comment to the sample document.
- Agent API documentation: the full reference, including the live-editor transport, the headless reviewer, and the MCP server.
- Tool catalog: every tool's input schema, output shape, and behavior.
- Office.js compatibility: the mapping table for developers migrating from Office.js.
- Components reference: the React UI kit (
AgentPanel,AgentChatLog,AgentComposer,AgentSuggestionChip,AgentTimeline). examples/agent-chat-demo: a runnable Next.js app demonstrating the live transport.examples/agent-use-demo: the server-side review pattern.
The source is on GitHub. You can report issues or submit pull requests.
For related information, see Track Changes in a React DOCX Editor and Real-time DOCX collaboration with React, Vue 3, and Yjs.