defineToolPlugin, definePluginEntry,
defineChannelPluginEntry, defineSetupPluginEntry.
Package entries
Installed plugins pointpackage.json openclaw fields at both source and
built entries:
extensionsandsetupEntryare source entries, used for workspace and git checkout development.runtimeExtensionsandruntimeSetupEntryare preferred for installed packages: they let npm packages skip runtime TypeScript compilation.runtimeExtensions, when present, must matchextensionsin array length (entries pair positionally).runtimeSetupEntryrequiressetupEntry.- If a
runtimeExtensions/runtimeSetupEntryartifact is declared but missing, install/discovery fails with a packaging error; OpenClaw does not silently fall back to source. Source fallback (below) only applies when no runtime entry is declared at all. - If an installed package declares only a TypeScript source entry, OpenClaw
looks for a matching built
dist/*.js(or.mjs/.cjs) peer and uses it; otherwise it falls back to the TypeScript source. - All entry paths must stay inside the plugin package directory. Runtime
entries and inferred built-JS peers do not make an escaping
extensionsorsetupEntrysource path valid.
defineToolPlugin
Import: openclaw/plugin-sdk/tool-plugin
For plugins that only add agent tools. Keeps the source small, infers config
and tool-parameter types from TypeBox schemas, wraps plain return values in
the OpenClaw tool-result format, and exposes static metadata that
openclaw plugins build writes into the plugin manifest (contracts.tools,
configSchema).
configSchemais optional; omitting it uses a strict empty object schema (the generated manifest still includesconfigSchema).executereturns a plain string or JSON-serializable value; the helper wraps it as a text tool result withdetailsset to the original (unstringified) return value.- For custom tool results,
openclaw/plugin-sdk/tool-resultsexportstextResultandjsonResult. - Tool names are static, so
openclaw plugins buildderivescontracts.toolsfrom the declared tools without hand-duplicated names. - Runtime loading stays strict: installed plugins still need
openclaw.plugin.jsonandpackage.jsonopenclaw.extensions. OpenClaw never executes plugin code to infer missing manifest data.
definePluginEntry
Import: openclaw/plugin-sdk/plugin-entry
For provider plugins, advanced tool plugins, hook plugins, and anything that
is not a messaging channel.
| Field | Type | Required | Default |
|---|---|---|---|
id | string | Yes | - |
name | string | Yes | - |
description | string | Yes | - |
kind | string (deprecated, see below) | No | - |
configSchema | OpenClawPluginConfigSchema | () => OpenClawPluginConfigSchema | No | Empty object schema |
reload | OpenClawPluginReloadRegistration | No | - |
nodeHostCommands | OpenClawPluginNodeHostCommand[] | No | - |
securityAuditCollectors | OpenClawPluginSecurityAuditCollector[] | No | - |
register | (api: OpenClawPluginApi) => void | Yes | - |
idmust match youropenclaw.plugin.jsonmanifest.kindis deprecated: declare an exclusive slot ("memory"or"context-engine") in theopenclaw.plugin.jsonmanifestkindfield instead. Runtime-entrykindremains only as a compatibility fallback for older plugins.configSchemacan be a function for lazy evaluation. OpenClaw resolves and memoizes the schema on first access, so expensive schema builders only run once.
defineChannelPluginEntry
Import: openclaw/plugin-sdk/channel-core
Wraps definePluginEntry with channel-specific wiring: it automatically
calls api.registerChannel({ plugin }), exposes an optional root-help CLI
metadata seam, and gates registerFull on registration mode.
| Field | Type | Required | Default |
|---|---|---|---|
id | string | Yes | - |
name | string | Yes | - |
description | string | Yes | - |
plugin | ChannelPlugin | Yes | - |
configSchema | OpenClawPluginConfigSchema | () => OpenClawPluginConfigSchema | No | Empty object schema |
setRuntime | (runtime: PluginRuntime) => void | No | - |
registerCliMetadata | (api: OpenClawPluginApi) => void | No | - |
registerFull | (api: OpenClawPluginApi) => void | No | - |
setRuntimeruns in every mode except"cli-metadata"and"tool-discovery". Store the runtime reference here, typically viacreatePluginRuntimeStore.registerCliMetadataruns for"cli-metadata","discovery", and"full". Use it as the canonical place for channel-owned CLI descriptors so root help stays non-activating, discovery snapshots include static command metadata, and normal CLI registration stays compatible with full plugin loads.registerFullruns only for"full"and"tool-discovery". For"tool-discovery"it runs instead of channel registration: OpenClaw skipsregisterChannel/setRuntimeentirely and calls onlyregisterFull, so any provider/tool registration your channel needs for standalone tool discovery or execution must live there, not behind normal channel setup.- Discovery registration is non-activating, not import-free: OpenClaw may
evaluate the trusted plugin entry and channel plugin module to build the
snapshot. Keep top-level imports side-effect-free and put sockets,
clients, workers, and services behind
"full"-only paths. - Like
definePluginEntry,configSchemacan be a lazy factory; OpenClaw memoizes the resolved schema on first access.
- Use
api.registerCli(..., { descriptors: [...] })for plugin-owned root CLI commands you want lazy-loaded without disappearing from the root CLI parse tree. Descriptor names must match letters, numbers, hyphen, and underscore, starting with a letter or number; OpenClaw rejects other shapes and strips terminal control sequences from descriptions before rendering help. Cover every top-level command root the registrar exposes.commandsalone stays on the eager compatibility path. - Use
api.registerNodeCliFeature(...)for paired-node feature commands so they land underopenclaw nodes(equivalent toregisterCli(registrar, { parentPath: ["nodes"], ... })). - For other nested plugin commands, add
parentPathand register commands on theprogramobject passed to the registrar; OpenClaw resolves it to the parent command before calling the plugin. - For channel plugins, register CLI descriptors from
registerCliMetadataand keepregisterFullfocused on runtime-only work. - If
registerFullalso registers gateway RPC methods, keep them on a plugin-specific prefix. Reserved core admin namespaces (config.*,exec.approvals.*,wizard.*,update.*) always coerce tooperator.admin.
defineSetupPluginEntry
Import: openclaw/plugin-sdk/channel-core
For the lightweight setup-entry.ts file. Returns just { plugin } with no
runtime or CLI wiring.
defineSetupPluginEntry(...) with the narrow setup helper families:
| Import | Use for |
|---|---|
openclaw/plugin-sdk/setup-runtime | Runtime-safe setup helpers: createSetupTranslator, import-safe setup patch adapters, lookup-note output, promptResolvedAllowFrom, splitSetupEntries, delegated setup proxies |
openclaw/plugin-sdk/channel-setup | Optional-install setup surfaces |
openclaw/plugin-sdk/setup-tools | Setup/install CLI, archive, and docs helpers |
defineBundledChannelSetupEntry(...) from
openclaw/plugin-sdk/channel-entry-contract instead. It lets the setup
entry keep setup-safe plugin/secrets exports while still exposing a runtime
setter:
registerSetupRuntime runs only for "setup-runtime" loads; keep it
limited to config-only routes or methods that must exist before deferred
full activation.
Registration mode
api.registrationMode tells your plugin how it was loaded:
| Mode | When | What to register |
|---|---|---|
"full" | Normal gateway startup | Everything |
"discovery" | Read-only capability discovery | Channel registration plus static CLI descriptors; entry code may load, but skip sockets, workers, clients, and services |
"tool-discovery" | Scoped load to list or run specific plugins’ tools | Capability/tool registration only; no channel activation |
"setup-only" | Disabled/unconfigured channel | Channel registration only |
"setup-runtime" | Setup flow with runtime available | Channel registration plus only the lightweight runtime needed before the full entry loads |
"cli-metadata" | Root help / CLI metadata capture | CLI descriptors only |
defineChannelPluginEntry handles this split automatically. If you use
definePluginEntry directly for a channel, check mode yourself and remember
"tool-discovery" skips channel registration:
"setup-runtime" as the window where setup-only startup surfaces must
exist without re-entering the full bundled channel runtime. Good fits are
channel registration, setup-safe HTTP routes, setup-safe gateway methods,
and delegated setup helpers. Heavy background services, CLI registrars, and
provider/client SDK bootstraps still belong in "full".
Plugin shapes
OpenClaw classifies loaded plugins by their registration behavior:| Shape | Description |
|---|---|
| plain-capability | One capability type (e.g. provider-only) |
| hybrid-capability | Multiple capability types (e.g. provider + speech) |
| hook-only | Only hooks, no capabilities |
| non-capability | Tools/commands/services but no capabilities |
openclaw plugins inspect <id> to see a plugin’s shape.
Related
- SDK Overview - registration API and subpath reference
- Runtime Helpers -
api.runtimeandcreatePluginRuntimeStore - Setup and Config - manifest, setup entry, deferred loading
- Channel Plugins - building the
ChannelPluginobject - Provider Plugins - provider registration and hooks