Write your first plugin
WednesdayAI’s core is intentionally small. Features that are specific to a use case, platform, or workflow belong in plugins — not in core. This keeps the core stable and the system extensible without requiring a fork. A plugin is a Node.js package whoseregister(api) function attaches one or more capabilities through the plugin SDK. This guide builds a plugin with one agent tool. It covers both manifest forms, the SDK surface, async safety, optional tools, config schema, and local testing.
Prerequisites
- Node.js ≥ 24.12.0
- A running WednesdayAI instance for testing
- TypeScript familiarity — the plugin SDK is fully typed
Extension points overview
Before writing code, understand where plugins touch the system. Each capability is attached with a dedicated method on theOpenClawPluginApi object passed to your register function:
This guide builds a tool. For the other extension points, see Agent tools, Hooks, Channel adapters, and the Plugin SDK reference (which also covers providers, commands, services, gateway methods, session-store adapters, and web-search providers).
Step 1: Scaffold the plugin
Create a directory for your plugin. The directory name must match the pluginid exactly.
"type": "module" to package.json — the plugin SDK uses ESM:
tsconfig.json:
Step 2: Write the manifest
Createopenclaw.plugin.json at the plugin root. This file is required — the runtime validates plugin config against it without executing your code, so a missing or invalid manifest blocks loading.
The manifest is plain JSON (not JSON5):
idmust match the directory name exactly.configSchemais required. If your plugin has no config, use{ "type": "object", "additionalProperties": false }.additionalProperties: falseensures typos in user config are caught, not silently ignored.- The manifest is plain JSON — do not add comments or trailing commas.
- Declaring a field in
requiredmeans the runtime rejects the plugin config at load time if the field is absent. If you also guard for a missing value insideexecute(), that guard is belt-and-suspenders for deployments where config validation may be bypassed (e.g. direct API calls). Both are fine to have together.
openclaw.plugin.json vs the package.json "openclaw" key. These are two different things.
openclaw.plugin.json is the manifest — it carries id + configSchema and drives config
validation. The package.json "openclaw" key is used for workspace/pack discovery (the id,
or an extensions array for a multi-plugin pack). A published plugin typically ships both. See the
manifest reference.Step 3: Write the plugin entry point
The plugin’s entry exports a defaultregister function. This function is called by the runtime when the plugin is loaded.
Create src/index.ts:
- Import from
openclaw/plugin-sdk, not from relative paths into core. Relative core imports (../../src/...) work in bundled extensions but break in installed plugins. - Tool names are not deduplicated at registration time. If two tools share a name, both are registered and runtime resolution order determines which is called. Use a unique, namespaced name (e.g.
my-plugin_my_tool) to avoid conflicts.
Async safety
Theregister function may be synchronous or async. Use async if you need to perform startup I/O (reading a config file, pinging an API). The key constraint is: don’t block indefinitely.
Tool execute() functions are always async and run when the AI decides to call the tool. A slow execute() does not block the gateway — but it does block the user’s reply until it completes.
Do not do this:
description field.
Step 4: Register as optional
By default, registered tools are available to all agents. If your tool is not universally useful (makes outbound network requests, uses a paid API, has side effects), register it as optional:tools.allow key in their WednesdayAI config:
Step 5: Access plugin config in execute
If your manifest declares aconfigSchema, users set values under the plugin’s id in their WednesdayAI config. The config is available on the api object passed to register. Capture it in a closure so execute() can access it:
Step 6: Build and install locally
openclaw plugins list:
Step 7: Test it
Send a message to your WednesdayAI bot that should invoke the tool. If you registered it as optional, first add"my_tool" to tools.allow in your WednesdayAI config (see Step 4).
To watch tool calls in the logs on Linux:
Other extension points
Once you are comfortable registering a tool, the sameregister(api) entry attaches everything else:
- Lifecycle hooks —
api.on(name, handler)to transform LLM input/output, gate tool calls, or shape context. See Hooks. - Channel adapters —
api.registerChannel(...)to add a new messaging platform. See Channel adapters. - HTTP routes and gateway methods —
api.registerHttpRoute(...)/api.registerGatewayMethod(...). - Commands and CLI —
api.registerCommand(...)for chat commands that bypass the LLM;api.registerCli(...)for CLI subcommands. - Services —
api.registerService(...)for start/stop background work. - Providers, session stores, web search —
api.registerProvider(...),api.registerSessionStoreAdapter(...),api.registerWebSearchProvider(...).
What’s next
- Plugin manifest reference — all manifest fields and their effects
- Plugin SDK reference — full exported surface with types
- Agent tools — go deeper on the tool surface
- Hooks — lifecycle callbacks for context, LLM input/output, and model call events
- Channel adapters — add a new chat platform
- Publishing a plugin — the managed plugin lifecycle