Skip to content

Commit f725f36

Browse files
feat: llm tools beta (#2393)
* feat: llm tools beta * fix: update dependencies in pnpm-lock.yaml * refactor: simplify intent tools generation and enhance provider handling * chore: fix type breaking build * chore: fix mcp build * chore: update lock * chore: update docs --------- Co-authored-by: Nick Bernal <nick@superdoc.dev>
1 parent 266c11b commit f725f36

47 files changed

Lines changed: 1704 additions & 3175 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-mcp.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,15 @@ jobs:
3131
- name: Install dependencies
3232
run: pnpm install --frozen-lockfile
3333

34+
- name: Generate SDK artifacts
35+
run: pnpm run generate:all
36+
3437
- name: Build superdoc (dependency)
3538
run: pnpm run build:superdoc
3639

40+
- name: Build SDK (dependency)
41+
run: pnpm --prefix packages/sdk/langs/node run build
42+
3743
- name: Build
3844
run: pnpm --prefix apps/mcp run build
3945

.github/workflows/release-mcp.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,15 @@ jobs:
4848
- name: Install dependencies
4949
run: pnpm install
5050

51+
- name: Generate SDK artifacts
52+
run: pnpm run generate:all
53+
5154
- name: Build superdoc (dependency)
5255
run: pnpm run build:superdoc
5356

57+
- name: Build SDK (dependency)
58+
run: pnpm --prefix packages/sdk/langs/node run build
59+
5460
- name: Build MCP server
5561
run: pnpm --prefix apps/mcp run build
5662

apps/cli/scripts/export-sdk-contract.ts

Lines changed: 4 additions & 310 deletions
Large diffs are not rendered by default.

apps/docs/document-api/reference/_generated-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,5 +951,5 @@
951951
}
952952
],
953953
"marker": "{/* GENERATED FILE: DO NOT EDIT. Regenerate via `pnpm run docapi:sync`. */}",
954-
"sourceHash": "b52520ee9b80ed98b8bb191aeec45df6488bdd1ee406009553247cfec58cfea1"
954+
"sourceHash": "068dea933bf1591112019534c6bae48f811dc8d65c42f6cb94f365548028ea77"
955955
}

apps/docs/document-api/reference/insert.mdx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Returns an SDMutationReceipt with applied status; resolution reports a TextAddre
4343

