Skip to main content

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:

  1. Read the current state.
  2. Build and display a change plan.
  3. Require user confirmation.
  4. Apply the smallest set of operations.
  5. Save explicitly.
  6. Read the affected state again and verify the result.

SDK errors​

All SDK-owned failures reject with SDKError (code, message, optional data).

CodeConstantMeaningTypical recovery
1001INVALID_ARGUMENTInvalid module, origin, iframe target, or an iframe already bound to another moduleCorrect configuration before retrying
1002TIMEOUTEditor did not answer before the timeoutWait with whenReady(), check the origin, then retry intentionally
1003DESTROYEDHandle used after disconnect()Obtain a new handle with Office.word(iframe)
1004INVALID_RESPONSEResponse envelope could not be parsedCheck editor/SDK compatibility
1005NETWORK_ERRORpostMessage send failedCheck target window lifecycle
2000FRAMEWORK_ERROREditor operation failedInspect 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.