Skip to content

Commit b34ef8d

Browse files
Elon Muskclaude
andauthored
docs(ai): add the tool-record guide, routing readers to skills first (#10222) (#10358)
`tool` is an authorable metadata kind with a generated field reference and no guide. The gap is not "a page is missing" — it is that a reader asking "how do I give my agent a new tool" had nowhere that told them the answer is usually NOT a tool record. So content/docs/ai/tools.mdx opens with the decision, not the shape: the three ways a capability reaches an agent (a platform tool named by a skill, an Action materialised as `action_<name>`, an authored `tool` record) and when each is right. The declaration shape is below that, for the reader who has established they need it, together with an honest statement of what a record does today — ADR-0109 Phase 2 has not landed, so `stack.tools` has no runtime reader and authoring one makes nothing runnable. - ai/index.mdx: the module list gains the page, and the sentence that presented `defineAgent` / `defineTool` / `defineSkill` as coequal now places them per ADR-0063 §2 + ADR-0109 (skills are the primitive; agents are platform-owned; a tool record is an optional refinement layer). - ai/meta.json: one sidebar entry, after actions-as-tools. - .claude/workflows/docs-accuracy-audit.js: one line, produced by `node scripts/docs-audit/check-audit-scope.mjs --write` — a new hand-written page must enter the audit scope or the gate goes red. Both TypeScript examples carry `{/* os:check */}` and type-check against the built @objectstack/spec. Part of #10206 Claude-Session: https://claude.ai/code/session_01GawRwpD44VwBDVy3hs77AX Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 530c1df commit b34ef8d

4 files changed

Lines changed: 222 additions & 1 deletion

File tree

.claude/workflows/docs-accuracy-audit.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const ALL_HANDWRITTEN = [
4141
"content/docs/ai/natural-language-queries.mdx",
4242
"content/docs/ai/skills-reference.mdx",
4343
"content/docs/ai/skills.mdx",
44+
"content/docs/ai/tools.mdx",
4445
"content/docs/api/client-sdk.mdx",
4546
"content/docs/api/data-api.mdx",
4647
"content/docs/api/data-flow.mdx",

content/docs/ai/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ AI in ObjectStack is a **cross-protocol capability layer**: agents, tools, and k
1818

1919
- **Data & actions → `@objectstack/mcp`** (BYO-AI). Point your own AI — Claude, Cursor, any MCP client, or a local model — at the app's objects, queries, and business **actions**, governed by the same RLS. With a local model, data *and* inference stay inside your boundary.
2020
- **Knowledge & RAG → the Knowledge Protocol + adapter plugins** (`knowledge-memory`, `knowledge-ragflow`, `embedder-openai`) — permission-aware retrieval over your own objects.
21-
- **Agents, tools, skills → typed metadata** (`defineAgent` / `defineTool` / `defineSkill`) plus the Model Registry. Author them as source (`*.agent.ts`, `*.tool.ts`, …) with your own AI coding agent (Claude Code, Cursor), aided by the ObjectStack [skills](/docs/ai/skills-reference) and MCP introspection.
21+
- **Skills → typed metadata** (`defineSkill`) plus the Model Registry. A skill is the third-party extension primitive; agents are platform-owned, and a `defineTool` record is an *optional refinement layer* you will rarely need — see [Tool Records](/docs/ai/tools) for which of the three paths your case wants. Author skills as source (`*.skill.ts`) with your own AI coding agent (Claude Code, Cursor), aided by the ObjectStack [skills](/docs/ai/skills-reference) and MCP introspection.
2222

2323
**ObjectOS** adds an in-product chat *runtime* on top of these same primitives — the `ask` data-query assistant, the `build` Studio authoring assistant, and the `/api/v1/ai/*` chat endpoints. The open-source framework has no built-in in-product chat — that runtime is documented in the [ObjectOS AI & Agents docs](https://docs.objectos.app/docs/ai).
2424
</Callout>
@@ -27,6 +27,7 @@ AI in ObjectStack is a **cross-protocol capability layer**: agents, tools, and k
2727

2828
- [AI Agents](/docs/ai/agents) — the two platform agents (`ask` / `build`), skills as the extension primitive, and agent anatomy
2929
- [Actions as Tools](/docs/ai/actions-as-tools) — explicit opt-in exposure of Actions to the LLM, HITL approval, permission-aware execution
30+
- [Tool Records](/docs/ai/tools) — the three ways a capability reaches an agent, and the narrow case where authoring a `tool` record is the right answer
3031
- [Knowledge & RAG](/docs/ai/knowledge-rag) — the Knowledge Protocol and its adapter plugins
3132
- [Natural Language Queries](/docs/ai/natural-language-queries) — the built-in data tools that turn questions into ObjectQL
3233
- [AI Skills System](/docs/ai/skills) — structured knowledge modules for AI coding assistants

content/docs/ai/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"connect-mcp",
66
"agents",
77
"actions-as-tools",
8+
"tools",
89
"knowledge-rag",
910
"natural-language-queries",
1011
"skills",

content/docs/ai/tools.mdx

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
---
2+
title: Tool Records
3+
description: The three ways a capability reaches an agent, and the narrow case where authoring a tool record is the right answer
4+
---
5+
6+
# Tool Records
7+
8+
Part of the [AI module](/docs/ai). `tool` is an authorable metadata kind —
9+
`ToolSchema`, declared as `defineStack({ tools })` — and it is the **least
10+
likely** answer to "how do I give my agent a new capability".
11+
12+
So this page opens with the decision instead of the shape. If you read only the
13+
next section and leave, you will be on the right path. The declaration shape is
14+
further down, for the reader who has already established that they need it.
15+
16+
<Callout type="info">
17+
**The default third-party path declares no tool records at all.** That is
18+
[ADR-0109](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0109-ai-tool-authoring-model.md),
19+
and the `stack.tools` field says so in its own description:
20+
21+
```text
22+
AI Tool metadata records — optional refinement layer, never required: the
23+
default path is skills referencing platform tools or materialised action_<name>
24+
tools (ADR-0109)
25+
```
26+
</Callout>
27+
28+
## Three ways a capability reaches an agent
29+
30+
An agent's capability set is the union of its surface-compatible **skills'**
31+
tools ([ADR-0064](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0064-tool-scoping-to-agent.md)),
32+
so every one of these is ultimately a name in some skill's `tools[]`. What
33+
differs is where that name comes from:
34+
35+
| What you want the agent to do | How you get there | Tool record? |
36+
|:---|:---|:---|
37+
| Read, query, aggregate, or search — what the platform already does for every app | Name the **platform tool** in your skill: `query_records`, `get_record`, `aggregate_data`, `search_knowledge`, `describe_object`, … | **No** |
38+
| Run something your app already does — a `script` / `api` / `flow` Action | Opt the Action in with `ai.exposed` + `ai.description`; the runtime materialises one `action_<name>` tool per exposed Action and your skill names that. See [Actions as Tools](/docs/ai/actions-as-tools). | **No** |
39+
| Reach a system outside your app | Connect it over MCP — see [Connect an MCP Client](/docs/ai/connect-mcp) | **No** |
40+
| Present an executable to the model *differently* from the way your app runs it | A `tool` record — the optional refinement layer described below | **Yes**, and rarely |
41+
42+
There is a fourth thing authors reach for that is not on this list, because it
43+
is not a tool at all: *reasoning*. "Analyse the pipeline", "draft this email",
44+
"score this lead" are things the model does with data it already has. Writing
45+
them as tool names is the most common authoring mistake on this surface, and it
46+
produces an assistant that claims abilities it does not have.
47+
48+
### The default path, end to end
49+
50+
Two declarations, no tool record — the Action you already ship for your UI, and
51+
a skill that names its materialised tool:
52+
53+
{/* os:check */}
54+
```typescript
55+
import { defineAction } from '@objectstack/spec/ui';
56+
import { defineSkill } from '@objectstack/spec/ai';
57+
58+
// 1. An Action the app already has — opted in to AI.
59+
export const EscalateCaseAction = defineAction({
60+
name: 'escalate_case',
61+
label: 'Escalate Case',
62+
objectName: 'support_case',
63+
type: 'flow',
64+
target: 'case_escalation_flow',
65+
ai: {
66+
exposed: true,
67+
description: 'Escalates a support case to the on-call queue and notifies the account owner.',
68+
},
69+
});
70+
71+
// 2. The skill names the materialised tool. No defineTool anywhere.
72+
export const CaseTriageSkill = defineSkill({
73+
name: 'case_triage',
74+
label: 'Case Triage',
75+
surface: 'ask',
76+
instructions: 'Read the case and its recent activity, then escalate when the customer is blocked.',
77+
tools: ['get_record', 'query_records', 'action_escalate_case'],
78+
});
79+
```
80+
81+
The full walkthrough — including the three conditions that decide whether
82+
`action_<name>` exists at all — is in
83+
[AI Agents](/docs/ai/agents#a-skill-needs-no-tool-records--name-the-action).
84+
85+
### Why that is the default
86+
87+
- **AI capability ≡ application capability.** The executable, its permission
88+
checks and its audit trail are the Action your UI button already runs. There
89+
is no second security surface to review, and no way for the agent to do
90+
something the app cannot.
91+
- **One less namespace to get wrong.** A tool record is a second place a name
92+
has to stay consistent — and a second place an AI author can invent one.
93+
- **Unresolved names surface at authoring time.** `os validate` reports a
94+
`skill.tools[]` entry that resolves to nothing (`ai-skill-tool-unresolved`,
95+
advisory). The rule exists because an app once shipped ten fictional tool
96+
names across six skills, every one of them passing validation.
97+
98+
## When a tool record is the right answer
99+
100+
Reach for one only when the **AI-facing surface must differ from the raw
101+
executable**. ADR-0109 names the qualifying cases:
102+
103+
- **A different LLM-facing description** — the model needs a contract written
104+
for it, and the Action's own `ai.description` cannot serve both audiences.
105+
- **Parameter narrowing** — the Action takes twenty parameters and the agent
106+
should see three, or an enum should be tighter for the model than for the API.
107+
- **Exposing a Flow** — there is no materialised family for flows, only for
108+
Actions.
109+
- **A stable AI-facing name** — decoupled from the Action's name, so renaming
110+
the Action does not rename the tool the model was trained against.
111+
- **Execution policy** — a confirm-before-run flag. Note that
112+
`requiresConfirmation` was *removed* from `ToolSchema` as unenforced
113+
([ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md));
114+
it returns only together with its enforcement.
115+
116+
If none of those describe your situation, you do not need a tool record. If one
117+
of them does, read the next callout before you write it.
118+
119+
<Callout type="warn">
120+
**What a tool record does today.** `ToolSchema` has no `implementation` or
121+
`handler` field, and no framework executor loads a metadata-authored tool —
122+
authoring one does **not** make anything runnable. The refinement layer above is
123+
ADR-0109 *Phase 2*, which is gated on a real refinement need and has not landed:
124+
`stack.tools` has no runtime reader yet.
125+
126+
What a record does do today is narrower and worth knowing: it survives stack
127+
composition, it is mirrored into the metadata store for Studio and discovery,
128+
and its name joins the resolution universe for `skill.tools[]` — so a skill
129+
naming it validates clean.
130+
131+
When Phase 2 lands, a third-party tool record must carry a `binding`
132+
(`{ type: 'action' | 'flow', name }`) to the executable it refines. Handlers
133+
never live on the tool. Write the record as a *view* of something your app
134+
already executes, and it will still be one.
135+
</Callout>
136+
137+
## The declaration shape
138+
139+
`ToolSchema` and the `defineTool` factory are exported from
140+
`@objectstack/spec/ai`. The generated field reference is
141+
[Tool](/docs/references/ai/tool); the authoring-relevant fields are:
142+
143+
| Field | Required | Meaning |
144+
|:---|:---|:---|
145+
| `name` || Machine name, `snake_case`, globally unique — this is what a skill's `tools[]` names |
146+
| `label` || Human-readable display name |
147+
| `description` || The text the **model** reads to decide when to call the tool |
148+
| `parameters` || JSON Schema for the tool input — the model generates arguments conforming to it |
149+
| `outputSchema` | optional | ⚠️ **Experimental, not enforced.** Its top-level keys are folded into the description shown to the model; outputs are never validated against it |
150+
| `objectName` | optional | The object this tool operates on, when there is exactly one |
151+
| `protection` | optional | Package-author lock policy ([ADR-0010](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0010-metadata-protection-model.md)) |
152+
153+
The shape is **strict**: an undeclared key is rejected at parse time, not
154+
stripped. Five keys that were once authorable — `permissions`, `active`,
155+
`category`, `builtIn` and `requiresConfirmation` — were removed because nothing
156+
read them, and each rejects today with the prescription for what to write
157+
instead. If you are porting an older record, the parse error is the instruction.
158+
159+
{/* os:check */}
160+
```typescript
161+
import { defineTool, defineSkill } from '@objectstack/spec/ai';
162+
163+
// A refinement: the underlying Action takes the full case payload, but the
164+
// agent only ever needs the record and a length hint.
165+
export const SummariseCaseTool = defineTool({
166+
name: 'summarise_case',
167+
label: 'Summarise Case',
168+
description:
169+
'Summarise a support case and its recent activity for a human reader. '
170+
+ 'Use it before escalating, so the summary can be pasted into the handover note.',
171+
objectName: 'support_case',
172+
parameters: {
173+
type: 'object',
174+
properties: {
175+
caseId: { type: 'string', description: 'Record id of the case to summarise' },
176+
length: { type: 'string', enum: ['short', 'detailed'] },
177+
},
178+
required: ['caseId'],
179+
},
180+
});
181+
182+
// The skill names it exactly the way it names a platform or materialised tool.
183+
export const CaseHandoverSkill = defineSkill({
184+
name: 'case_handover',
185+
label: 'Case Handover',
186+
surface: 'ask',
187+
instructions: 'Summarise before you escalate, and put the summary in the handover note.',
188+
tools: ['summarise_case', 'action_escalate_case', 'get_record'],
189+
});
190+
```
191+
192+
## How the name is resolved
193+
194+
A `skill.tools[]` entry resolves against three sources, in order:
195+
196+
1. the stack's own `tools[]` names — the refinement records on this page;
197+
2. the curated registry of tools the platform runtime registers at boot;
198+
3. the materialised `action_<name>` family, one per AI-exposed Action declared
199+
on the stack or on any object.
200+
201+
A trailing wildcard matches every member of that universe sharing the prefix, so
202+
`action_*` subscribes a skill to all of the app's exposed Actions at once.
203+
204+
**Agents do not name tools.** `agent.tools` was removed in protocol 17: it was
205+
the one seam that let an agent reach a tool no skill of its surface declared.
206+
An agent reaches exactly the tools its surface-compatible skills declare
207+
(ADR-0064), so a tool record becomes reachable by attaching the skill that names
208+
it, never by listing it on the agent.
209+
210+
---
211+
212+
## See also
213+
214+
- [Actions as Tools](/docs/ai/actions-as-tools) — the default path: an Action materialised as an `action_<name>` tool
215+
- [AI Agents](/docs/ai/agents) — the two platform agents, and skills as the extension primitive
216+
- [Connect an MCP Client](/docs/ai/connect-mcp) — reaching tools outside your app
217+
- [AI Skills System](/docs/ai/skills) — a different layer: the `SKILL.md` knowledge modules that teach a coding assistant to *write* your metadata
218+
- [Tool reference](/docs/references/ai/tool) — every field, generated from `ToolSchema`

0 commit comments

Comments
 (0)