Files
clawdbot/docs/plugin.md
2026-01-24 00:17:58 +00:00

17 KiB
Raw Blame History

summary, read_when
summary read_when
Clawdbot plugins/extensions: discovery, config, and safety
Adding or modifying plugins/extensions
Documenting plugin install or load rules

Plugins (Extensions)

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, youll use plugins when you want a feature thats not built into core Clawdbot yet (or you want to keep optional features out of your main install).

Fast path:

  1. See whats already loaded:
clawdbot plugins list
  1. Install an official plugin (example: Voice Call):
clawdbot plugins install @clawdbot/voice-call
  1. Restart the Gateway, then configure under plugins.entries.<id>.config.

See Voice Call for a concrete example plugin.

Available plugins (official)

  • Microsoft Teams is plugin-only as of 2026.1.15; install @clawdbot/msteams if you use Teams.
  • 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@clawdbot/voice-call
  • Zalo Personal@clawdbot/zalouser
  • Matrix@clawdbot/matrix
  • Nostr@clawdbot/nostr
  • Zalo@clawdbot/zalo
  • Microsoft Teams@clawdbot/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; distinct from built-in github-copilot device login (bundled, disabled by default)

Clawdbot plugins are TypeScript modules loaded at runtime via jiti. Config validation does not execute plugin code; it uses the plugin manifest and JSON Schema instead. See Plugin manifest.

Plugins can register:

  • Gateway RPC methods
  • Gateway HTTP handlers
  • Agent tools
  • CLI commands
  • Background services
  • Optional config validation
  • Skills (by listing skills directories in the plugin manifest)

Plugins run inprocess with the Gateway, so treat them as trusted code. Tool authoring guide: Plugin agent tools.

Discovery & precedence

