Welcome to RDK X5 MagicBox repository
8. OpenClaw Plugin Feature Introduction
Plugins
Quick Start
A plugin is just a small code module that extends OpenClaw with additional functionality (commands, tools, and Gateway RPC). Most of the time, you'll use a plugin when you want a feature that isn't built into core OpenClaw (or you want to exclude optional features from the main installation). Quick path:
- See what's loaded:
openclaw plugins list
- Install an official plugin (e.g., Voice Call):
xxxxxxxxxxopenclaw plugins install @openclaw/voice-call
- Restart the Gateway, then configure under
plugins.entries.<id>.config.
Available Plugins (Official)
- As of 2026.1.15, Microsoft Teams is only available as a plugin; if using Teams, install
@openclaw/msteams. - Memory (Core) — Bundled memory search plugin (enabled by default via
plugins.slots.memory) - Memory (LanceDB) — Bundled long-term memory plugin (auto-recall/capture; set
plugins.slots.memory = "memory-lancedb") - Voice Call —
@openclaw/voice-call - Zalo Personal —
@openclaw/zalouser - Matrix —
@openclaw/matrix - Nostr —
@openclaw/nostr - Zalo —
@openclaw/zalo - Microsoft Teams —
@openclaw/msteams - Google Antigravity OAuth (provider auth) — Bundled as
google-antigravity-auth(disabled by default) - Gemini CLI OAuth (provider auth) — Bundled as
google-gemini-cli-auth(disabled by default) - Qwen OAuth (provider auth) — Bundled as
qwen-portal-auth(disabled by default) - Copilot Proxy (provider auth) — Local VS Code Copilot Proxy bridge; different from built-in
github-copilotdevice login (bundled, disabled by default)
OpenClaw plugins are TypeScript modules loaded at runtime via jiti. Config validation does not execute plugin code; it uses the plugin manifest and JSON Schema. See Plugin Manifest. Plugins can register:
- Gateway RPC methods
- Gateway HTTP handlers
- Agent tools
- CLI commands
- Background services
- Optional config validation
- Skills (by listing
skillsdirectory in the plugin manifest) - Auto-reply commands (execute without invoking an AI agent)
Plugins run in the same process as the Gateway, so treat them as trusted code. Tool writing guide: Plugin Agent Tools.
Runtime Helpers
Plugins can access selected core helpers via api.runtime. For phone TTS:
xxxxxxxxxxconst result = await api.runtime.tts.textToSpeechTelephony({text: "Hello from OpenClaw",cfg: api.config,});
Notes:
- Uses core
messages.ttsconfig (OpenAI or ElevenLabs). - Returns PCM audio buffer + sample rate. Plugins must resample/encode for their provider.
- Edge TTS does not support phone.
Discovery and Priority
OpenClaw scans in order:
- Config path
plugins.load.paths(file or directory)
- Workspace extensions
<workspace>/.openclaw/extensions/*.ts<workspace>/.openclaw/extensions/*/index.ts
- Global extensions
~/.openclaw/extensions/*.ts~/.openclaw/extensions/*/index.ts
- Bundled extensions (shipped with OpenClaw, disabled by default)
<openclaw>/extensions/*
Bundled plugins must be explicitly enabled via plugins.entries.<id>.enabled or openclaw plugins enable <id>. Installed plugins are enabled by default but can be disabled the same way. Each plugin must contain an openclaw.plugin.json file in its root directory. If the path points to a file, the plugin root is that file's directory, which must contain the manifest. If multiple plugins resolve to the same id, the first match in the order above wins, and lower-priority copies are ignored.
Package Collections
A plugin directory can contain a package.json with openclaw.extensions:
xxxxxxxxxx{"name": "my-pack","openclaw": {"extensions": ["./src/safety.ts", "./src/tools.ts"]}}
Each entry becomes a plugin. If the package lists multiple extensions, the plugin id becomes name/<fileBase>. If your plugin imports npm dependencies, install them in that directory so node_modules is available (npm install / pnpm install).
Channel Directory Metadata
Channel plugins can broadcast onboarding metadata via openclaw.channel and install hints via openclaw.install. This keeps the core directory data-free. Example:
xxxxxxxxxx{"name": "@openclaw/nextcloud-talk","openclaw": {"extensions": ["./index.ts"],"channel": {"id": "nextcloud-talk","label": "Nextcloud Talk","selectionLabel": "Nextcloud Talk (self-hosted)","docsPath": "/channels/nextcloud-talk","docsLabel": "nextcloud-talk","blurb": "Self-hosted chat via Nextcloud Talk webhook bots.","order": 65,"aliases": ["nc-talk", "nc"]},"install": {"npmSpec": "@openclaw/nextcloud-talk","localPath": "extensions/nextcloud-talk","defaultChoice": "npm"}}}
OpenClaw can also merge external channel directories (e.g., MPM registry exports). Place JSON files in one of:
~/.openclaw/mpm/plugins.json~/.openclaw/mpm/catalog.json~/.openclaw/plugins/catalog.json
Or point OPENCLAW_PLUGIN_CATALOG_PATHS (or OPENCLAW_MPM_CATALOG_PATHS) to one or more JSON files (comma/semicolon/PATH separated). Each file should contain { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }.
Plugin ID
Default plugin ids:
- Package collections:
package.jsonname - Standalone files: file base name (
~/.../voice-call.ts→voice-call)
If a plugin exports id, OpenClaw uses it but warns when it doesn't match the configured id.
Configuration
xxxxxxxxxx{plugins: {enabled: true,allow: ["voice-call"],deny: ["untrusted-plugin"],load: { paths: ["~/Projects/oss/voice-call-extension"] },entries: {"voice-call": { enabled: true, config: { provider: "twilio" } },},},}
Fields:
enabled: Master switch (default: true)allow: Allow list (optional)deny: Deny list (optional; deny takes precedence)load.paths: Additional plugin files/directoriesentries.<id>: Per-plugin switch + config
Config changes require a Gateway restart. Validation rules (strict):
- Unknown plugin ids in
entries,allow,deny, orslotsare errors. - Unknown
channels.<id>keys are errors unless the plugin manifest declares the channel id. - Plugin config is validated using JSON Schema embedded in
openclaw.plugin.json(configSchema). - If a plugin is disabled, its config is preserved and a warning is issued.
Plugin Slots (Exclusive Categories)
Some plugin categories are exclusive (only one active at a time). Use plugins.slots to select which plugin owns a slot:
xxxxxxxxxx{plugins: {slots: {memory: "memory-core", // or "none" to disable memory plugins},},}
If multiple plugins declare kind: "memory", only the selected one loads. Others are disabled with diagnostics.
Control UI (Schema + Labels)
The control UI uses config.schema (JSON Schema + uiHints) to render better forms. OpenClaw augments uiHints at runtime based on discovered plugins:
- Add per-plugin labels for
plugins.entries.<id>/.enabled/.config - Merge optional plugin-provided config field hints at:
plugins.entries.<id>.config.<field>
If you want plugin config fields to show good labels/placeholders (and mark keys as sensitive), provide uiHints and JSON Schema in the plugin manifest. Example:
xxxxxxxxxx{"id": "my-plugin","configSchema": {"type": "object","additionalProperties": false,"properties": {"apiKey": { "type": "string" },"region": { "type": "string" }}},"uiHints": {"apiKey": { "label": "API Key", "sensitive": true },"region": { "label": "Region", "placeholder": "us-east-1" }}}
CLI
xxxxxxxxxxopenclaw plugins listopenclaw plugins info <id>openclaw plugins install <path> # copy a local file/dir into ~/.openclaw/extensions/<id>openclaw plugins install ./extensions/voice-call # relative path okopenclaw plugins install ./plugin.tgz # install from a local tarballopenclaw plugins install ./plugin.zip # install from a local zipopenclaw plugins install -l ./extensions/voice-call # link (no copy) for devopenclaw plugins install @openclaw/voice-call # install from npmopenclaw plugins update <id>openclaw plugins update --allopenclaw plugins enable <id>openclaw plugins disable <id>openclaw plugins doctor
plugins update only works for npm installations tracked under plugins.installs. Plugins can also register their own top-level commands (e.g., openclaw voicecall).
Plugin API (Overview)
Plugins export one of:
- Function:
(api) => { ... } - Object:
{ id, name, configSchema, register(api) { ... } }
Plugin Hooks
Plugins can bundle hooks and register them at runtime. This lets plugins bundle event-driven automation without a separate hook package.
Example
ximport { registerPluginHooksFromDir } from "openclaw/plugin-sdk";export default function register(api) {registerPluginHooksFromDir(api, "./hooks");}
Notes:
- Hooks directory follows normal hook structure (
HOOK.md+handler.ts). - Hook eligibility rules still apply (OS/binary/env/config requirements).
- Plugin-managed hooks show as
plugin:<id>inopenclaw hooks list. - You cannot enable/disable plugin-managed hooks via
openclaw hooks; enable/disable the plugin instead.
Provider Plugins (Model Auth)
Plugins can register model provider auth flows so users can run OAuth or API key setup inside OpenClaw (no external scripts). Register providers via api.registerProvider(...). Each provider exposes one or more auth methods (OAuth, API key, device code, etc.). These methods drive:
openclaw models auth login --provider <id> [--method <id>]
Example:
xxxxxxxxxxapi.registerProvider({id: "acme",label: "AcmeAI",auth: [{id: "oauth",label: "OAuth",kind: "oauth",run: async (ctx) => {// Run OAuth flow and return auth profiles.return {profiles: [{profileId: "acme:default",credential: {type: "oauth",provider: "acme",access: "...",refresh: "...",expires: Date.now() + 3600 * 1000,},},],defaultModel: "acme/opus-1",};},},],});
Notes:
runreceives aProviderAuthContextwithprompter,runtime,openUrl, andoauth.createVpsAwareHandlershelpers.- Return
configPatchwhen you need to add default models or provider config. - Return
defaultModelso--set-defaultcan update agent defaults.
Registering Messaging Channels
Plugins can register channel plugins, which behave like built-in channels (WhatsApp, Telegram, etc.). Channel config lives under channels.<id>, validated by your channel plugin code.
xxxxxxxxxxconst myChannel = {id: "acmechat",meta: {id: "acmechat",label: "AcmeChat",selectionLabel: "AcmeChat (API)",docsPath: "/channels/acmechat",blurb: "demo channel plugin.",aliases: ["acme"],},capabilities: { chatTypes: ["direct"] },config: {listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),resolveAccount: (cfg, accountId) =>cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? {accountId,},},outbound: {deliveryMode: "direct",sendText: async () => ({ ok: true }),},};export default function (api) {api.registerChannel({ plugin: myChannel });}
Notes:
- Put config under
channels.<id>(notplugins.entries). meta.labelis used for labels in CLI/UI lists.meta.aliasesadds alternate ids for normalization and CLI input.meta.preferOverlists channel ids to skip when both are configured for auto-enable.meta.detailLabelandmeta.systemImagelet the UI show richer channel labels/icons.
Writing a New Messaging Channel (Step-by-Step)
Use this when you want a new chat interface ("messaging channel") rather than a model provider. Model provider docs are under /providers/*.
- Choose id + config structure
- All channel config lives under
channels.<id>. - For multi-account setups, prefer
channels.<id>.accounts.<accountId>.
- Define channel metadata
meta.label,meta.selectionLabel,meta.docsPath,meta.blurbcontrol CLI/UI lists.meta.docsPathshould point to a docs page like/channels/<id>.meta.preferOverlets the plugin replace another channel (auto-enable prefers it).meta.detailLabelandmeta.systemImageare used by the UI for detail text/icons.
- Implement required adapters
config.listAccountIds+config.resolveAccountcapabilities(chat types, media, threads, etc.)outbound.deliveryMode+outbound.sendText(for basic send)
- Add optional adapters as needed
setup(wizard),security(DM policy),status(health/diagnostics)gateway(start/stop/login),mentions,threading,streamingactions(message operations),commands(native command behavior)
- Register the channel in the plugin
api.registerChannel({ plugin })
Minimal config example:
xxxxxxxxxx{channels: {acmechat: {accounts: {default: { token: "ACME_TOKEN", enabled: true },},},},}
Minimal channel plugin (outbound only):
xxxxxxxxxxconst plugin = {id: "acmechat",meta: {id: "acmechat",label: "AcmeChat",selectionLabel: "AcmeChat (API)",docsPath: "/channels/acmechat",blurb: "AcmeChat messaging channel.",aliases: ["acme"],},capabilities: { chatTypes: ["direct"] },config: {listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),resolveAccount: (cfg, accountId) =>cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? {accountId,},},outbound: {deliveryMode: "direct",sendText: async ({ text }) => {// deliver `text` to your channel herereturn { ok: true };},},};export default function (api) {api.registerChannel({ plugin });}
Load the plugin (extension directory or plugins.load.paths), restart the Gateway, then configure channels.<id> in your config.
Agent Tools
See the dedicated guide: Plugin Agent Tools.
Register Gateway RPC Methods
xxxxxxxxxxexport default function (api) {api.registerGatewayMethod("myplugin.status", ({ respond }) => {respond(true, { ok: true });});}
Register CLI Commands
xxxxxxxxxxexport default function (api) {api.registerCli(({ program }) => {program.command("mycmd").action(() => {console.log("Hello");});},{ commands: ["mycmd"] },);}
Register Auto-Reply Commands
Plugins can register custom slash commands that execute without invoking an AI agent. This is useful for toggle commands, status checks, or quick operations that don't need LLM processing.
xxxxxxxxxxexport default function (api) {api.registerCommand({name: "mystatus",description: "Show plugin status",handler: (ctx) => ({text: `Plugin is running! Channel: ${ctx.channel}`,}),});}
Command handler context:
senderId: Sender's ID (if available)channel: Channel the command was sent onisAuthorizedSender: Whether the sender is an authorized userargs: Arguments passed after the command (ifacceptsArgs: true)commandBody: Full command textconfig: Current OpenClaw config
Command options:
name: Command name (without leading/)description: Help text shown in command listacceptsArgs: Whether the command accepts arguments (default: false). If false and arguments are provided, the command won't match and the message goes to other handlersrequireAuth: Whether authorized sender is required (default: true)handler: Function that returns{ text: string }(can be async)
Example with auth and args:
xxxxxxxxxxapi.registerCommand({name: "setmode",description: "Set plugin mode",acceptsArgs: true,requireAuth: true,handler: async (ctx) => {const mode = ctx.args?.trim() || "default";await saveMode(mode);return { text: `Mode set to: ${mode}` };},});
Notes:
- Plugin commands are processed before built-in commands and AI agents
- Commands are registered globally, applying to all channels
- Command names are case-insensitive (
/MyStatusmatches/mystatus) - Command names must start with a letter and can only contain letters, numbers, hyphens, and underscores
- Reserved command names (like
help,status,reset, etc.) cannot be overridden by plugins - Duplicate command registration across plugins fails with diagnostic error
Register Background Services
xxxxxxxxxxexport default function (api) {api.registerService({id: "my-service",start: () => api.logger.info("ready"),stop: () => api.logger.info("bye"),});}
Naming Conventions
- Gateway methods:
pluginId.action(e.g.,voicecall.status) - Tools:
snake_case(e.g.,voice_call) - CLI commands: kebab or camel, but avoid conflicts with core commands
Skills
Plugins can bundle Skills in the repo (skills/<name>/SKILL.md). Enable via plugins.entries.<id>.enabled (or other config gating), and ensure it exists in your workspace/hosted Skills location.
Distribution (npm)
Recommended packaging:
- Main package:
openclaw(this repo) - Plugins: Standalone npm packages under
@openclaw/*(e.g.,@openclaw/voice-call)
Publish contract:
- Plugin
package.jsonmust containopenclaw.extensionswith one or more entry files. - Entry files can be
.jsor.ts(jiti loads TS at runtime). openclaw plugins install <npm-spec>usesnpm pack, extracts to~/.openclaw/extensions/<id>/, and enables it in config.- Config key stability: Scoped packages are normalized to unscoped ids in
plugins.entries.*.
Example Plugin: Voice Call
This repo includes a voice call plugin (Twilio or log fallback):
- Source:
extensions/voice-call - Skills:
skills/voice-call - CLI:
openclaw voicecall start|status - Tools:
voice_call - RPC:
voicecall.start,voicecall.status - Config (twilio):
provider: "twilio"+twilio.accountSid/authToken/from(optionalstatusCallbackUrl,twimlUrl) - Config (dev):
provider: "log"(no network)
See Voice Call and extensions/voice-call/README.md for setup and usage.
Security Notes
Plugins run in the same process as the Gateway. Treat them as trusted code:
- Only install plugins you trust.
- Prefer allow lists via
plugins.allow. - Restart the Gateway after changes.
Testing Plugins
Plugins can (and should) bundle tests:
- In-repo plugins can keep Vitest tests under
src/**(e.g.,src/plugins/voice-call.plugin.test.ts). - Individually released plugins should run their own CI (lint/build/test) and verify
openclaw.extensionspoints to the built entry point (dist/index.js).
For more about plugins, see: Plugins - OpenClaw