Builtin Tool Authoring Guide
SkillAI & modelsHelps you build tool packages that let an AI agent call custom tools with manifests, executors, and runtimes.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Builtin Tool Authoring Guide skill
About this capability
Build LobeHub builtin tool packages. Use when adding agent-callable tools, manifests, executors, runtimes, inspectors, renders, placeholders, streaming, interventions, portals, or tool registries.
What this skill tells your AI
The instructions your AI receives, as published by lobehub/lobehub in .agents/skills/builtin-tool/SKILL.md and read by ahel’s review.
A builtin tool is a package the agent runtime can call. It ships five faces:
| Face | Lives in | Audience |
|---|---|---|
| Manifest + types | src/{manifest,types,systemRole}.ts | The LLM (tool spec + system prompt) |
| ExecutionRuntime | src/ExecutionRuntime/ | Server / desktop / any runtime caller |
| Executor | src/client/executor/ | Frontend (wraps stores/services) |
| Client UI | src/client/{Inspector,Render,…}/ | Chat UI |
| Registry wiring | packages/builtin-tools/src/*.ts + src/store/tool/slices/builtin/executors/index.ts | Framework |
Read These First
| Question | Doc |
|---|---|
| Where do files live? What does each face do? Wiring? | architecture.md |
| How do I name the tool, design APIs, write the manifest, executor, ExecutionRuntime? | tool-design.md |
| How do I build Inspector / Render / Placeholder / Streaming / Intervention / Portal? | ui/ |
When to Use This Skill
- Creating a new
packages/builtin-tool-<name>/package - Adding a new API method to an existing builtin tool
- Building or restyling any of the 6 client surfaces for a tool
- Wiring a tool into the central registries
- Debugging "tool not found / API not found / render not showing / placeholder stuck" errors
Top-Level Design Principles
lobe-<domain>identifier is permanent. It's stored in message history. Renames need@deprecatedaliases (seepackages/builtin-tools/src/inspectors.ts:88-89). Get it right the first time.- ApiName is an
as constobject, not a TS enum. It doubles as the runtime listBaseExecutoriterates over. - Three result fields, three audiences:
content: string→ the LLM reads itstate: Record<…>→ the UI'spluginState; result-domain only, never echo all params backerror: { type, message, body? }→ both LLM and UI;typeis a stable code
- Split execution from frontend wiring.
src/ExecutionRuntime/— pure runtime, no React, no Zustand, accepts services via constructor. The default place for new logic.src/client/executor/—BaseExecutorsubclass that callsExecutionRuntime(or stores/services directly when frontend-only).
- UI defaults to "do nothing". Inspector is required (the header strip). Render/Placeholder/Streaming/Intervention/Portal are added only when there's something specific to show — empty registries are fine.
- Style with
createStaticStyles + cssVar.*(zero-runtime). Fall back tocreateStyles + tokenonly when you genuinely need runtime values. Use@lobehub/uicomponents, not raw antd. - i18n keys live in
packages/locales/src/default/plugin.ts. Inspector titles must come fromt('builtins.<identifier>.apiName.<api>')so something renders while args stream.
Package Layout (preferred, post-2026 convention)
packages/builtin-tool-<name>/
├── package.json
└── src/
├── index.ts # exports manifest + types + systemRole + Identifier (no React, no stores)
├── manifest.ts # BuiltinToolManifest with JSON Schema for every API
├── types.ts # ApiName const + Params/State interfaces per API
├── systemRole.ts # System prompt teaching the model when/how to use the APIs
├── ExecutionRuntime/ # ✅ Default home for runtime logic (server- or anywhere-callable)
│ └── index.ts
└── client/
├── index.ts # Re-exports for the registries
├── executor/ # ✅ Frontend executor — extends BaseExecutor, often delegates to ExecutionRuntime
│ └── index.ts
├── Inspector/ # required — header chip per API
├── Render/ # optional — rich result card
├── Placeholder/ # optional — skeleton during streaming/execution
├── Streaming/ # optional — live output renderer (e.g. RunCommand, WriteFile)
├── Intervention/ # optional — approval / edit-before-run UI
├── Portal/ # optional — full-screen detail view
└── components/ # shared subcomponents used by the surfaces above
Older packages (builtin-tool-task, builtin-tool-calculator, etc.) still have src/executor/ as a sibling of src/client/. That's grandfathered; don't relocate without a deliberate refactor. New packages and new APIs added to existing packages should follow the layout above.
package.json exports map:
"exports": {
".": "./src/index.ts",
"./client": "./src/client/index.ts",
"./executor": "./src/client/executor/index.ts",
"./executionRuntime": "./src/ExecutionRuntime/index.ts"
}
Authoring Checklist
Before opening the PR:
- Identifier follows
lobe-<domain>and is stable (lives in message history). - Every
<Name>ApiNamevalue has: a manifestapi[]entry, an executor method, an Inspector, an i18napiName.*key. -
Paramsinterfaces match the JSON Schema;Stateinterfaces match what the executor returns and what the UI surfaces read. - System prompt disambiguates confusable APIs and points to batch variants.
- Runtime logic lives in
ExecutionRuntime/; theclient/executor/only wires stores/services and delegates. - Executor returns
{ success, content, state, error? }via a singletoResult()funnel —contentalways non-empty (default toerror.message). - Inspector handles
isArgumentsStreaming,isLoading,partialArgs, missingpluginState. - Render returns
nulluntil it has data; only created for APIs with rich results. - Placeholder added if the API has a perceivable execution lag (search, list, crawl).
- Streaming added for APIs that emit incremental output (run command, write file, code execution).
- Intervention added if
humanInterventionis set in the manifest. - All registry files updated (see architecture.md → Registry wiring).
- i18n keys in
packages/locales/src/default/plugin.tsplus dev seeds inen-US/zh-CN. -
bunx vitest run --silent='passed-only' 'packages/builtin-tool-<name>'passes. -
bun run type-checkpasses.
Reference Tools
Pick the closest neighbor and copy:
| If your tool is… | Read first |
|---|---|
| Pure-compute, no UI state | packages/builtin-tool-calculator/ — ExecutionRuntime reuses executor (mathjs/nerdamer work everywhere) |
| CRUD over a domain entity | packages/builtin-tool-task/ — full Inspector + Render set, batch variants |
| Heavy UI (Inspector/Render/Placeholder/Portal) | packages/builtin-tool-web-browsing/ — search-style result UI, Portal for detail view |
| Desktop / filesystem with all surfaces (incl. Streaming + Intervention) | packages/builtin-tool-local-system/ — ExecutionRuntime injects an ILocalSystemService, executor calls it |
| Server-side pure (no client executor) | packages/builtin-tool-web-browsing/ — only ExecutionRuntime is exported; the chat client doesn't run it |
| Needs human approval before running | packages/builtin-tool-local-system/src/client/Intervention/ — per-API approval components |
Signals
- GitHub stars
- 82k
- Forks
- 16k
- Last commit
- Sep 2026
ahel recommends instead
Advanced
- Catalog kind
- skill
- Gateway key
builtin-tool-lobehub- Source
- github.com/lobehub/lobehub