Documentation Index
Fetch the complete documentation index at: https://mintlify.com/openclaw/openclaw/llms.txt
Use this file to discover all available pages before exploring further.
This guide walks you through creating your first OpenClaw plugin. We’ll build a simple plugin that adds a custom agent tool.
Prerequisites
- OpenClaw installed (Node 22+)
- TypeScript knowledge
- Basic understanding of OpenClaw concepts (agents, tools, sessions)
Project Setup
Create plugin directory
Create a directory for your plugin:mkdir my-plugin
cd my-plugin
Initialize package.json
Create a package.json:{
"name": "openclaw-plugin-hello",
"version": "1.0.0",
"type": "module",
"main": "index.ts",
"dependencies": {},
"devDependencies": {
"openclaw": "latest"
}
}
Note: If you plan to publish to npm, use the @openclaw/ scope: @openclaw/my-plugin. Create plugin entry point
Create index.ts:import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
const plugin = {
id: "hello",
name: "Hello Plugin",
description: "Example plugin with a custom tool",
configSchema: emptyPluginConfigSchema(),
register(api: OpenClawPluginApi) {
api.logger.info("Hello plugin registered");
}
};
export default plugin;
Add the plugin to OpenClaw
Edit your ~/.openclaw/config.json:{
"plugins": {
"entries": {
"hello": {
"enabled": true,
"source": "/path/to/my-plugin/index.ts"
}
}
}
}
Or install from a local directory:openclaw plugins install /path/to/my-plugin
Verify the plugin loads
Restart the gateway and check logs:You should see: Hello plugin registered
Let’s add an agent tool that generates greetings:
import type { OpenClawPluginApi, AnyAgentTool } from "openclaw/plugin-sdk";
import { Type } from "@sinclair/typebox";
function createGreetingTool(): AnyAgentTool {
return {
name: "greet",
description: "Generate a personalized greeting",
input: Type.Object({
name: Type.String({ description: "Name to greet" }),
style: Type.Optional(
Type.Union([Type.Literal("formal"), Type.Literal("casual")])
)
}),
execute: async (args) => {
const { name, style = "casual" } = args;
const greeting =
style === "formal"
? `Good day, ${name}. How may I assist you?`
: `Hey ${name}! What's up?`;
return {
result: "success",
details: { greeting }
};
}
};
}
const plugin = {
id: "hello",
name: "Hello Plugin",
description: "Example plugin with a custom tool",
configSchema: emptyPluginConfigSchema(),
register(api: OpenClawPluginApi) {
api.registerTool(createGreetingTool());
api.logger.info("Greeting tool registered");
}
};
export default plugin;
OpenClaw uses @sinclair/typebox for tool schemas. Common patterns:
import { Type } from "@sinclair/typebox";
// String
Type.String({ description: "A text value" })
// Number
Type.Number({ description: "A numeric value", minimum: 0 })
// Boolean
Type.Boolean({ description: "True or false" })
// Optional field
Type.Optional(Type.String())
// Enum (use Type.Union with Type.Literal for string enums)
Type.Union([
Type.Literal("option1"),
Type.Literal("option2")
])
// Array
Type.Array(Type.String())
// Object
Type.Object({
field1: Type.String(),
field2: Type.Number()
})
Restart the gateway and send a message:
Greet Alice in a formal style
The agent should use the greet tool and respond with the generated greeting.
You can mark tools as optional (requiring explicit allowlisting):
api.registerTool(createGreetingTool(), { optional: true });
Then allowlist in agent config:
{
"agents": {
"list": [
{
"id": "main",
"tools": { "allow": ["greet"] }
}
]
}
}
Add Configuration
Plugins can define configuration schemas:
import { z } from "zod";
const configSchema = z.object({
defaultStyle: z.enum(["formal", "casual"]).default("casual"),
prefix: z.string().default("")
});
const plugin = {
id: "hello",
configSchema: {
safeParse: (value) => configSchema.safeParse(value),
uiHints: {
defaultStyle: { label: "Default Style" },
prefix: { label: "Greeting Prefix", placeholder: "Hello" }
}
},
register(api: OpenClawPluginApi) {
const config = api.pluginConfig as z.infer<typeof configSchema>;
const defaultStyle = config?.defaultStyle ?? "casual";
// Use config values...
}
};
Configure via config.json:
{
"plugins": {
"entries": {
"hello": {
"enabled": true,
"config": {
"defaultStyle": "formal",
"prefix": "Greetings,"
}
}
}
}
}
Directory Structure
For larger plugins, organize code into modules:
my-plugin/
package.json
index.ts # Entry point
src/
tool.ts # Tool definitions
config.ts # Config schema
service.ts # Background service
README.md
// index.ts
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { createGreetingTool } from "./src/tool.js";
import { configSchema } from "./src/config.js";
const plugin = {
id: "hello",
configSchema,
register(api: OpenClawPluginApi) {
api.registerTool(createGreetingTool(api));
}
};
export default plugin;
Next Steps
Now that you’ve built a basic plugin, explore advanced features: