2026-03-20 10:54:48 -07:00
---
title: "Building Plugins"
2026-03-22 11:35:53 -07:00
sidebarTitle: "Getting Started"
summary: "Create your first OpenClaw plugin in minutes"
2026-03-20 10:54:48 -07:00
read_when:
- You want to create a new OpenClaw plugin
2026-03-22 11:35:53 -07:00
- You need a quick-start for plugin development
2026-03-20 10:54:48 -07:00
- You are adding a new channel, provider, tool, or other capability to OpenClaw
---
# Building Plugins
Plugins extend OpenClaw with new capabilities: channels, model providers, speech,
2026-03-22 11:35:53 -07:00
image generation, web search, agent tools, or any combination.
2026-03-20 10:54:48 -07:00
2026-03-22 11:58:01 -07:00
You do not need to add your plugin to the OpenClaw repository. Publish to
[ClawHub ](/tools/clawhub ) or npm and users install with
`openclaw plugins install <package-name>` . OpenClaw tries ClawHub first and
falls back to npm automatically.
2026-03-20 10:54:48 -07:00
## Prerequisites
- Node >= 22 and a package manager (npm or pnpm)
- Familiarity with TypeScript (ESM)
2026-03-22 11:35:53 -07:00
- For in-repo plugins: repository cloned and `pnpm install` done
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
## What kind of plugin?
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
<CardGroup cols={3}>
2026-03-22 11:50:53 -07:00
<Card title="Channel plugin" icon="messages-square" href="/plugins/sdk-channel-plugins">
2026-03-22 11:35:53 -07:00
Connect OpenClaw to a messaging platform (Discord, IRC, etc.)
</Card>
2026-03-22 11:50:53 -07:00
<Card title="Provider plugin" icon="cpu" href="/plugins/sdk-provider-plugins">
2026-03-22 11:35:53 -07:00
Add a model provider (LLM, proxy, or custom endpoint)
</Card>
<Card title="Tool / hook plugin" icon="wrench">
Register agent tools, event hooks, or services — continue below
</Card>
</CardGroup>
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
## Quick start: tool plugin
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
This walkthrough creates a minimal plugin that registers an agent tool. Channel
and provider plugins have dedicated guides linked above.
2026-03-20 10:54:48 -07:00
<Steps>
2026-03-22 11:35:53 -07:00
<Step title="Create the package and manifest">
<CodeGroup>
```json package.json
2026-03-20 10:54:48 -07:00
{
2026-03-22 11:35:53 -07:00
"name": "@myorg/openclaw-my-plugin",
2026-03-20 10:54:48 -07:00
"version": "1.0.0",
"type": "module",
"openclaw": {
2026-03-22 11:35:53 -07:00
"extensions": ["./index.ts"]
2026-03-20 10:54:48 -07:00
}
}
` ``
2026-03-22 11:35:53 -07:00
` ``json openclaw.plugin.json
2026-03-20 10:54:48 -07:00
{
2026-03-22 11:35:53 -07:00
"id": "my-plugin",
"name": "My Plugin",
"description": "Adds a custom tool to OpenClaw",
"configSchema": {
"type": "object",
"additionalProperties": false
2026-03-20 10:54:48 -07:00
}
}
` ``
2026-03-22 11:35:53 -07:00
</CodeGroup>
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
Every plugin needs a manifest, even with no config. See
[Manifest](/plugins/manifest) for the full schema.
2026-03-20 10:54:48 -07:00
</Step>
2026-03-22 11:35:53 -07:00
<Step title="Write the entry point">
2026-03-20 10:54:48 -07:00
` ``typescript
2026-03-22 11:35:53 -07:00
// index.ts
2026-03-21 20:07:24 +00:00
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
2026-03-22 11:35:53 -07:00
import { Type } from "@sinclair/typebox";
2026-03-20 10:54:48 -07:00
export default definePluginEntry({
id: "my-plugin",
name: "My Plugin",
2026-03-22 11:35:53 -07:00
description: "Adds a custom tool to OpenClaw",
2026-03-20 10:54:48 -07:00
register(api) {
2026-03-22 11:35:53 -07:00
api.registerTool({
name: "my_tool",
description: "Do a thing",
parameters: Type.Object({ input: Type.String() }),
async execute(_id, params) {
return { content: [{ type: "text", text: ` Got: ${params.input}` }] };
},
});
2026-03-20 10:54:48 -07:00
},
});
` ``
2026-03-22 11:35:53 -07:00
` definePluginEntry` is for non-channel plugins. For channels, use
` defineChannelPluginEntry` — see [Channel Plugins](/plugins/sdk-channel-plugins).
For full entry point options, see [Entry Points](/plugins/sdk-entrypoints).
2026-03-20 10:54:48 -07:00
</Step>
2026-03-22 11:35:53 -07:00
<Step title="Test and publish">
2026-03-20 10:54:48 -07:00
2026-03-22 11:58:01 -07:00
**External plugins:** publish to [ClawHub](/tools/clawhub) or npm, then install:
2026-03-20 10:54:48 -07:00
` ``bash
openclaw plugins install @myorg/openclaw-my-plugin
` ``
2026-03-22 11:58:01 -07:00
OpenClaw checks ClawHub first, then falls back to npm.
2026-03-22 11:35:53 -07:00
**In-repo plugins:** place under ` extensions/` — automatically discovered.
2026-03-20 10:54:48 -07:00
` ``bash
2026-03-22 11:35:53 -07:00
pnpm test -- extensions/my-plugin/
2026-03-20 10:54:48 -07:00
` ``
</Step>
</Steps>
2026-03-22 11:35:53 -07:00
## Plugin capabilities
A single plugin can register any number of capabilities via the ` api` object:
2026-03-26 15:09:01 +00:00
| Capability | Registration method | Detailed guide |
| --------------------- | --------------------------------------------- | ------------------------------------------------------------------------------- |
| Text inference (LLM) | ` api.registerProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins) |
| CLI inference backend | ` api.registerCliBackend(...)` | [CLI Backends](/gateway/cli-backends) |
| Channel / messaging | ` api.registerChannel(...)` | [Channel Plugins](/plugins/sdk-channel-plugins) |
| Speech (TTS/STT) | ` api.registerSpeechProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Media understanding | ` api.registerMediaUnderstandingProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Image generation | ` api.registerImageGenerationProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Web search | ` api.registerWebSearchProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Agent tools | ` api.registerTool(...)` | Below |
| Custom commands | ` api.registerCommand(...)` | [Entry Points](/plugins/sdk-entrypoints) |
| Event hooks | ` api.registerHook(...)` | [Entry Points](/plugins/sdk-entrypoints) |
| HTTP routes | ` api.registerHttpRoute(...)` | [Internals](/plugins/architecture#gateway-http-routes) |
| CLI subcommands | ` api.registerCli(...)` | [Entry Points](/plugins/sdk-entrypoints) |
2026-03-22 11:35:53 -07:00
For the full registration API, see [SDK Overview](/plugins/sdk-overview#registration-api).
2026-03-25 00:11:13 -05:00
Hook guard semantics to keep in mind:
- ` before_tool_call`: ` { block: true }` is terminal and stops lower-priority handlers.
- ` before_tool_call`: ` { block: false }` is treated as no decision.
- ` message_sending`: ` { cancel: true }` is terminal and stops lower-priority handlers.
- ` message_sending`: ` { cancel: false }` is treated as no decision.
See [SDK Overview hook decision semantics](/plugins/sdk-overview#hook-decision-semantics) for details.
2026-03-20 11:32:11 -07:00
## Registering agent tools
2026-03-22 11:35:53 -07:00
Tools are typed functions the LLM can call. They can be required (always
available) or optional (user opt-in):
2026-03-20 11:32:11 -07:00
` ``typescript
2026-03-22 11:35:53 -07:00
register(api) {
// Required tool — always available
api.registerTool({
name: "my_tool",
description: "Do a thing",
parameters: Type.Object({ input: Type.String() }),
async execute(_id, params) {
return { content: [{ type: "text", text: params.input }] };
},
});
// Optional tool — user must add to allowlist
api.registerTool(
{
name: "workflow_tool",
description: "Run a workflow",
parameters: Type.Object({ pipeline: Type.String() }),
2026-03-20 11:32:11 -07:00
async execute(_id, params) {
2026-03-22 11:35:53 -07:00
return { content: [{ type: "text", text: params.pipeline }] };
2026-03-20 11:32:11 -07:00
},
2026-03-22 11:35:53 -07:00
},
{ optional: true },
);
}
2026-03-20 11:32:11 -07:00
` ``
2026-03-22 11:35:53 -07:00
Users enable optional tools in config:
2026-03-20 11:32:11 -07:00
` ``json5
{
tools: { allow: ["workflow_tool"] },
}
` ``
2026-03-22 11:35:53 -07:00
- Tool names must not clash with core tools (conflicts are skipped)
- Use ` optional: true` for tools with side effects or extra binary requirements
2026-03-20 11:32:11 -07:00
- Users can enable all tools from a plugin by adding the plugin id to ` tools.allow`
2026-03-22 11:35:53 -07:00
## Import conventions
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
Always import from focused ` openclaw/plugin-sdk/<subpath>` paths:
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
` ``typescript
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
// Wrong: monolithic root (deprecated, will be removed)
import { ... } from "openclaw/plugin-sdk";
` ``
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
For the full subpath reference, see [SDK Overview](/plugins/sdk-overview).
2026-03-20 10:54:48 -07:00
2026-03-22 11:35:53 -07:00
Within your plugin, use local barrel files (` api.ts`, ` runtime-api.ts`) for
internal imports — never import your own plugin through its SDK path.
2026-03-20 10:54:48 -07:00
## Pre-submission checklist
<Check>**package.json** has correct ` openclaw` metadata</Check>
2026-03-22 11:35:53 -07:00
<Check>**openclaw.plugin.json** manifest is present and valid</Check>
2026-03-20 10:54:48 -07:00
<Check>Entry point uses ` defineChannelPluginEntry` or ` definePluginEntry`</Check>
2026-03-22 11:35:53 -07:00
<Check>All imports use focused ` plugin-sdk/<subpath>` paths</Check>
2026-03-20 10:54:48 -07:00
<Check>Internal imports use local modules, not SDK self-imports</Check>
2026-03-22 11:35:53 -07:00
<Check>Tests pass (` pnpm test -- extensions/my-plugin/`)</Check>
2026-03-20 10:54:48 -07:00
<Check>` pnpm check` passes (in-repo plugins)</Check>
2026-03-26 07:34:08 -05:00
## Beta Release Testing
1. Watch for GitHub release tags on [openclaw/openclaw](https://github.com/openclaw/openclaw/releases) and subscribe via ` Watch` > ` Releases`. Beta tags look like ` v2026.3.N-beta.1`. You can also turn on notifications for the official OpenClaw X account [@openclaw](https://x.com/openclaw) for release announcements.
2. Test your plugin against the beta tag as soon as it appears. The window before stable is typically only a few hours.
3. Post in your plugin's thread in the ` plugin-forum` Discord channel after testing with either ` all good` or what broke. If you do not have a thread yet, create one.
2026-03-26 09:31:59 -05:00
4. If something breaks, open or update an issue titled ` Beta blocker: <plugin-name> - <summary>` and apply the ` beta-blocker` label. Put the issue link in your thread.
5. Open a PR to ` main` titled ` fix(<plugin-id>): beta blocker - <summary>` and link the issue in both the PR and your Discord thread. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation. Blockers with a PR get merged; blockers without one might ship anyway. Maintainers watch these threads during beta testing.
6. Silence means green. If you miss the window, your fix likely lands in the next cycle.
2026-03-26 07:34:08 -05:00
2026-03-22 11:35:53 -07:00
## Next steps
<CardGroup cols={2}>
2026-03-22 11:50:53 -07:00
<Card title="Channel Plugins" icon="messages-square" href="/plugins/sdk-channel-plugins">
2026-03-22 11:35:53 -07:00
Build a messaging channel plugin
</Card>
2026-03-22 11:50:53 -07:00
<Card title="Provider Plugins" icon="cpu" href="/plugins/sdk-provider-plugins">
2026-03-22 11:35:53 -07:00
Build a model provider plugin
</Card>
2026-03-22 11:50:53 -07:00
<Card title="SDK Overview" icon="book-open" href="/plugins/sdk-overview">
2026-03-22 11:35:53 -07:00
Import map and registration API reference
</Card>
2026-03-22 11:50:53 -07:00
<Card title="Runtime Helpers" icon="settings" href="/plugins/sdk-runtime">
2026-03-22 11:35:53 -07:00
TTS, search, subagent via api.runtime
</Card>
2026-03-22 11:50:53 -07:00
<Card title="Testing" icon="test-tubes" href="/plugins/sdk-testing">
2026-03-22 11:35:53 -07:00
Test utilities and patterns
</Card>
2026-03-22 11:50:53 -07:00
<Card title="Plugin Manifest" icon="file-json" href="/plugins/manifest">
2026-03-22 11:35:53 -07:00
Full manifest schema reference
</Card>
</CardGroup>