4444
| Field | Type | Required | Description |
4545
| --- | --- | --- | --- |
46-
| `content` | object | yes | |
46+
| `content` | object \\| object[] | yes | One of: object, object[] |
4747
| `nestingPolicy` | object | no | |
4848
| `nestingPolicy.tables` | enum | no | `"forbid"`, `"allow"` |
4949
| `placement` | enum | no | `"before"`, `"after"`, `"insideStart"`, `"insideEnd"` |
@@ -201,7 +201,17 @@ Returns an SDMutationReceipt with applied status; resolution reports a TextAddre
201201
"additionalProperties": false,
202202
"properties": {
203203
"content": {
204-
"type": "object"
204+
"oneOf": [
205+
{
206+
"type": "object"
207+
},
208+
{
209+
"items": {
210+
"type": "object"
211+
},
212+
"type": "array"
213+
}
214+
]
205215
},
206216
"nestingPolicy": {
207217
"additionalProperties": false,

apps/docs/document-api/reference/ranges/index.mdx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,3 @@ Deterministic range construction from explicit document anchors.
1515
| Operation | Member path | Mutates | Idempotency | Tracked | Dry run |
1616
| --- | --- | --- | --- | --- | --- |
1717
| <span style={{ whiteSpace: 'nowrap', wordBreak: 'normal', overflowWrap: 'normal' }}><a href="/document-api/reference/ranges/resolve"><code>ranges.resolve</code></a></span> | `ranges.resolve` | No | `idempotent` | No | No |
18-

apps/docs/document-api/reference/replace.mdx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t
4747

4848
| Field | Type | Required | Description |
4949
| --- | --- | --- | --- |
50-
| `content` | object | yes | |
50+
| `content` | object \\| object[] | yes | One of: object, object[] |
5151
| `nestingPolicy` | object | no | |
5252
| `nestingPolicy.tables` | enum | no | `"forbid"`, `"allow"` |
5353
| `target` | BlockNodeAddress \\| SelectionTarget | yes | One of: BlockNodeAddress, SelectionTarget |
@@ -56,7 +56,7 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t
5656

5757
| Field | Type | Required | Description |
5858
| --- | --- | --- | --- |
59-
| `content` | object | yes | |
59+
| `content` | object \\| object[] | yes | One of: object, object[] |
6060
| `nestingPolicy` | object | no | |
6161
| `nestingPolicy.tables` | enum | no | `"forbid"`, `"allow"` |
6262
| `ref` | string | yes | |
@@ -225,7 +225,17 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t
225225
"additionalProperties": false,
226226
"properties": {
227227
"content": {
228-
"type": "object"
228+
"oneOf": [
229+
{
230+
"type": "object"
231+
},
232+
{
233+
"items": {
234+
"type": "object"
235+
},
236+
"type": "array"
237+
}
238+
]
229239
},
230240
"nestingPolicy": {
231241
"additionalProperties": false,
@@ -260,7 +270,17 @@ Returns an SDMutationReceipt with applied status; receipt reports NO_OP if the t
260270
"additionalProperties": false,
261271
"properties": {
262272
"content": {
263-
"type": "object"
273+
"oneOf": [
274+
{
275+
"type": "object"
276+
},
277+
{
278+
"items": {
279+
"type": "object"
280+
},
281+
"type": "array"
282+
}
283+
]
264284
},
265285
"nestingPolicy": {
266286
"additionalProperties": false,

apps/docs/document-engine/ai-agents/llm-tools.mdx

Lines changed: 45 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,8 @@ Install the SDK, create a client, and wire up an agentic loop.
119119
```typescript
120120
import { chooseTools } from '@superdoc-dev/sdk';
121121

122-
const { tools, selected, meta } = await chooseTools({
122+
const { tools, meta } = await chooseTools({
123123
provider: 'openai', // 'openai' | 'anthropic' | 'vercel' | 'generic'
124-
mode: 'essential', // 'essential' (default) | 'all'
125-
groups: ['comments'], // optional — load additional groups alongside essential tools
126124
});
127125
```
128126
</Tab>
@@ -132,49 +130,31 @@ Install the SDK, create a client, and wire up an agentic loop.
132130

133131
result = choose_tools(
134132
provider="openai",
135-
mode="essential",
136-
groups=["comments"],
137133
)
138134
tools = result["tools"]
139135
```
140136
</Tab>
141137
</Tabs>
142138

143-
### Essential mode (default)
139+
The current SDK returns the full grouped intent tool set for the selected provider. Group filtering and meta-discovery are not part of the shipped public API here.
144140

145-
Returns 5 essential tools plus `discover_tools` — a meta-tool that lets the LLM load more groups on demand. This keeps the initial context small while giving the model access to the full toolkit when needed.
141+
## Current tool set
146142

147-
The 5 essential tools:
143+
The generated catalog currently contains 9 grouped intent tools:
148144

149-
| Tool | What it does |
150-
| --- | --- |
151-
| `get_document_text` | Returns the full plain-text content of the document |
152-
| `query_match` | Searches by node type, text pattern, or both — returns matches with addresses |
153-
| `apply_mutations` | Batch edit: rewrite, insert, delete text and apply formatting in one call |
154-
| `get_node_by_id` | Get details about a specific node by its address |
155-
| `undo` | Undo the last operation |
156-
157-
If you pass `groups`, those groups are loaded **in addition** to the essential set:
158-
159-
```typescript
160-
// Essential tools + all comment tools
161-
const { tools } = await chooseTools({
162-
provider: 'openai',
163-
groups: ['comments'],
164-
});
165-
```
166-
167-
### All mode
168-
169-
Returns every tool from the requested groups (or all groups if `groups` is omitted). The `core` group is always included.
170-
171-
```typescript
172-
const { tools } = await chooseTools({
173-
provider: 'openai',
174-
mode: 'all',
175-
groups: ['core', 'format', 'comments'],
176-
});
177-
```
145+
| Tool | Actions | What it does |
146+
| --- | --- | --- |
147+
| `superdoc_get_content` | `text`, `markdown`, `html`, `info` | Read document content in different formats |
148+
| `superdoc_search` | `match` | Find text or nodes and return handles or addresses for later edits |
149+
| `superdoc_edit` | `insert`, `replace`, `delete`, `undo`, `redo` | Perform text edits and history actions |
150+
| `superdoc_format` | `inline`, `set_style`, `set_alignment`, `set_indentation`, `set_spacing` | Apply inline or paragraph formatting |
151+
| `superdoc_create` | `paragraph`, `heading` | Create structural block elements |
152+
| `superdoc_list` | `insert`, `create`, `detach`, `indent`, `outdent`, `set_level`, `set_type` | Create and manipulate lists |
153+
| `superdoc_comment` | `create`, `update`, `delete`, `get`, `list` | Manage comment threads |
154+
| `superdoc_track_changes` | `list`, `decide` | Review and resolve tracked changes |
155+
| `superdoc_mutations` | `preview`, `apply` | Execute multi-step atomic edits as a batch |
156+
157+
Multi-action tools use an `action` argument to select the underlying operation. Single-action tools like `superdoc_search` do not require `action`.
178158

179159
## Dispatching tool calls
180160

@@ -206,51 +186,6 @@ const { tools } = await chooseTools({
206186

207187
The dispatcher validates required parameters, enforces mutual exclusivity constraints, and throws descriptive errors if arguments are invalid — so the LLM gets actionable feedback.
208188

209-
## Tool groups
210-
211-
Tools are organized into 11 groups. In essential mode, the LLM can load any group dynamically via `discover_tools`.
212-
213-
| Group | Description |
214-
| --- | --- |
215-
| `core` | Read nodes, get text, find/replace, insert, delete, batch mutations |
216-
| `format` | Bold, italic, underline, strikethrough, alignment, spacing, borders, shading |
217-
| `create` | Create headings, paragraphs, tables, sections, table of contents |
218-
| `tables` | Row/column operations, cell merging, table formatting, borders |
219-
| `sections` | Page layout, margins, columns, headers/footers, page numbering |
220-
| `lists` | Bullet and numbered lists, indentation, list type conversion |
221-
| `comments` | Create, edit, delete, resolve, and list comment threads |
222-
| `trackChanges` | List, inspect, accept, and reject tracked changes |
223-
| `toc` | Table of contents — create, configure, refresh |
224-
| `history` | Undo and redo |
225-
| `session` | Open, save, close, and manage document sessions |
226-
227-
## The discover_tools pattern
228-
229-
When the LLM needs tools beyond the essential set, it calls `discover_tools` with the groups it wants. Since `discover_tools` is a meta-tool (not a document operation), intercept it before `dispatchSuperDocTool` and handle it client-side via `chooseTools`:
230-
231-
```typescript
232-
import { chooseTools } from '@superdoc-dev/sdk';
233-
234-
for (const call of message.tool_calls) {
235-
let result;
236-
const args = JSON.parse(call.function.arguments);
237-
238-
if (call.function.name === 'discover_tools') {
239-
// Meta-tool — resolve client-side, then merge new tools
240-
result = await chooseTools({ provider: 'openai', groups: args.groups });
241-
tools.push(...result.tools);
242-
} else {
243-
result = await dispatchSuperDocTool(client, call.function.name, args);
244-
}
245-
246-
messages.push({
247-
role: 'tool',
248-
tool_call_id: call.id,
249-
content: JSON.stringify(result),
250-
});
251-
}
252-
```
253-
254189
## Providers
255190

256191
Each provider gets tool definitions in its native format.
@@ -284,79 +219,69 @@ Each provider gets tool definitions in its native format.
284219

285220
## Best practices
286221

287-
### Start with essential mode
222+
### Start with the full grouped set
288223

289-
Load only the 5 essential tools plus `discover_tools`. This keeps the context window small and gives the model room to reason. Let it call `discover_tools` when it needs more — don't front-load every group.
224+
The current catalog is intentionally small. In most integrations you should load the full grouped set once and let the model choose among the 9 tools.
290225

291226
### Minimize tool calls
292227

293-
A typical edit should take 35 tool calls: query, mutate, done. Instruct the LLM to plan all edits before calling tools, and to batch multiple changes into a single `apply_mutations` call when possible.
228+
A typical edit should take 3-5 tool calls: read, search, mutate, done. Instruct the LLM to plan all edits before calling tools, and to batch multiple changes only when atomic execution is genuinely helpful.
294229

295-
### Use `apply_mutations` for text edits
230+
### Read first, then search before editing
296231

297-
`apply_mutations` can rewrite, insert, delete, and format text in one call. It supports multiple steps, so the LLM can edit several paragraphs at once. Use it for any operation on existing text.
232+
Use `superdoc_get_content` to understand the document, then `superdoc_search` to obtain stable handles or addresses. Pass those targets into `superdoc_edit`, `superdoc_format`, `superdoc_create`, `superdoc_list`, or `superdoc_comment`.
233+
234+
### Prefer focused tools; use `superdoc_mutations` as an escape hatch
235+
236+
Use the focused intent tools for straightforward edits. Reach for `superdoc_mutations` when you need preview/apply semantics, atomic multi-step edits, or a batched workflow that would otherwise require several target refreshes.
298237

299238
### Feed errors back to the model
300239

301-
`dispatchSuperDocTool` throws descriptive errors with codes like `MATCH_NOT_FOUND` or `INVALID_ARGUMENT`. Pass these back as tool results — most models self-correct on the next turn.
240+
`dispatchSuperDocTool` surfaces structured errors from the underlying SDK and Document API. Pass these back as tool results — most models self-correct on the next turn.
302241

303242
### Add tool call examples for repeatable actions
304243

305244
If your workflow involves the same kind of edit across many documents (e.g., always rewriting a specific clause, always adding a comment to a section), include a concrete tool call example in your system prompt. Models that see a working example of the exact tool invocation produce correct calls more reliably than models that only see the schema.
306245

307246
### Include a system prompt
308247

309-
Tell the model what it can do and how to approach edits. Here's an example:
248+
Tell the model what it can do and how to approach edits. You can load the bundled prompt with `getSystemPrompt()`, or start with something like this:
310249

311250
````markdown
312-
You edit `.docx` files using SuperDoc tools. Be efficient minimize tool calls.
251+
You edit `.docx` files using SuperDoc intent tools. Be efficient and minimize tool calls.
313252

314253
## Workflow
315254

316-
1. **Query** — Call `query_match` to find text you want to edit.
317-
Use `require: "any"` to match broadly.
318-
2. **Plan** — Decide what changes to make. Think through all
319-
edits before making tool calls.
320-
3. **Mutate** — Call `apply_mutations` to rewrite, insert, delete,
321-
or format text.
322-
323-
Keep it to 3–5 tool calls total.
324-
325-
## Tools
326-
327-
You start with 5 essential tools plus `discover_tools`.
328-
Call `discover_tools` to load additional categories when needed:
255+
1. **Read** — Use `superdoc_get_content` to understand the document.
256+
2. **Search** — Use `superdoc_search` to find stable handles or block addresses.
257+
3. **Edit** — Use the focused tool that matches the job:
258+
- `superdoc_edit` for insert, replace, delete, undo, redo
259+
- `superdoc_format` for inline or paragraph formatting
260+
- `superdoc_create` for paragraphs and headings
261+
- `superdoc_comment` for comment threads
262+
- `superdoc_track_changes` for review decisions
263+
4. **Batch only when useful** — Use `superdoc_mutations` for preview/apply or atomic multi-step edits.
329264

330-
| Category | What it covers |
331-
|----------|----------------|
332-
| core | Read nodes, get text, query, mutations |
333-
| format | Text formatting, alignment, spacing, borders |
334-
| create | Headings, paragraphs, tables, sections |
335-
| tables | Table manipulation, cell operations |
336-
| comments | Comment threads |
337-
| trackChanges | Accept/reject tracked changes |
265+
Keep it to 3-5 tool calls total when possible.
338266

339267
## Rules
340268

341-
- Use `apply_mutations` for text edits on existing content.
342-
You can batch multiple rewrites in one call.
343-
- Use standalone tools (`create_heading`, `create_table`, etc.)
344-
for structural changes — these are NOT step ops.
269+
- Search before mutating so targets come from fresh results.
270+
- Use focused intent tools for normal edits.
271+
- Use `superdoc_mutations` when you need an atomic batch or preview/apply flow.
345272
- Set `changeMode: "tracked"` when edits need human review.
346-
- Don't rewrite and format in the same `apply_mutations` batch.
347-
Rewrite first, query again for fresh refs, then format.
273+
- Feed tool errors back to the model so it can recover.
348274
````
349275

350276
## Utility functions
351277

352278
| Function | Description |
353279
| --- | --- |
354-
| `chooseTools(input)` | Select tools for a provider, filtered by mode and groups |
280+
| `chooseTools(input)` | Load grouped tool definitions for a provider |
355281
| `dispatchSuperDocTool(client, name, args)` | Execute a tool call against a connected client |
356282
| `listTools(provider)` | List all tool definitions for a provider |
357-
| `resolveToolOperation(toolName)` | Map a tool name to its operation ID |
358283
| `getToolCatalog()` | Load the full tool catalog with metadata |
359-
| `getAvailableGroups()` | List all available tool groups |
284+
| `getSystemPrompt()` | Read the bundled system prompt for intent tools |
360285

361286
## Related
362287

0 commit comments

Comments
 (0)