defineToolPlugin builds a plugin that only adds agent-callable tools: no
channel, model provider, hook, service, or setup backend. It generates the
manifest metadata OpenClaw needs to discover tools without loading plugin
runtime code.
For provider, channel, hook, service, or mixed-capability plugins, start with
Building plugins, Channel Plugins,
or Provider Plugins instead.
Requirements
- Node 22.19+, Node 23.11+, or Node 24+.
- TypeScript ESM package output.
typeboxindependencies(not justdevDependencies- the generated plugin imports it at runtime).openclaw >=2026.5.17, the first version that exportsopenclaw/plugin-sdk/tool-plugin.- A package root that ships
dist/,openclaw.plugin.json, andpackage.json.
Quickstart
plugins init scaffolds:
| File | Purpose |
|---|---|
src/index.ts | defineToolPlugin entry with one echo tool |
src/index.test.ts | Metadata test asserting the tool list |
tsconfig.json | NodeNext TypeScript output to dist/ |
vitest.config.ts | Vitest config for src/**/*.test.ts |
package.json | Scripts, runtime deps, openclaw.extensions: ["./dist/index.js"] |
openclaw.plugin.json | Generated manifest metadata for the initial tool |
npm run plugin:build runs npm run build (tsc) then
openclaw plugins build --entry ./dist/index.js. npm run plugin:validate
rebuilds and runs openclaw plugins validate --entry ./dist/index.js.
Successful validation prints:
openclaw plugins init <id> options:
| Flag | Default | Effect |
|---|---|---|
--directory <path> | <id> | Output directory |
--name <name> | Title-cased <id> | Display name |
--type <type> | tool | Scaffold type: tool or provider |
--force | off | Overwrite an existing output directory |
Write a tool
defineToolPlugin takes plugin identity, an optional config schema, and a
static list of tools. Parameter and config types are inferred from the
TypeBox schemas.
Optional and factory tools
Setoptional: true when users should explicitly allowlist the tool before it
is sent to a model. openclaw plugins build writes the matching
toolMetadata.<tool>.optional manifest entry, so OpenClaw can see that the
tool is optional without loading plugin runtime code.
factory when a tool needs the runtime tool context before it can be
created - to opt out for a specific run, inspect sandbox state, or bind
runtime helpers. Metadata stays static even though the concrete tool is built
at runtime.
definePluginEntry
directly when the plugin computes tool names dynamically or combines tools
with hooks, services, providers, or commands.
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.
AgentToolResult or want to reuse an
existing api.registerTool implementation.
Configuration
configSchema is optional. Omit it and OpenClaw applies a strict empty object
schema; the generated manifest still includes configSchema.
configSchema, the second execute argument is typed from it:
Generated metadata
OpenClaw must read the plugin manifest before importing plugin runtime code.defineToolPlugin exposes static metadata for this, and
openclaw plugins build writes it into the package. Rerun the generator after
changing plugin id, name, description, config schema, activation, or tool
names:
contracts.tools is the important discovery contract: it tells OpenClaw which
plugin owns each tool without loading every installed plugin’s runtime. A
stale manifest means a tool can go missing from discovery, or a registration
error gets blamed on the wrong plugin.
Package metadata
openclaw plugins build also aligns package.json to the selected runtime
entry:
./dist/index.js), not a TypeScript source entry.
Source entries only work for workspace-local development.
Validate in CI
plugins build --check fails without rewriting files when generated metadata
is stale:
plugins validate checks that:
openclaw.plugin.jsonexists and passes the normal manifest loader.- The current entry exports
defineToolPluginmetadata. - Generated manifest fields match the entry metadata.
contracts.toolsmatches the declared tool names.package.jsonpointsopenclaw.extensionsat the selected runtime entry.
Install and inspect locally
From a separate OpenClaw checkout or installed CLI, install the package path:Publish
Publish through ClawHub once the package is ready.clawhub package publish
takes a source: a local folder, a GitHub repo (owner/repo[@ref]), or a
tarball URL.
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. Confirm the
module’s 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:
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
package metadata with the entry you intend to ship.
Cannot find package 'typebox'
The built plugin imports typebox at runtime. Keep it in dependencies,
reinstall, rebuild, and rerun validation.
Tool does not appear after install
Check these in order:openclaw plugins inspect <plugin-id> --runtimeopenclaw plugins validate --root <plugin-root> --entry ./dist/index.jsopenclaw.plugin.jsonhascontracts.toolswith the expected tool names.package.jsonhasopenclaw.extensions: ["./dist/index.js"].- The Gateway was restarted or reloaded after installing the plugin.