mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 17:54:47 +00:00
docs: add tool plugin authoring guide
This commit is contained in:
@@ -90,7 +90,8 @@ npm run plugin:validate
|
||||
`defineToolPlugin`. `plugins build` imports that entry, reads its static tool
|
||||
metadata, writes `openclaw.plugin.json`, and keeps `package.json`
|
||||
`openclaw.extensions` aligned. `plugins validate` checks that the generated
|
||||
manifest, package metadata, and current entry export still agree.
|
||||
manifest, package metadata, and current entry export still agree. See
|
||||
[Tool Plugins](/plugins/tool-plugins) for the full authoring workflow.
|
||||
|
||||
The scaffold writes TypeScript source but generates metadata from the built
|
||||
`./dist/index.js` entry so the workflow also works with the published CLI. Use
|
||||
|
||||
@@ -1220,6 +1220,7 @@
|
||||
"group": "Building plugins",
|
||||
"pages": [
|
||||
"plugins/building-plugins",
|
||||
"plugins/tool-plugins",
|
||||
"plugins/sdk-channel-plugins",
|
||||
"plugins/sdk-provider-plugins",
|
||||
"plugins/cli-backend-plugins",
|
||||
|
||||
@@ -38,8 +38,11 @@ install from npm during the launch cutover.
|
||||
<Card title="CLI backend plugin" icon="terminal" href="/plugins/cli-backend-plugins">
|
||||
Map a local AI CLI into OpenClaw's text fallback runner
|
||||
</Card>
|
||||
<Card title="Tool / hook plugin" icon="wrench" href="/plugins/hooks">
|
||||
Register agent tools, event hooks, or services - continue below
|
||||
<Card title="Tool plugin" icon="wrench" href="/plugins/tool-plugins">
|
||||
Add simple typed agent tools with generated manifest metadata
|
||||
</Card>
|
||||
<Card title="Hook plugin" icon="plug" href="/plugins/hooks">
|
||||
Register event hooks, services, or advanced runtime integrations
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -53,6 +56,7 @@ until the plugin is installed.
|
||||
|
||||
This walkthrough creates a minimal plugin that registers an agent tool. Channel
|
||||
and provider plugins have dedicated guides linked above.
|
||||
For the detailed tool-only workflow, see [Tool Plugins](/plugins/tool-plugins).
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the package and manifest">
|
||||
@@ -133,8 +137,9 @@ and provider plugins have dedicated guides linked above.
|
||||
`defineToolPlugin` is for simple agent-tool plugins. For providers, hooks,
|
||||
services, and other advanced non-channel plugins, use `definePluginEntry`.
|
||||
For channels, use `defineChannelPluginEntry` - see
|
||||
[Channel Plugins](/plugins/sdk-channel-plugins). For full entry point
|
||||
options, see [Entry Points](/plugins/sdk-entrypoints).
|
||||
[Channel Plugins](/plugins/sdk-channel-plugins). For the full
|
||||
`defineToolPlugin` workflow, see [Tool Plugins](/plugins/tool-plugins). For
|
||||
full entry point options, see [Entry Points](/plugins/sdk-entrypoints).
|
||||
|
||||
</Step>
|
||||
|
||||
|
||||
@@ -40,8 +40,9 @@ and inferred built JavaScript peers do not make an escaping `extensions` or
|
||||
`setupEntry` source path valid.
|
||||
|
||||
<Tip>
|
||||
**Looking for a walkthrough?** See [Channel Plugins](/plugins/sdk-channel-plugins)
|
||||
or [Provider Plugins](/plugins/sdk-provider-plugins) for step-by-step guides.
|
||||
**Looking for a walkthrough?** See [Tool Plugins](/plugins/tool-plugins),
|
||||
[Channel Plugins](/plugins/sdk-channel-plugins), or
|
||||
[Provider Plugins](/plugins/sdk-provider-plugins) for step-by-step guides.
|
||||
</Tip>
|
||||
|
||||
## `defineToolPlugin`
|
||||
|
||||
369
docs/plugins/tool-plugins.md
Normal file
369
docs/plugins/tool-plugins.md
Normal file
@@ -0,0 +1,369 @@
|
||||
---
|
||||
summary: "Build simple typed agent tools with defineToolPlugin and openclaw plugins init/build/validate"
|
||||
title: "Tool plugins"
|
||||
sidebarTitle: "Tool Plugins"
|
||||
read_when:
|
||||
- You want to build a simple OpenClaw plugin that only adds agent tools
|
||||
- You want to use defineToolPlugin instead of hand-writing plugin manifest metadata
|
||||
- You need to scaffold, generate, validate, test, or publish a tool-only plugin
|
||||
---
|
||||
|
||||
Tool plugins add agent-callable tools to OpenClaw without adding a channel,
|
||||
model provider, hook, service, or setup backend. Use `defineToolPlugin` when the
|
||||
plugin owns a fixed list of tools and you want OpenClaw to generate the manifest
|
||||
metadata that keeps those tools discoverable without loading runtime code.
|
||||
|
||||
The recommended flow is:
|
||||
|
||||
1. Scaffold a package with `openclaw plugins init`.
|
||||
2. Write tools with `defineToolPlugin`.
|
||||
3. Build JavaScript.
|
||||
4. Generate `openclaw.plugin.json` and `package.json` metadata with
|
||||
`openclaw plugins build`.
|
||||
5. Validate the generated metadata before publishing or installing.
|
||||
|
||||
For provider, channel, hook, service, or mixed-capability plugins, start with
|
||||
[Building plugins](/plugins/building-plugins), [Channel Plugins](/plugins/sdk-channel-plugins),
|
||||
or [Provider Plugins](/plugins/sdk-provider-plugins) instead.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Node >= 22.
|
||||
- TypeScript ESM package output.
|
||||
- `typebox` for config and tool parameter schemas.
|
||||
- `openclaw >=2026.5.17`, the first OpenClaw version that exports
|
||||
`openclaw/plugin-sdk/tool-plugin`.
|
||||
- A package root that can ship `dist/`, `openclaw.plugin.json`, and
|
||||
`package.json`.
|
||||
|
||||
The generated plugin imports `typebox` at runtime, so keep `typebox` in
|
||||
`dependencies`, not only `devDependencies`.
|
||||
|
||||
## Quickstart
|
||||
|
||||
Create a new plugin package:
|
||||
|
||||
```bash
|
||||
openclaw plugins init stock-quotes --name "Stock Quotes"
|
||||
cd stock-quotes
|
||||
npm install
|
||||
npm run plugin:build
|
||||
npm run plugin:validate
|
||||
npm test
|
||||
```
|
||||
|
||||
The scaffold creates:
|
||||
|
||||
- `src/index.ts`: a `defineToolPlugin` entry with an `echo` tool.
|
||||
- `src/index.test.ts`: a small metadata test.
|
||||
- `tsconfig.json`: NodeNext TypeScript output to `dist/`.
|
||||
- `package.json`: scripts, runtime dependencies, and
|
||||
`openclaw.extensions: ["./dist/index.js"]`.
|
||||
- `openclaw.plugin.json`: generated manifest metadata for the initial tool.
|
||||
|
||||
Expected validation output:
|
||||
|
||||
```text
|
||||
Plugin stock-quotes is valid.
|
||||
```
|
||||
|
||||
## Write a tool
|
||||
|
||||
`defineToolPlugin` takes plugin identity, an optional config schema, and a
|
||||
static list of tools. Parameter and config types are inferred from TypeBox
|
||||
schemas.
|
||||
|
||||
```typescript
|
||||
import { Type } from "typebox";
|
||||
import { defineToolPlugin } from "openclaw/plugin-sdk/tool-plugin";
|
||||
|
||||
export default defineToolPlugin({
|
||||
id: "stock-quotes",
|
||||
name: "Stock Quotes",
|
||||
description: "Fetch stock quote snapshots.",
|
||||
configSchema: Type.Object({
|
||||
apiKey: Type.Optional(Type.String({ description: "Quote API key." })),
|
||||
baseUrl: Type.Optional(Type.String({ description: "Quote API base URL." })),
|
||||
}),
|
||||
tools: (tool) => [
|
||||
tool({
|
||||
name: "stock_quote",
|
||||
label: "Stock Quote",
|
||||
description: "Fetch a stock quote snapshot.",
|
||||
parameters: Type.Object({
|
||||
symbol: Type.String({ description: "Ticker symbol, for example OPEN." }),
|
||||
}),
|
||||
async execute({ symbol }, config, context) {
|
||||
context.signal?.throwIfAborted();
|
||||
return {
|
||||
symbol: symbol.toUpperCase(),
|
||||
configured: Boolean(config.apiKey),
|
||||
baseUrl: config.baseUrl ?? "https://api.example.com",
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Tool names are the stable API. Pick names that are unique, lowercase, and
|
||||
specific enough to avoid collisions with core tools or other plugins.
|
||||
|
||||
## Return values
|
||||
|
||||
`defineToolPlugin` wraps plain return values into the OpenClaw tool-result
|
||||
format:
|
||||
|
||||
- Return a string when the model should see that exact text.
|
||||
- Return a JSON-compatible value when you want the model to see formatted JSON
|
||||
and OpenClaw to keep the original value in `details`.
|
||||
|
||||
```typescript
|
||||
tool({
|
||||
name: "echo_text",
|
||||
description: "Echo input text.",
|
||||
parameters: Type.Object({
|
||||
input: Type.String(),
|
||||
}),
|
||||
execute: ({ input }) => input,
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
tool({
|
||||
name: "echo_json",
|
||||
description: "Echo input as structured JSON.",
|
||||
parameters: Type.Object({
|
||||
input: Type.String(),
|
||||
}),
|
||||
execute: ({ input }) => ({ input, length: input.length }),
|
||||
});
|
||||
```
|
||||
|
||||
Use `definePluginEntry` instead of `defineToolPlugin` when you need to return a
|
||||
custom `AgentToolResult`, stream custom progress semantics, register dynamic
|
||||
tools, or combine tools with hooks, services, providers, commands, or other
|
||||
advanced surfaces.
|
||||
|
||||
## Configuration
|
||||
|
||||
`configSchema` is optional. If you omit it, OpenClaw uses a strict empty object
|
||||
schema and the generated manifest still includes `configSchema`.
|
||||
|
||||
```typescript
|
||||
export default defineToolPlugin({
|
||||
id: "no-config-tools",
|
||||
name: "No Config Tools",
|
||||
description: "Adds tools that do not need configuration.",
|
||||
tools: () => [],
|
||||
});
|
||||
```
|
||||
|
||||
When you include `configSchema`, the second `execute` argument is typed from the
|
||||
schema:
|
||||
|
||||
```typescript
|
||||
const configSchema = Type.Object({
|
||||
apiKey: Type.String(),
|
||||
});
|
||||
|
||||
export default defineToolPlugin({
|
||||
id: "configured-tools",
|
||||
name: "Configured Tools",
|
||||
description: "Adds configured tools.",
|
||||
configSchema,
|
||||
tools: (tool) => [
|
||||
tool({
|
||||
name: "configured_ping",
|
||||
description: "Check whether configuration is available.",
|
||||
parameters: Type.Object({}),
|
||||
execute: (_params, config) => ({ hasKey: config.apiKey.length > 0 }),
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
OpenClaw reads plugin config from the plugin entry in the Gateway config. Do not
|
||||
hard-code secrets in source or in docs examples. Use config, environment
|
||||
variables, or SecretRefs according to the plugin's security model.
|
||||
|
||||
## Generated metadata
|
||||
|
||||
OpenClaw discovers installed plugins from cold metadata. It must be able to read
|
||||
the plugin manifest before importing plugin runtime code. `defineToolPlugin`
|
||||
therefore exposes static metadata, and `openclaw plugins build` writes that
|
||||
metadata into the package.
|
||||
|
||||
Run the generator after changing plugin id, name, description, config schema,
|
||||
activation, or tool names:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
openclaw plugins build --entry ./dist/index.js
|
||||
```
|
||||
|
||||
For a one-tool plugin, the generated manifest looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "stock-quotes",
|
||||
"name": "Stock Quotes",
|
||||
"description": "Fetch stock quote snapshots.",
|
||||
"version": "0.1.0",
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
},
|
||||
"activation": {
|
||||
"onStartup": true
|
||||
},
|
||||
"contracts": {
|
||||
"tools": ["stock_quote"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`contracts.tools` is the important discovery contract. It tells OpenClaw which
|
||||
plugin owns each tool without loading every installed plugin runtime. If the
|
||||
manifest is stale, the tool may be missing from discovery or the wrong plugin
|
||||
may be blamed for a registration error.
|
||||
|
||||
## Package metadata
|
||||
|
||||
For the simple tool-plugin workflow, `openclaw plugins build` aligns
|
||||
`package.json` to the selected single runtime entry:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "module",
|
||||
"files": ["dist", "openclaw.plugin.json", "README.md"],
|
||||
"dependencies": {
|
||||
"typebox": "^1.1.38"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.17"
|
||||
},
|
||||
"openclaw": {
|
||||
"extensions": ["./dist/index.js"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use built JavaScript such as `./dist/index.js` for installed packages. Source
|
||||
entries are useful in workspace development, but published packages should not
|
||||
depend on TypeScript runtime loading.
|
||||
|
||||
## Validate in CI
|
||||
|
||||
Use `plugins build --check` to fail CI when generated metadata is stale without
|
||||
rewriting files:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
openclaw plugins build --entry ./dist/index.js --check
|
||||
openclaw plugins validate --entry ./dist/index.js
|
||||
npm test
|
||||
```
|
||||
|
||||
`plugins validate` checks that:
|
||||
|
||||
- `openclaw.plugin.json` exists and passes the normal manifest loader.
|
||||
- The current entry exports `defineToolPlugin` metadata.
|
||||
- Generated manifest fields match the entry metadata.
|
||||
- `contracts.tools` matches the declared tool names.
|
||||
- `package.json` points `openclaw.extensions` at the selected runtime entry.
|
||||
|
||||
## Install and inspect locally
|
||||
|
||||
From a separate OpenClaw checkout or installed CLI, install the package path:
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./stock-quotes
|
||||
openclaw plugins inspect stock-quotes --runtime
|
||||
```
|
||||
|
||||
For a packaged smoke, pack first and install the tarball:
|
||||
|
||||
```bash
|
||||
npm pack
|
||||
openclaw plugins install npm-pack:./openclaw-plugin-stock-quotes-0.1.0.tgz
|
||||
openclaw plugins inspect stock-quotes --runtime --json
|
||||
```
|
||||
|
||||
After installation, start or restart the Gateway and ask the agent to use the
|
||||
tool. If you are debugging tool visibility, inspect the plugin runtime and the
|
||||
effective tool catalog before changing the code.
|
||||
|
||||
## Publish
|
||||
|
||||
Publish through ClawHub when the package is ready:
|
||||
|
||||
```bash
|
||||
clawhub package publish your-org/stock-quotes --dry-run
|
||||
clawhub package publish your-org/stock-quotes
|
||||
```
|
||||
|
||||
Install with an explicit ClawHub locator:
|
||||
|
||||
```bash
|
||||
openclaw plugins install clawhub:your-org/stock-quotes
|
||||
```
|
||||
|
||||
Bare npm package specs remain supported during the launch cutover, but ClawHub
|
||||
is the preferred discovery and distribution surface for OpenClaw plugins.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `plugin entry not found: ./dist/index.js`
|
||||
|
||||
The selected entry file does not exist. Run `npm run build`, then rerun
|
||||
`openclaw plugins build --entry ./dist/index.js` or
|
||||
`openclaw plugins validate --entry ./dist/index.js`.
|
||||
|
||||
### `plugin entry does not expose defineToolPlugin metadata`
|
||||
|
||||
The entry did not export a value created by `defineToolPlugin`. Check that the
|
||||
module default export is the `defineToolPlugin(...)` result, or pass the correct
|
||||
entry with `--entry`.
|
||||
|
||||
### `openclaw.plugin.json generated metadata is stale`
|
||||
|
||||
The manifest no longer matches the entry metadata. Run:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
openclaw plugins build --entry ./dist/index.js
|
||||
```
|
||||
|
||||
Commit both `openclaw.plugin.json` and `package.json` changes.
|
||||
|
||||
### `package.json openclaw.extensions must include ./dist/index.js`
|
||||
|
||||
The package metadata points at a different runtime entry. Run
|
||||
`openclaw plugins build --entry ./dist/index.js` so the generator aligns the
|
||||
package metadata with the entry you intend to ship.
|
||||
|
||||
### `Cannot find package 'typebox'`
|
||||
|
||||
The built plugin imports `typebox` at runtime. Keep `typebox` in
|
||||
`dependencies`, reinstall package dependencies, rebuild, and rerun validation.
|
||||
|
||||
### Tool does not appear after install
|
||||
|
||||
Check these in order:
|
||||
|
||||
1. `openclaw plugins inspect <plugin-id> --runtime`
|
||||
2. `openclaw plugins validate --root <plugin-root> --entry ./dist/index.js`
|
||||
3. `openclaw.plugin.json` has `contracts.tools` with the expected tool names.
|
||||
4. `package.json` has `openclaw.extensions: ["./dist/index.js"]`.
|
||||
5. The Gateway was restarted or reloaded after installing the plugin.
|
||||
|
||||
## See also
|
||||
|
||||
- [Building plugins](/plugins/building-plugins)
|
||||
- [Plugin entry points](/plugins/sdk-entrypoints)
|
||||
- [Plugin SDK subpaths](/plugins/sdk-subpaths)
|
||||
- [Plugin manifest](/plugins/manifest)
|
||||
- [Plugins CLI](/cli/plugins)
|
||||
- [ClawHub publishing](/clawhub/publishing)
|
||||
Reference in New Issue
Block a user