OpenClaw now tries ClawHub before npm for bare plugin specs. Update install examples and guidance across: - building-plugins.md: intro and publish step - sdk-setup.md: publishing section with clawhub:/npm: prefix examples - tools/plugin.md: CLI reference table - community.md: submission guidance and quality bar
9.2 KiB
title, sidebarTitle, summary, read_when
| title | sidebarTitle | summary | read_when | |||
|---|---|---|---|---|---|---|
| Plugin SDK Setup | Setup and Config | Setup wizards, setup-entry.ts, config schemas, and package.json metadata |
|
Plugin Setup and Config
Reference for plugin packaging (package.json metadata), manifests
(openclaw.plugin.json), setup entries, and config schemas.
Package metadata
Your package.json needs an openclaw field that tells the plugin system what
your plugin provides:
Channel plugin:
{
"name": "@myorg/openclaw-my-channel",
"version": "1.0.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"channel": {
"id": "my-channel",
"label": "My Channel",
"blurb": "Short description of the channel."
}
}
}
Provider plugin:
{
"name": "@myorg/openclaw-my-provider",
"version": "1.0.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"],
"providers": ["my-provider"]
}
}
openclaw fields
| Field | Type | Description |
|---|---|---|
extensions |
string[] |
Entry point files (relative to package root) |
setupEntry |
string |
Lightweight setup-only entry (optional) |
channel |
object |
Channel metadata: id, label, blurb, selectionLabel, docsPath, order, aliases |
providers |
string[] |
Provider ids registered by this plugin |
install |
object |
Install hints: npmSpec, localPath, defaultChoice |
startup |
object |
Startup behavior flags |
Deferred full load
Channel plugins can opt into deferred loading with:
{
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"startup": {
"deferConfiguredChannelFullLoadUntilAfterListen": true
}
}
}
When enabled, OpenClaw loads only setupEntry during the pre-listen startup
phase, even for already-configured channels. The full entry loads after the
gateway starts listening.
Plugin manifest
Every native plugin must ship an openclaw.plugin.json in the package root.
OpenClaw uses this to validate config without executing plugin code.
{
"id": "my-plugin",
"name": "My Plugin",
"description": "Adds My Plugin capabilities to OpenClaw",
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"webhookSecret": {
"type": "string",
"description": "Webhook verification secret"
}
}
}
}
For channel plugins, add kind and channels:
{
"id": "my-channel",
"kind": "channel",
"channels": ["my-channel"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
Even plugins with no config must ship a schema. An empty schema is valid:
{
"id": "my-plugin",
"configSchema": {
"type": "object",
"additionalProperties": false
}
}
See Plugin Manifest for the full schema reference.
Setup entry
The setup-entry.ts file is a lightweight alternative to index.ts that
OpenClaw loads when it only needs setup surfaces (onboarding, config repair,
disabled channel inspection).
// setup-entry.ts
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { myChannelPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(myChannelPlugin);
This avoids loading heavy runtime code (crypto libraries, CLI registrations, background services) during setup flows.
When OpenClaw uses setupEntry instead of the full entry:
- The channel is disabled but needs setup/onboarding surfaces
- The channel is enabled but unconfigured
- Deferred loading is enabled (
deferConfiguredChannelFullLoadUntilAfterListen)
What setupEntry must register:
- The channel plugin object (via
defineSetupPluginEntry) - Any HTTP routes required before gateway listen
- Any gateway methods needed during startup
What setupEntry should NOT include:
- CLI registrations
- Background services
- Heavy runtime imports (crypto, SDKs)
- Gateway methods only needed after startup
Config schema
Plugin config is validated against the JSON Schema in your manifest. Users configure plugins via:
{
plugins: {
entries: {
"my-plugin": {
config: {
webhookSecret: "abc123",
},
},
},
},
}
Your plugin receives this config as api.pluginConfig during registration.
For channel-specific config, use the channel config section instead:
{
channels: {
"my-channel": {
token: "bot-token",
allowFrom: ["user1", "user2"],
},
},
}
Building channel config schemas
Use buildChannelConfigSchema from openclaw/plugin-sdk/core to convert a
Zod schema into the ChannelConfigSchema wrapper that OpenClaw validates:
import { z } from "zod";
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/core";
const accountSchema = z.object({
token: z.string().optional(),
allowFrom: z.array(z.string()).optional(),
accounts: z.object({}).catchall(z.any()).optional(),
defaultAccount: z.string().optional(),
});
const configSchema = buildChannelConfigSchema(accountSchema);
Setup wizards
Channel plugins can provide interactive setup wizards for openclaw onboard.
The wizard is a ChannelSetupWizard object on the ChannelPlugin:
import type { ChannelSetupWizard } from "openclaw/plugin-sdk/channel-setup";
const setupWizard: ChannelSetupWizard = {
channel: "my-channel",
status: {
configuredLabel: "Connected",
unconfiguredLabel: "Not configured",
resolveConfigured: ({ cfg }) => Boolean((cfg.channels as any)?.["my-channel"]?.token),
},
credentials: [
{
inputKey: "token",
providerHint: "my-channel",
credentialLabel: "Bot token",
preferredEnvVar: "MY_CHANNEL_BOT_TOKEN",
envPrompt: "Use MY_CHANNEL_BOT_TOKEN from environment?",
keepPrompt: "Keep current token?",
inputPrompt: "Enter your bot token:",
inspect: ({ cfg, accountId }) => {
const token = (cfg.channels as any)?.["my-channel"]?.token;
return {
accountConfigured: Boolean(token),
hasConfiguredValue: Boolean(token),
};
},
},
],
};
The ChannelSetupWizard type supports credentials, textInputs,
dmPolicy, allowFrom, groupAccess, prepare, finalize, and more.
See bundled plugins (e.g. extensions/discord/src/channel.setup.ts) for
full examples.
For optional setup surfaces that should only appear in certain contexts, use
createOptionalChannelSetupSurface from openclaw/plugin-sdk/channel-setup:
import { createOptionalChannelSetupSurface } from "openclaw/plugin-sdk/channel-setup";
const setupSurface = createOptionalChannelSetupSurface({
channel: "my-channel",
label: "My Channel",
npmSpec: "@myorg/openclaw-my-channel",
docsPath: "/channels/my-channel",
});
// Returns { setupAdapter, setupWizard }
Publishing and installing
External plugins: publish to ClawHub or npm, then install:
openclaw plugins install @myorg/openclaw-my-plugin
OpenClaw tries ClawHub first and falls back to npm automatically. You can also force a specific source:
openclaw plugins install clawhub:@myorg/openclaw-my-plugin # ClawHub only
openclaw plugins install npm:@myorg/openclaw-my-plugin # npm only
In-repo plugins: place under extensions/ and they are automatically
discovered during build.
Users can browse and install:
openclaw plugins search <query>
openclaw plugins install <package-name>
Related
- SDK Entry Points --
definePluginEntryanddefineChannelPluginEntry - Plugin Manifest -- full manifest schema reference
- Building Plugins -- step-by-step getting started guide