Skip to main content
Not recommended for new integrations. The legacy preset remains the default (omitting preset selects it) purely for backwards compatibility — existing integrations keep working unchanged. For new work, use the core preset: it scores measurably higher on our revision-fidelity evals, returns verifiable receipts instead of raw operation results, and treats tracked-changes redlining as a first-class workflow.
The legacy preset gives the model 10 grouped intent tools that map closely to Document API operations. The model works low-level: search for text to obtain handles/addresses, then edit by address.

Quick start

import { createAgentToolkit } from '@superdoc-dev/sdk';

// 'legacy' is also what you get if you omit `preset`.
const { tools, systemPrompt, dispatch } = await createAgentToolkit({
  provider: 'openai',
  preset: 'legacy',
});
The legacy preset ignores excludeActions everywhere (it has no action surface to narrow) — passing it is a harmless no-op.

Tool catalog

Most tools use an action argument to select the underlying operation. Single-action tools like superdoc_search do not require action.
ToolActionsWhat it does
superdoc_get_contenttext, markdown, html, blocks, extract, infoRead document content in different formats
superdoc_search(single action)Find text or nodes and return handles or addresses for later edits
superdoc_editinsert, replace, delete, undo, redoPerform text edits and history actions
superdoc_formatinline, set_style, set_alignment, set_indentation, set_spacing, set_direction, set_flow_optionsApply inline or paragraph formatting
superdoc_createparagraph, heading, tableCreate structural block elements
superdoc_listinsert, create, attach, detach, delete, merge, split, indent, outdent, set_level, set_type, set_value, continue_previousCreate and manipulate lists
superdoc_commentcreate, update, delete, get, listManage comment threads
superdoc_track_changeslist, decideReview and resolve tracked changes
superdoc_mutationspreview, applyExecute multi-step atomic edits as a batch
superdoc_tabledelete, delete_column, delete_row, insert_column, insert_row, merge_cells, set_borders, set_cell, set_cell_text, set_column, set_layout, set_options, set_row, set_row_options, set_shading, set_style_options, unmerge_cellsModify table structure, content, and styling (find table/row/cell nodeIds via superdoc_get_content or superdoc_search)

Behaviors you must know

superdoc_search require valuesfirst returns the first match (error on zero), all returns every match, any returns matches without failing on zero, exactlyOne errors unless exactly one matched (AMBIGUOUS_MATCH on several, MATCH_NOT_FOUND on none). Refs expire on mutation. Every successful edit bumps the document revision and invalidates previously fetched refs — using a stale ref raises REVISION_MISMATCH. This is the sharpest edge of the legacy surface: pre-fetching refs for two blocks and editing them sequentially always fails on the second edit. For multi-block edits use superdoc_mutations (which resolves all targets before any step executes and applies everything in one revision bump), or re-search between edits. get_content action:"text" returns the whole document with no pagination — in an agent loop that result lives in conversation history forever and amplifies every later turn’s token cost. Prefer scoped reads.

System prompt

getSystemPrompt() / getSystemPrompt('legacy') returns the prompt this surface was designed with: the search-before-edit workflow, targeting rules, and batching guidance. Use it as-is or extend it — and pair prompt and tools from the same preset.

Creating custom tools

The built-in intent tools cover core editing operations. For anything else, create custom tools that call doc.* methods directly and merge them with the SDK tools.
StepWhat you do
1. Pick operationsBrowse doc.* to find the methods you need
2. Define the schemaWrite a function tool definition for your provider
3. Write a dispatcherMap tool actions to doc.* calls
4. Merge and useCombine with SDK tools in your agentic loop

Step 1: Pick your operations

Every doc.* namespace maps to a group of Document API operations:
doc.hyperlinks   → list, get, wrap, insert, patch, remove
doc.tables       → get, insertRow, deleteRow, mergeCells, ...
doc.images       → list, get, setSize, rotate, crop, ...
doc.footnotes    → list, get, create, delete, update, ...
doc.bookmarks    → list, get, create, delete, ...

