clawhub: prefix when you want ClawHub resolution.
Requirements
- Node 22.19+, Node 23.11+, or Node 24+, and
npmorpnpm. - TypeScript ESM modules.
- For in-repo bundled plugin work, clone the repository and run
pnpm install. Source-checkout plugin development is pnpm-only because OpenClaw discovers bundled plugins fromextensions/*workspace packages.
Choose the plugin shape
Channel plugin
Connect OpenClaw to a messaging platform.
Provider plugin
Add a model, media, search, fetch, speech, or realtime provider.
CLI backend plugin
Run a local AI CLI through OpenClaw model fallback.
Tool plugin
Register agent tools.
Quickstart
Build a minimal tool plugin by registering one required agent tool. This is the shortest useful plugin shape and covers the package, manifest, entry point, and local proof.Create package metadata
contracts.tools so OpenClaw can discover ownership without
eagerly loading every plugin runtime. Set activation.onStartup
intentionally; this example loads on Gateway startup.Host-trusted plugin surfaces are manifest-gated too and require explicit
declaration for installed plugins: api.registerAgentToolResultMiddleware(...)
needs each target runtime listed in contracts.agentToolResultMiddleware,
and api.registerTrustedToolPolicy(...) needs each policy id in
contracts.trustedToolPolicies. These declarations keep install-time
inspection and runtime registration aligned.For every manifest field, see Plugin manifest.Register the tool
index.ts
definePluginEntry for non-channel plugins. Channel plugins use
defineChannelPluginEntry from openclaw/plugin-sdk/core instead.Test the runtime
For an installed or external plugin, inspect the loaded runtime:If the plugin registers a CLI command, run that command too and confirm
output, for example
openclaw demo-plugin ping.For a bundled plugin in this repository, OpenClaw discovers source-checkout
plugin packages from the extensions/* workspace. Run the closest targeted
test:Test the package install
Before publishing a package-ready plugin, test the same install shape users
will get. First add a build step, point runtime entries such as
openclaw.extensions at built JavaScript like ./dist/index.js, and make
sure npm pack includes that dist/ output. TypeScript source entries are
only for source checkouts and local development paths.Then pack the plugin and install the tarball with npm-pack::npm-pack: uses OpenClaw’s managed per-plugin npm project, so it catches
runtime dependency mistakes that source checkout testing can hide. It proves
the package and dependency shape, not catalog-linked official trust.
Runtime imports must be in dependencies or optionalDependencies;
dependencies left only in devDependencies will not be installed for the
managed runtime project.Do not use a raw archive/path install as the final proof for official or
privileged plugin behavior. Raw sources are useful for local debugging, but
they do not prove the same dependency path as npm or ClawHub installs. If
your plugin relies on trusted official plugin status, add a second proof
through a catalog-backed official install or a published package path that
records official trust. See
Plugin dependency resolution for
install-root and dependency ownership details.Publish
Validate the package before publishing:Canonical ClawHub package snippets live in
docs/snippets/plugin-publish/.Registering tools
Tools can be required or optional. Required tools are always available when the plugin is enabled. Optional tools need explicit user opt-in before OpenClaw loads the owning plugin runtime.api.registerTool(...) must also be declared in the
plugin manifest:
tools.allow:
name, a non-function execute, or a tool descriptor without a parameters
object.
Tool factories receive a runtime-supplied context object. Use ctx.activeModel
when a tool needs to log, display, or adapt to the active model for the current
turn; it can include provider, modelId, and modelRef. Treat it as
informational runtime metadata, not a security boundary against the local
operator, installed plugin code, or a modified OpenClaw runtime. Sensitive
local tools should still require an explicit plugin or operator opt-in and
fail closed when active-model metadata is missing or unsuitable.
The manifest declares ownership and discovery; execution still calls the live
registered tool implementation. Keep toolMetadata.<tool>.optional: true
aligned with api.registerTool(..., { optional: true }) so OpenClaw can avoid
loading that plugin runtime until the tool is explicitly allowlisted.
Import conventions
Import from focused SDK subpaths:api.ts and
runtime-api.ts for internal imports. Do not import your own plugin through an
SDK path. Provider-specific helpers should stay in the provider package unless
the seam is truly generic.
Custom Gateway RPC methods are an advanced entry point. Keep them on a
plugin-specific prefix; core admin namespaces such as config.*,
exec.approvals.*, operator.admin.*, wizard.*, and update.* stay reserved
and resolve to operator.admin. The
openclaw/plugin-sdk/gateway-method-runtime bridge is reserved for plugin HTTP
routes that declare contracts.gatewayMethodDispatch: ["authenticated-request"].
For the full import map, see Plugin SDK overview.
Pre-submission checklist
package.json has correct
openclaw metadataopenclaw.plugin.json manifest is present and valid
Entry point uses
defineChannelPluginEntry or definePluginEntryAll imports use focused
plugin-sdk/<subpath> pathsInternal imports use local modules, not SDK self-imports
Tests pass (
pnpm test <bundled-plugin-root>/my-plugin/)pnpm check passes (in-repo plugins)Test against beta releases
- Watch openclaw/openclaw releases (
Watch>Releases). Beta tags look likev2026.3.N-beta.1. You can also follow @openclaw on X for release announcements. - Test your plugin against the beta tag as soon as it appears. The window before stable is typically only a few hours.
- Post in your plugin’s thread in the
plugin-forumDiscord channel (discord.gg/clawd) after testing, with eitherall goodor what broke. Create a thread if you do not have one yet. - If something breaks, open or update an issue titled
Beta blocker: <plugin-name> - <summary>and apply thebeta-blockerlabel. Link the issue in your thread. - Open a PR to
maintitledfix(<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. - Silence means green. Missing the window usually means your fix lands in the next cycle.
Next steps
Channel Plugins
Build a messaging channel plugin
Provider Plugins
Build a model provider plugin
CLI Backend Plugins
Register a local AI CLI backend
SDK Overview
Import map and registration API reference
Runtime Helpers
TTS, search, subagent via api.runtime
Testing
Test utilities and patterns
Plugin Manifest
Full manifest schema reference