Clawdbot scans, in order:

  1. Config paths
  • plugins.load.paths (file or directory)
  1. Workspace extensions
  • <workspace>/.clawdbot/extensions/*.ts
  • <workspace>/.clawdbot/extensions/*/index.ts
  1. Global extensions
  • ~/.clawdbot/extensions/*.ts
  • ~/.clawdbot/extensions/*/index.ts
  1. Bundled extensions (shipped with Clawdbot, disabled by default)
  • <clawdbot>/extensions/*

Bundled plugins must be enabled explicitly via plugins.entries.<id>.enabled or clawdbot plugins enable <id>. Installed plugins are enabled by default, but can be disabled the same way.

Each plugin must include a clawdbot.plugin.json file in its root. If a path points at a file, the plugin root is the file's directory and must contain the manifest.

If multiple plugins resolve to the same id, the first match in the order above wins and lower-precedence copies are ignored.

Package packs

A plugin directory may include a package.json with clawdbot.extensions:

{
  "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).

Channel catalog metadata

Channel plugins can advertise onboarding metadata via clawdbot.channel and install hints via clawdbot.install. This keeps the core catalog data-free.

Example:

{
  "name": "@clawdbot/nextcloud-talk",
  "clawdbot": {
    "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": "@clawdbot/nextcloud-talk",
      "localPath": "extensions/nextcloud-talk",
      "defaultChoice": "npm"
    }
  }
}

Clawdbot can also merge external channel catalogs (for example, an MPM registry export). Drop a JSON file at one of:

  • ~/.clawdbot/mpm/plugins.json
  • ~/.clawdbot/mpm/catalog.json
  • ~/.clawdbot/plugins/catalog.json

Or point CLAWDBOT_PLUGIN_CATALOG_PATHS (or CLAWDBOT_MPM_CATALOG_PATHS) at one or more JSON files (comma/semicolon/PATH-delimited). Each file should contain { "entries": [ { "name": "@scope/pkg", "clawdbot": { "channel": {...}, "install": {...} } } ] }.

Plugin IDs

Default plugin ids:

  • Package packs: package.json name
  • Standalone file: file base name (~/.../voice-call.tsvoice-call)

If a plugin exports id, Clawdbot uses it but warns when it doesnt match the configured id.

Config

{
  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>: perplugin toggles + config

Config changes require a gateway restart.

Validation rules (strict):

  • Unknown plugin ids in entries, allow, deny, or slots are errors.
  • Unknown channels.<id> keys are errors unless a plugin manifest declares the channel id.
  • Plugin config is validated using the JSON Schema embedded in clawdbot.plugin.json (configSchema).
  • If a plugin is disabled, its config is preserved and a warning is emitted.

Plugin slots (exclusive categories)

Some plugin categories are exclusive (only one active at a time). Use plugins.slots to select which plugin owns the slot:

{
  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.

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 uiHints alongside your JSON Schema in the plugin manifest.

Example:

{
  "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

clawdbot plugins list
clawdbot plugins info <id>
clawdbot plugins install <path>                 # copy a local file/dir into ~/.clawdbot/extensions/<id>
clawdbot plugins install ./extensions/voice-call # relative path ok
clawdbot plugins install ./plugin.tgz           # install from a local tarball
clawdbot plugins install ./plugin.zip           # install from a local zip
clawdbot plugins install -l ./extensions/voice-call # link (no copy) for dev
clawdbot plugins install @clawdbot/voice-call # install from npm
clawdbot plugins update <id>
clawdbot plugins update --all
clawdbot plugins enable <id>
clawdbot plugins disable <id>
clawdbot plugins doctor

plugins update only works for npm installs tracked under plugins.installs.

Plugins may also register their own toplevel commands (example: clawdbot voicecall).

Plugin API (overview)

Plugins export either:

  • A function: (api) => { ... }
  • An object: { id, name, configSchema, register(api) { ... } }

Plugin hooks

Plugins can ship hooks and register them at runtime. This lets a plugin bundle event-driven automation without a separate hook pack install.

Example

import { registerPluginHooksFromDir } from "clawdbot/plugin-sdk";

export default function register(api) {
  registerPluginHooksFromDir(api, "./hooks");
}

Notes:

  • Hook directories follow the normal hook structure (HOOK.md + handler.ts).
  • Hook eligibility rules still apply (OS/bins/env/config requirements).
  • Plugin-managed hooks show up in clawdbot hooks list with plugin:<id>.
  • You cannot enable/disable plugin-managed hooks via clawdbot 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 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:

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.

Register a messaging channel

Plugins can register channel plugins that behave like builtin channels (WhatsApp, Telegram, etc.). Channel config lives under channels.<id> and is validated by your channel plugin code.

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.
  • meta.preferOver lists channel ids to skip auto-enable when both are configured.
  • meta.detailLabel and meta.systemImage let UIs show richer channel labels/icons.

Write a new messaging channel (stepbystep)

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 multiaccount setups.
  1. 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>.
  • meta.preferOver lets a plugin replace another channel (auto-enable prefers it).
  • meta.detailLabel and meta.systemImage are used by UIs for detail text/icons.
  1. Implement the required adapters
  • config.listAccountIds + config.resolveAccount
  • capabilities (chat types, media, threads, etc.)
  • outbound.deliveryMode + outbound.sendText (for basic send)
  1. 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)
  1. Register the channel in your plugin
  • api.registerChannel({ plugin })

Minimal config example:

{
  channels: {
    acmechat: {
      accounts: {
        default: { token: "ACME_TOKEN", enabled: true }
      }
    }
  }
}

Minimal channel plugin (outboundonly):

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.

Agent tools

See the dedicated guide: Plugin agent tools.

Register a gateway RPC method

export default function (api) {
  api.registerGatewayMethod("myplugin.status", ({ respond }) => {
    respond(true, { ok: true });
  });
}

Register CLI commands

export default function (api) {
  api.registerCli(({ program }) => {
    program.command("mycmd").action(() => {
      console.log("Hello");
    });
  }, { commands: ["mycmd"] });
}

Register background services

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 its present in your workspace/managed skills locations.

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.*.

Example plugin: Voice Call

This repo includes a voicecall plugin (Twilio or log fallback):

  • Source: extensions/voice-call
  • Skill: skills/voice-call
  • CLI: clawdbot voicecall start|status
  • Tool: voice_call
  • RPC: voicecall.start, voicecall.status
  • Config (twilio): provider: "twilio" + twilio.accountSid/authToken/from (optional statusCallbackUrl, twimlUrl)
  • Config (dev): provider: "log" (no network)

See Voice Call and extensions/voice-call/README.md for setup and usage.

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.

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).