Step 2: Define the tool schema

Group related operations under a single tool using an action enum. This matches the pattern the built-in tools use.
import type { ChatCompletionTool } from 'openai/resources/chat/completions';

const hyperlinkTool: ChatCompletionTool = {
  type: 'function',
  function: {
    name: 'superdoc_hyperlink',
    description:
      'Create, read, update, or remove hyperlinks in the document.',
    parameters: {
      type: 'object',
      properties: {
        action: {
          type: 'string',
          enum: ['list', 'get', 'wrap', 'insert', 'patch', 'remove'],
        },
        target: {
          description: 'Target address from superdoc_search results.',
        },
        text: { type: 'string', description: 'Display text (insert only).' },
        href: { type: 'string', description: 'URL destination.' },
        tooltip: { type: 'string', description: 'Hover tooltip text.' },
      },
      required: ['action'],
      additionalProperties: false,
    },
  },
};
  • Keep descriptions short. The model reads every tool definition on each turn.
  • Use additionalProperties: false to prevent hallucinated parameters.
  • Reference superdoc_search in descriptions so the model knows how to get targets.

Step 3: Write a dispatcher

Map each action to the corresponding doc.* call:
import { dispatchSuperDocTool } from '@superdoc-dev/sdk';

async function dispatchToolCall(doc, toolName, args) {
  // Built-in tools: delegate to the SDK
  if (toolName !== 'superdoc_hyperlink') {
    return dispatchSuperDocTool(doc, toolName, args);
  }

  // Custom tool: call doc.* directly
  const { action, target, text, href, tooltip } = args;

  switch (action) {
    case 'list':
      return doc.hyperlinks.list({});
    case 'get':
      return doc.hyperlinks.get({ target });
    case 'wrap':
      return doc.hyperlinks.wrap({
        target,
        link: { destination: { href }, ...(tooltip && { tooltip }) },
      });
    case 'insert':
      return doc.hyperlinks.insert({
        text,
        link: { destination: { href }, ...(tooltip && { tooltip }) },
        ...(target && { target }),
      });
    case 'patch':
      return doc.hyperlinks.patch({
        target,
        patch: { ...(href && { href }), ...(tooltip && { tooltip }) },
      });
    case 'remove':
      return doc.hyperlinks.remove({ target });
    default:
      throw new Error(`Unknown action: "${action}"`);
  }
}

Step 4: Merge and use

Combine your custom tool with the SDK tools and use your dispatcher in the agentic loop:
const { tools: sdkTools } = await chooseTools({ provider: 'openai' });
const allTools = [...sdkTools, hyperlinkTool];

// In your agentic loop, use your dispatcher instead of dispatchSuperDocTool:
// OpenAI chat completions store the tool name on function.name.
const toolName = toolCall.function.name;
const result = await dispatchToolCall(doc, toolName, args);

Extending the system prompt

For custom tools, append usage instructions to the SDK system prompt so the model knows how to use them:
const systemPrompt = await getSystemPrompt();

const customInstructions = `
## superdoc_hyperlink

Use this tool to manage hyperlinks. First use superdoc_search to find
text you want to link, then pass the handle as target to the wrap action.
`;

const fullPrompt = systemPrompt + '\n' + customInstructions;

Migrating to core

The mapping is conceptual, not mechanical — core actions absorb multi-call legacy patterns into single verbs:
Legacy patternCore equivalent
superdoc_searchsuperdoc_edit (replace by ref)replace_text (finds and edits in one call)
superdoc_searchsuperdoc_mutations (multi-block batch)one action call — each action resolves its own targets atomically
superdoc_create + superdoc_format chainsinsert_paragraphs / insert_heading / create_table with placement
superdoc_track_changes {action:"decide"}accept_tracked_changes / reject_tracked_changes (with author/changeType filters)
superdoc_commentadd_comments / reply_to_comment / resolve_comments
Switching is a one-line change per call site (preset: 'core' on the toolkit) plus adopting the receipt contract — see the core preset reference.