Plugin Development
OpenClaw plugins are distributable packages of tools, agent configurations, and integrations. Building a plugin lets you share functionality across projects, distribute to other teams, or publish to the community plugin registry.
Plugins vs Custom Tools
Custom tools (covered in the previous guide) are single Python functions added to one agent. Plugins are structured packages that can contain multiple tools, agent configurations, default settings, dependencies, and documentation — designed to be installed and shared like any Python package.
| Custom Tool | Plugin | |
|---|---|---|
| Scope | Single function, one project | Package of tools + config, shareable |
| Distribution | Copy Python file | pip install or plugin registry |
| Dependencies | Manual management | Declared in setup.cfg / pyproject.toml |
| Configuration | Hardcoded or env vars | Declarative schema with defaults |
| Versioning | None | Semantic versioning via package |
Plugin Structure
OpenClaw plugins follow a standard directory layout that the plugin registry and installer understand:
my-openclaw-plugin/
├── pyproject.toml # package metadata and dependencies
├── README.md # installation and usage docs
├── my_plugin/
│ ├── __init__.py # expose plugin entrypoint
│ ├── plugin.json # plugin manifest (name, version, description)
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── main_tool.py # tool implementations
│ │ └── helper_tool.py
│ └── agents/
│ └── default_config.yaml # optional bundled agent configs// plugin.json — plugin manifest
{
"name": "openclaw-github",
"version": "1.2.0",
"description": "GitHub integration tools for OpenClaw agents",
"openclaw_min_version": "0.8.0",
"tools": ["list_issues", "create_issue", "get_pr_diff", "post_comment"],
"config_schema": {
"github_token": { "type": "string", "env": "GITHUB_TOKEN", "required": true },
"default_repo": { "type": "string", "required": false }
}
}Implementing Plugin Tools
Plugin tools follow the same @tool decorator pattern as custom tools, with the addition of plugin-level configuration access:
from openclaw.tools import tool
from openclaw.plugins import get_plugin_config
@tool
def list_issues(repo: str = None, state: str = "open", limit: int = 20) -> list:
"""
List GitHub issues for a repository.
Args:
repo: Repository in owner/name format. Uses plugin default_repo if not specified.
state: Issue state — 'open', 'closed', or 'all'. Default: 'open'
limit: Maximum number of issues to return. Default: 20, max: 100
Returns:
List of issue dicts with keys: number, title, state, labels, url, created_at
"""
config = get_plugin_config("openclaw-github")
token = config["github_token"]
target_repo = repo or config.get("default_repo")
if not target_repo:
raise ValueError("repo must be specified or plugin default_repo must be configured")
import requests
headers = {"Authorization": f"Bearer {token}", "Accept": "application/vnd.github.v3+json"}
response = requests.get(
f"https://api.github.com/repos/{target_repo}/issues",
params={"state": state, "per_page": min(limit, 100)},
headers=headers, timeout=15
)
response.raise_for_status()
issues = response.json()
return [{"number": i["number"], "title": i["title"], "state": i["state"],
"labels": [l["name"] for l in i.get("labels", [])],
"url": i["html_url"], "created_at": i["created_at"]} for i in issues]Publishing Your Plugin
Plugins can be shared two ways: published to PyPI for open-source distribution, or published to the OpenClaw community plugin registry for discovery via openclaw plugin search.
# Publish to PyPI
pip install build twine
python -m build
twine upload dist/*
# Register with OpenClaw plugin registry (after PyPI publish)
openclaw plugin register --pypi-name openclaw-github --manifest my_plugin/plugin.json# Users install your plugin with:
openclaw plugin install openclaw-github
# or directly:
pip install openclaw-githubOnce installed, plugin tools are immediately available to any agent. Users configure plugin settings in their agent config or environment variables as declared in your plugin.json config schema.
Plugin Development Best Practices
- Declare all dependencies explicitly in
pyproject.toml— pin major versions to avoid surprise breakage - Use environment variables for secrets — never hardcode API keys or credentials in tool implementations
- Write comprehensive docstrings — they directly affect how accurately agents select your tools
- Include integration tests — test tools against real APIs in a sandbox environment before publishing
- Follow semantic versioning — breaking changes in tool signatures should increment the major version
- Provide a bundled agent config — a sensible default
agents/default_config.yamlhelps users get started quickly
Plugin Configuration Validation
Well-designed plugins validate their configuration at startup rather than failing unpredictably at runtime when a tool is actually called. OpenClaw's plugin system supports an on_load validation hook that runs when the plugin is first loaded:
from openclaw.plugins import plugin_init, get_plugin_config, PluginConfigError
@plugin_init
def validate_config():
"""Called when plugin is loaded — raise PluginConfigError if misconfigured."""
config = get_plugin_config("openclaw-github")
token = config.get("github_token")
if not token:
raise PluginConfigError("GITHUB_TOKEN must be set (via env var or config)")
# Optionally verify credentials are valid
import requests
resp = requests.get("https://api.github.com/user",
headers={"Authorization": f"Bearer {token}"}, timeout=5)
if resp.status_code == 401:
raise PluginConfigError("GITHUB_TOKEN is invalid or expired")This pattern surfaces configuration errors immediately (when OpenClaw starts) rather than mid-task, which produces much better error messages and avoids wasted agent work.
Plugin Documentation
Good documentation dramatically increases plugin adoption. At minimum, your plugin README should include: installation command, required configuration (env vars with example values), a quick-start example showing the plugin in action, and the complete list of tools with their purpose and expected inputs/outputs.
Consider including a sample agent-config.yaml that users can copy to get started. A working example that users can adapt is worth more than comprehensive API documentation — most developers start by modifying a working example rather than reading reference docs.
Plugin Lifecycle and Hot Reload
During plugin development, hot reload eliminates the restart-per-change cycle. Start OpenClaw with --dev flag and it watches for changes in registered plugin modules, reloading the tool registry automatically when a .py file changes. This is implemented via Python's importlib.reload and works for tool function body changes; changes to tool signatures (arguments, return types) still require a restart to update the LLM's tool schema. Use hot reload to iterate quickly on tool logic, then restart once to validate the final schema before running against the full agent.
For testing plugins in isolation, the openclaw tool call <tool_name> --args '{"arg": "value"}' command invokes a single tool directly without running the full agent reasoning loop. This is the fastest way to verify tool behavior during development and to build integration test fixtures.