2026-01-11 12:11:12 +00:00
---
summary: "Clawdbot plugins/extensions: discovery, config, and safety"
read_when:
- Adding or modifying plugins/extensions
- Documenting plugin install or load rules
---
# Plugins (Extensions)
2026-01-12 01:27:05 +00:00
## Quick start (new to plugins?)
A plugin is just a **small code module ** that extends Clawdbot with extra
features (commands, tools, and Gateway RPC).
Most of the time, you’ ll use plugins when you want a feature that’ s not built
into core Clawdbot yet (or you want to keep optional features out of your main
install).
Fast path:
1) See what’ s already loaded:
``` bash
clawdbot plugins list
```
2) Install an official plugin (example: Voice Call):
``` bash
clawdbot plugins install @clawdbot/voice-call
```
3) Restart the Gateway, then configure under `plugins.entries.<id>.config` .
See [Voice Call ](/plugins/voice-call ) for a concrete example plugin.
2026-01-15 09:07:14 +00:00
## Available plugins (official)
2026-01-16 02:58:08 +00:00
- Microsoft Teams is plugin-only as of 2026.1.15; install `@clawdbot/msteams` if you use Teams.
2026-01-15 09:07:14 +00:00
- [Voice Call ](/plugins/voice-call ) — `@clawdbot/voice-call`
2026-01-16 13:28:18 -08:00
- [Zalo Personal ](/plugins/zalouser ) — `@clawdbot/zalouser`
2026-01-15 09:07:14 +00:00
- [Matrix ](/channels/matrix ) — `@clawdbot/matrix`
- [Zalo ](/channels/zalo ) — `@clawdbot/zalo`
2026-01-16 02:58:08 +00:00
- [Microsoft Teams ](/channels/msteams ) — `@clawdbot/msteams`
2026-01-15 09:07:14 +00:00
2026-01-11 12:11:12 +00:00
Clawdbot plugins are **TypeScript modules ** loaded at runtime via jiti. They can
register:
- Gateway RPC methods
2026-01-15 05:03:50 +00:00
- Gateway HTTP handlers
2026-01-11 12:11:12 +00:00
- Agent tools
- CLI commands
- Background services
- Optional config validation
Plugins run **in‑ process ** with the Gateway, so treat them as trusted code.
## Discovery & precedence
Clawdbot scans, in order:
1) Global extensions
- `~/.clawdbot/extensions/*.ts`
- `~/.clawdbot/extensions/*/index.ts`
2) Workspace extensions
- `<workspace>/.clawdbot/extensions/*.ts`
- `<workspace>/.clawdbot/extensions/*/index.ts`
3) Config paths
- `plugins.load.paths` (file or directory)
### Package packs
A plugin directory may include a `package.json` with `clawdbot.extensions` :
``` json
{
"name" : "my-pack" ,
"clawdbot" : {
"extensions" : [ "./src/safety.ts" , "./src/tools.ts" ]
}
}
```
Each entry becomes a plugin. If the pack lists multiple extensions, the plugin id
becomes `name/<fileBase>` .
If your plugin imports npm deps, install them in that directory so
`node_modules` is available (`npm install` / `pnpm install` ).
## Plugin IDs
Default plugin ids:
- Package packs: `package.json` `name`
- Standalone file: file base name (`~/.../voice-call.ts` → `voice-call` )
If a plugin exports `id` , Clawdbot uses it but warns when it doesn’ t match the
configured id.
## Config
``` json5
{
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 toggle (default: true)
- `allow` : allowlist (optional)
- `deny` : denylist (optional; deny wins)
- `load.paths` : extra plugin files/dirs
- `entries.<id>` : per‑ plugin toggles + config
Config changes **require a gateway restart ** .
2026-01-12 01:16:46 +00:00
## Control UI (schema + labels)
The Control UI uses `config.schema` (JSON Schema + `uiHints` ) to render better forms.
Clawdbot augments `uiHints` at runtime based on discovered plugins:
- Adds per-plugin labels for `plugins.entries.<id>` / `.enabled` / `.config`
- Merges optional plugin-provided config field hints under:
`plugins.entries.<id>.config.<field>`
If you want your plugin config fields to show good labels/placeholders (and mark secrets as sensitive),
provide `configSchema.uiHints` .
Example:
``` ts
export default {
id : "my-plugin" ,
configSchema : {
parse : ( v ) = > v ,
uiHints : {
"apiKey" : { label : "API Key" , sensitive : true } ,
"region" : { label : "Region" , placeholder : "us-east-1" } ,
} ,
} ,
register ( api ) { } ,
} ;
```
2026-01-11 12:11:12 +00:00
## CLI
``` bash
clawdbot plugins list
clawdbot plugins info <id>
2026-01-12 01:16:46 +00:00
clawdbot plugins install <path> # add a local file/dir to plugins.load.paths
2026-01-12 22:32:33 +00:00
clawdbot plugins install ./extensions/voice-call # relative path ok
2026-01-12 01:16:46 +00:00
clawdbot plugins install ./plugin.tgz # install from a local tarball
clawdbot plugins install @clawdbot/voice-call # install from npm
2026-01-16 05:54:47 +00:00
clawdbot plugins update <id>
clawdbot plugins update --all
2026-01-11 12:11:12 +00:00
clawdbot plugins enable <id>
clawdbot plugins disable <id>
clawdbot plugins doctor
```
2026-01-16 05:54:47 +00:00
`plugins update` only works for npm installs tracked under `plugins.installs` .
2026-01-11 12:11:12 +00:00
Plugins may also register their own top‑ level commands (example: `clawdbot voicecall` ).
## Plugin API (overview)
Plugins export either:
- A function: `(api) => { ... }`
- An object: `{ id, name, configSchema, register(api) { ... } }`
2026-01-16 00:39:29 +00:00
## Provider plugins (model auth)
Plugins can register **model provider auth ** flows so users can run OAuth or
API-key setup inside Clawdbot (no external scripts needed).
Register a provider via `api.registerProvider(...)` . Each provider exposes one
or more auth methods (OAuth, API key, device code, etc.). These methods power:
- `clawdbot models auth login --provider <id> [--method <id>]`
Example:
``` ts
api . 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:
- `run` receives a `ProviderAuthContext` with `prompter` , `runtime` ,
`openUrl` , and `oauth.createVpsAwareHandlers` helpers.
- Return `configPatch` when you need to add default models or provider config.
- Return `defaultModel` so `--set-default` can update agent defaults.
2026-01-15 02:42:41 +00:00
### Register a messaging channel
Plugins can register **channel plugins ** that behave like built‑ in channels
(WhatsApp, Telegram, etc.). Channel config lives under `channels.<id>` and is
validated by your channel plugin code.
``` ts
const 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>` (not `plugins.entries` ).
- `meta.label` is used for labels in CLI/UI lists.
- `meta.aliases` adds alternate ids for normalization and CLI inputs.
2026-01-15 02:50:03 +00:00
### Write a new messaging channel (step‑ by‑ step)
Use this when you want a **new chat surface ** (a “messaging channel”), not a model provider.
Model provider docs live under `/providers/*` .
1) Pick an id + config shape
- All channel config lives under `channels.<id>` .
- Prefer `channels.<id>.accounts.<accountId>` for multi‑ account setups.
2) Define the channel metadata
- `meta.label` , `meta.selectionLabel` , `meta.docsPath` , `meta.blurb` control CLI/UI lists.
- `meta.docsPath` should point at a docs page like `/channels/<id>` .
3) Implement the required adapters
- `config.listAccountIds` + `config.resolveAccount`
- `capabilities` (chat types, media, threads, etc.)
- `outbound.deliveryMode` + `outbound.sendText` (for basic send)
4) Add optional adapters as needed
- `setup` (wizard), `security` (DM policy), `status` (health/diagnostics)
- `gateway` (start/stop/login), `mentions` , `threading` , `streaming`
- `actions` (message actions), `commands` (native command behavior)
5) Register the channel in your plugin
- `api.registerChannel({ plugin })`
Minimal config example:
``` json5
{
channels: {
acmechat: {
accounts: {
default: { token: "ACME_TOKEN", enabled: true }
}
}
}
}
```
Minimal channel plugin (outbound‑ only):
``` ts
const 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 here
return { ok : true } ;
} ,
} ,
} ;
export default function ( api ) {
api . registerChannel ( { plugin } ) ;
}
```
Load the plugin (extensions dir or `plugins.load.paths` ), restart the gateway,
then configure `channels.<id>` in your config.
2026-01-11 12:11:12 +00:00
### Register a tool
``` ts
import { Type } from "@sinclair/typebox" ;
export default function ( api ) {
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 } ] } ;
} ,
} ) ;
}
```
### Register a gateway RPC method
``` ts
export default function ( api ) {
api . registerGatewayMethod ( "myplugin.status" , ( { respond } ) = > {
respond ( true , { ok : true } ) ;
} ) ;
}
```
### Register CLI commands
``` ts
export default function ( api ) {
api . registerCli ( ( { program } ) = > {
program . command ( "mycmd" ) . action ( ( ) = > {
console . log ( "Hello" ) ;
} ) ;
} , { commands : [ "mycmd" ] } ) ;
}
```
### Register background services
``` ts
export default function ( api ) {
api . registerService ( {
id : "my-service" ,
start : ( ) = > api . logger . info ( "ready" ) ,
stop : ( ) = > api . logger . info ( "bye" ) ,
} ) ;
}
```
## Naming conventions
- Gateway methods: `pluginId.action` (example: `voicecall.status` )
- Tools: `snake_case` (example: `voice_call` )
- CLI commands: kebab or camel, but avoid clashing with core commands
## Skills
Plugins can ship a skill in the repo (`skills/<name>/SKILL.md` ).
Enable it with `plugins.entries.<id>.enabled` (or other config gates) and ensure
it’ s present in your workspace/managed skills locations.
2026-01-12 01:16:46 +00:00
## Distribution (npm)
Recommended packaging:
- Main package: `clawdbot` (this repo)
- Plugins: separate npm packages under `@clawdbot/*` (example: `@clawdbot/voice-call` )
Publishing contract:
- Plugin `package.json` must include `clawdbot.extensions` with one or more entry files.
- Entry files can be `.js` or `.ts` (jiti loads TS at runtime).
- `clawdbot plugins install <npm-spec>` uses `npm pack` , extracts into `~/.clawdbot/extensions/<id>/` , and enables it in config.
- Config key stability: scoped packages are normalized to the **unscoped ** id for `plugins.entries.*` .
2026-01-11 12:11:12 +00:00
## Example plugin: Voice Call
2026-01-11 23:23:14 +00:00
This repo includes a voice‑ call plugin (Twilio or log fallback):
2026-01-11 12:11:12 +00:00
- Source: `extensions/voice-call`
- Skill: `skills/voice-call`
2026-01-11 23:23:14 +00:00
- CLI: `clawdbot voicecall start|status`
2026-01-11 12:11:12 +00:00
- Tool: `voice_call`
2026-01-11 23:23:14 +00:00
- RPC: `voicecall.start` , `voicecall.status`
- Config (twilio): `provider: "twilio"` + `twilio.accountSid/authToken/from` (optional `statusCallbackUrl` , `twimlUrl` )
- Config (dev): `provider: "log"` (no network)
2026-01-11 12:11:12 +00:00
2026-01-12 01:16:46 +00:00
See [Voice Call ](/plugins/voice-call ) and `extensions/voice-call/README.md` for setup and usage.
2026-01-11 12:11:12 +00:00
## Safety notes
Plugins run in-process with the Gateway. Treat them as trusted code:
- Only install plugins you trust.
- Prefer `plugins.allow` allowlists.
- Restart the Gateway after changes.
2026-01-12 01:16:46 +00:00
## Testing plugins
Plugins can (and should) ship tests:
- In-repo plugins can keep Vitest tests under `src/**` (example: `src/plugins/voice-call.plugin.test.ts` ).
- Separately published plugins should run their own CI (lint/build/test) and validate `clawdbot.extensions` points at the built entrypoint (`dist/index.js` ).