Document tools and error handling
Each Office module publishes a catalog of structured document tools. getTools() returns that catalog as executable
tools for your own LLM loop, registerTools() registers it with a framework that exposes a registerTool callback, and
getSystemPrompt() returns the module prompt to put in the system slot.
Get executable tools
const tools = await word.getTools({ include: ["insert_text"] }); // omit `include` to get every tool
Each item is an OfficeTool - name, description, inputSchema (JSON Schema), and execute(args). execute
delegates to the editor and, by default, returns the editor's result unchanged (an MCP CallToolResult:
{ content, isError? }).
Keep the tool set small and task-specific with include (an array of names or a predicate). A shorter tool set improves
model selection quality and limits the impact of a wrong decision.
Tool results
execute(args) resolves with the editor's result: content is a list of blocks (usually one { type: "text", text })
and isError is true when the editor reports a failure. Return the result to the model with is_error: result.isError === true
and do not present a failed operation as successful. execute() rejects only when the SDK itself fails (see
SDK errors); catch that and return the message as an error result instead of ending the loop. To change the
shape before it reaches the model, pass formatResult / formatError to getTools().
Connect to your LLM framework
The code below maps schemas using the tools obtained above. The OpenAI example uses Chat Completions, which differs
from the Responses API. Use the Vercel example in a project with the ai package installed. Apply your application's
authorization, argument validation, and user approval in the execution function.
// OpenAI
const openaiTools = tools.map((t) => ({
type: "function",
function: { name: t.name, description: t.description, parameters: t.inputSchema },
}));
// Anthropic
const anthropicTools = tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.inputSchema }));
// Vercel AI SDK
import { tool, jsonSchema } from "ai";
const aiTools = Object.fromEntries(tools.map((t) => [
t.name,
tool({ description: t.description, inputSchema: jsonSchema(t.inputSchema), execute: t.execute }),
]));
Tool-call responses and result messages differ between providers. With Anthropic Messages, preserve the assistant's
tool_use in the conversation and return a user tool_result with the same ID.
Follow the Editor AI SDK quickstart for an example with an allowlist, argument validation,
user approval, and execution limits. Explicitly configure the loop termination condition for your framework version.
Register with a registerTool callback
For frameworks that expose a registry callback instead of accepting a tool array, registerTools() registers the whole
catalog. It builds on getTools(); its default formatters wrap results in the MCP text shape.
const schemas = await word.registerTools(modelContext, {
formatResult: (result) => result, // pass the editor result through
formatError: (error) => ({ isError: true, content: [{ type: "text", text: error.message }] }),
});
modelContext is the registerTool callback object provided by your application's framework. This example assumes it
is already available. Do not return stack traces, internal URLs, storage paths, or credentials to the model.
Inspect schemas and the module prompt
const schemas = await word.getToolSchemas(); // name, description, inputSchema - no executors
const prompt = await word.getSystemPrompt(); // module-provided system prompt; "" when not provided
Confirm destructive operations
For broad or destructive changes, especially when Editor SDK is called through an AI agent:
- Read the current state.
- Build and display a change plan.
- Require user confirmation.
- Apply the smallest set of operations.
- Save explicitly.
- Read the affected state again and verify the result.
SDK errors
All SDK-owned failures reject with SDKError (code, message, optional data).
| Code | Constant | Meaning | Typical recovery |
|---|---|---|---|
| 1001 | INVALID_ARGUMENT | Invalid module, origin, iframe target, or an iframe already bound to another module | Correct configuration before retrying |
| 1002 | TIMEOUT | Editor did not answer before the timeout | Wait with whenReady(), check the origin, then retry intentionally |
| 1003 | DESTROYED | Handle used after disconnect() | Obtain a new handle with Office.word(iframe) |
| 1004 | INVALID_RESPONSE | Response envelope could not be parsed | Check editor/SDK compatibility |
| 1005 | NETWORK_ERROR | postMessage send failed | Check target window lifecycle |
| 2000 | FRAMEWORK_ERROR | Editor operation failed | Inspect error.data.error.code and user input |
When a document operation fails, error.data is the editor's structured response:
{ success: false, error: { code, message } }. Branch on error.data.error.code (for example SHEET_NOT_FOUND) rather
than parsing the message.
import { SDKError, SDK_ERROR_CODE } from "@thinkfree.dev/tfo-sdk";
try {
const body = await word.getDocument().getBody();
await body.insertParagraph("Quarterly summary");
} catch (error) {
if (error instanceof SDKError && error.code === SDK_ERROR_CODE.TIMEOUT) {
showRetryMessage();
} else if (error instanceof SDKError && error.data?.success === false) {
showDocumentOperationError(error.data.error.code);
} else {
throw error;
}
}
showRetryMessage and showDocumentOperationError are error-display functions your application implements.
An edit may have been applied even after a timeout, so check the document state first. Do not automatically retry a
write unless the operation is known to be idempotent.
Use this guide to choose recovery behavior. For supported methods, exact parameters, and return types, see the Word, Spreadsheet, Presentation API references.