Building Plugins
Building Plugins
Plugins extend RemoteClaw with new capabilities: channels, model providers, speech, image generation, web search, agent tools, or any combination.
You do not need to add your plugin to the RemoteClaw repository. Publish to
ClawHub or npm and users install with
remoteclaw plugins install <package-name>. RemoteClaw tries ClawHub first and
falls back to npm automatically.
Prerequisites
- Node >= 22 and a package manager (npm or pnpm)
- Familiarity with TypeScript (ESM)
- For in-repo plugins: repository cloned and
pnpm installdone
What kind of plugin?
Quick start: tool plugin
This walkthrough creates a minimal plugin that registers an agent tool. Channel and provider plugins have dedicated guides linked above.
```json remoteclaw.plugin.json{ "id": "my-plugin", "name": "My Plugin", "description": "Adds a custom tool to RemoteClaw", "configSchema": { "type": "object", "additionalProperties": false }}```</CodeGroup>
Every plugin needs a manifest, even with no config. See[Manifest](/plugins/manifest) for the full schema.```typescriptimport { definePluginEntry } from "remoteclaw/plugin-sdk/plugin-entry";import { Type } from "@sinclair/typebox";
export default definePluginEntry({ id: "my-plugin", name: "My Plugin", description: "Adds a custom tool to RemoteClaw", register(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: `Got: ${params.input}` }] }; }, }); },});```
`definePluginEntry` is for non-channel plugins. For channels, use`defineChannelPluginEntry` — see [Channel Plugins](/plugins/sdk-channel-plugins).For full entry point options, see [Entry Points](/plugins/sdk-entrypoints).**External plugins:** publish to [ClawHub](/tools/clawhub) or npm, then install:
```bashremoteclaw plugins install @myorg/remoteclaw-my-plugin```
RemoteClaw checks ClawHub first, then falls back to npm.
**In-repo plugins:** place under `extensions/` — automatically discovered.
```bashpnpm test -- extensions/my-plugin/```Plugin capabilities
A single plugin can register any number of capabilities via the api object:
| Capability | Registration method | Detailed guide |
|---|---|---|
| Text inference (LLM) | api.registerProvider(...) | Provider Plugins |
| Channel / messaging | api.registerChannel(...) | Channel Plugins |
| Speech (TTS/STT) | api.registerSpeechProvider(...) | Provider Plugins |
| Media understanding | api.registerMediaUnderstandingProvider(...) | Provider Plugins |
| Image generation | api.registerImageGenerationProvider(...) | Provider Plugins |
| Web search | api.registerWebSearchProvider(...) | Provider Plugins |
| Agent tools | api.registerTool(...) | Below |
| Custom commands | api.registerCommand(...) | Entry Points |
| Event hooks | api.registerHook(...) | Entry Points |
| HTTP routes | api.registerHttpRoute(...) | Internals |
| CLI subcommands | api.registerCli(...) | Entry Points |
For the full registration API, see SDK Overview.
Registering agent tools
Tools are typed functions the LLM can call. They can be required (always available) or optional (user opt-in):
register(api) { // Required tool — always available 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 }] }; }, });
// Optional tool — user must add to allowlist api.registerTool( { name: "workflow_tool", description: "Run a workflow", parameters: Type.Object({ pipeline: Type.String() }), async execute(_id, params) { return { content: [{ type: "text", text: params.pipeline }] }; }, }, { optional: true }, );}Users enable optional tools in config:
{ tools: { allow: ["workflow_tool"] },}- Tool names must not clash with core tools (conflicts are skipped)
- Use
optional: truefor tools with side effects or extra binary requirements - Users can enable all tools from a plugin by adding the plugin id to
tools.allow
Import conventions
Always import from focused remoteclaw/plugin-sdk/<subpath> paths:
import { definePluginEntry } from "remoteclaw/plugin-sdk/plugin-entry";import { createPluginRuntimeStore } from "remoteclaw/plugin-sdk/runtime-store";
// Wrong: monolithic root (deprecated, will be removed)import { ... } from "remoteclaw/plugin-sdk";For the full subpath reference, see SDK Overview.
Within your plugin, use local barrel files (api.ts, runtime-api.ts) for
internal imports — never import your own plugin through its SDK path.
Pre-submission checklist
remoteclaw metadatadefineChannelPluginEntry or definePluginEntryplugin-sdk/<subpath> pathspnpm test -- extensions/my-plugin/)pnpm check passes (in-repo plugins)