Create Your Own OpenClaw Skill
Skills are the building blocks that give OpenClaw new capabilities. This guide walks you through creating, testing, and publishing your own custom skill.
What Are Skills?
Skills are small, focused packages that teach OpenClaw how to do specific tasks. They contain:
- Instructions (SKILL.md) — What the skill does and how to use it
- Scripts (optional) — Executable code for complex operations
- References (optional) — Documentation the agent can consult
- Assets (optional) — Templates, images, or other files
When you ask OpenClaw something, it scans available skills and loads the relevant one based on its description. The skill's instructions then guide the agent through the task.
Why Create a Skill?
- Consistency — Encode your workflows so OpenClaw follows them every time
- Domain knowledge — Teach OpenClaw about your company's APIs, schemas, or processes
- Reusable tools — Package scripts that would otherwise be rewritten repeatedly
- Share with others — Publish to ClawHub for the community
Skill Structure
Every skill follows this structure:
my-skill/
├── SKILL.md # Required: Instructions and metadata
├── scripts/ # Optional: Executable code
│ └── process.py
├── references/ # Optional: Documentation to load as needed
│ └── api-docs.md
└── assets/ # Optional: Templates, images, etc.
└── template.html
The only required file is SKILL.md. Everything else is optional.
Step 1: Plan Your Skill
Before writing code, answer these questions:
-
What task does this skill help with?
- Be specific: "Rotate PDF pages" not "Work with PDFs"
-
What triggers should activate this skill?
- Think about what users will say: "rotate this PDF", "flip the page", "turn PDF sideways"
-
What resources does it need?
- Scripts for deterministic operations?
- Reference docs for complex domains?
- Templates or assets for output?
Example: Weather Alert Skill
Let's build a skill that checks weather and sends alerts:
- Task: Check weather for a location and alert if conditions are severe
- Triggers: "weather alert", "storm warning", "check for severe weather"
- Resources: A script to fetch weather data
Step 2: Create the SKILL.md
The SKILL.md file has two parts:
- YAML frontmatter — Metadata that's always in context
- Markdown body — Instructions loaded when the skill triggers
Frontmatter (Required)
---
name: weather-alert
description: Check weather conditions and send alerts for severe weather. Use when asked to monitor weather, check for storms, set up weather alerts, or get severe weather warnings for any location.
---
The description is critical — it determines when OpenClaw uses your skill. Include:
- What the skill does
- Specific phrases that should trigger it
- When NOT to use it (if relevant)
Body (Required)
Write clear, concise instructions:
# Weather Alert
Check weather conditions and alert on severe weather.
## Usage
1. Get the location from the user (city, zip code, or coordinates)
2. Fetch current conditions using the weather API
3. Check for severe weather indicators:
- Wind speed > 50 mph
- Tornado watch/warning
- Flash flood warning
- Extreme temperatures (<0°F or >100°F)
4. If severe conditions found, send an alert via the user's preferred channel
## API
Use wttr.in for weather data:
```bash
curl "wttr.in/Denver?format=j1"
Alert Format
Keep alerts brief and actionable:
⚠️ WEATHER ALERT: Denver, CO
Condition: High winds (65 mph gusts)
Action: Secure outdoor items, avoid travel if possible
Valid until: 6:00 PM MST
### Writing Tips
- **Be concise** — The context window is shared with everything else
- **Use examples** — Show don't tell
- **Assume intelligence** — OpenClaw is smart; only explain non-obvious things
- **Imperative mood** — "Check the weather" not "You should check the weather"
---
## Step 3: Add Scripts (Optional)
For tasks that need deterministic reliability, add scripts:
my-skill/ ├── SKILL.md └── scripts/ └── fetch_weather.py
**scripts/fetch_weather.py:**
```python
#!/usr/bin/env python3
"""Fetch weather data from wttr.in"""
import sys
import json
import urllib.request
def get_weather(location: str) -> dict:
"""Fetch weather for a location."""
url = f"https://wttr.in/{location}?format=j1"
with urllib.request.urlopen(url) as response:
return json.loads(response.read())
def check_severe(data: dict) -> list:
"""Check for severe weather conditions."""
alerts = []
current = data["current_condition"][0]
# Check wind speed
wind_mph = int(current.get("windspeedMiles", 0))
if wind_mph > 50:
alerts.append(f"High winds: {wind_mph} mph")
# Check temperature
temp_f = int(current.get("temp_F", 70))
if temp_f < 0:
alerts.append(f"Extreme cold: {temp_f}°F")
elif temp_f > 100:
alerts.append(f"Extreme heat: {temp_f}°F")
return alerts
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: fetch_weather.py <location>")
sys.exit(1)
location = sys.argv[1]
data = get_weather(location)
alerts = check_severe(data)
if alerts:
print("⚠️ SEVERE WEATHER ALERTS:")
for alert in alerts:
print(f" - {alert}")
else:
print("✓ No severe weather conditions")
Reference it in SKILL.md:
## Checking Weather
Run the weather script:
```bash
python scripts/fetch_weather.py "Denver, CO"
The script returns alerts for:
- Wind > 50 mph
- Temperature < 0°F or > 100°F
---
## Step 4: Add References (Optional)
For complex domains, add reference documentation:
my-skill/ ├── SKILL.md └── references/ └── weather-codes.md
Reference docs are loaded **only when needed**, keeping the context lean.
In SKILL.md, tell OpenClaw when to use them:
```markdown
## Weather Codes
For interpreting weather condition codes, see [references/weather-codes.md](references/weather-codes.md).
Step 5: Test Locally
Install Your Skill
Copy your skill to the OpenClaw skills directory:
# Find your workspace
cd ~/.openclaw/workspace
# Create skills directory if needed
mkdir -p skills
# Copy your skill
cp -r /path/to/my-skill skills/
Verify It's Loaded
openclaw skills list
Your skill should appear in the list.
Test It
Start a conversation with OpenClaw and try phrases that should trigger your skill:
You: Check the weather alerts for Denver
OpenClaw: [Should use your weather-alert skill]
Debug Tips
If your skill isn't triggering:
- Check the description — Does it include the phrases you're using?
- Check for errors — Run
openclaw logs | grep -i "skill" - Restart the gateway —
openclaw gateway restart
If your skill triggers but doesn't work:
- Test scripts manually — Run them outside OpenClaw first
- Check file paths — Use relative paths from the skill directory
- Simplify — Start with minimal instructions, add complexity gradually
Step 6: Publish to ClawHub
Ready to share? Publish to ClawHub so others can install your skill with one command.
Prerequisites
- Create a ClawHub account at clawhub.com
- Install the ClawHub CLI:
npm install -g clawhub - Log in:
clawhub login
Prepare for Publishing
Add a clawhub.json to your skill:
{
"name": "weather-alert",
"version": "1.0.0",
"description": "Check weather and alert on severe conditions",
"author": "your-username",
"license": "MIT",
"keywords": ["weather", "alerts", "notifications"],
"repository": "https://github.com/you/weather-alert-skill"
}
Publish
cd my-skill
clawhub publish
Others Can Install
npx clawhub install weather-alert
Best Practices
Keep It Focused
One skill = one job. Don't create a "do everything" skill.
❌ Bad: office-tools — handles PDFs, spreadsheets, docs, and presentations
✅ Good: pdf-rotate — rotates PDF pages
Write Good Descriptions
The description determines when your skill triggers. Be comprehensive:
# ❌ Bad - too vague
description: Work with weather data
# ✅ Good - specific triggers included
description: Check weather conditions and send alerts for severe weather. Use when asked to monitor weather, check for storms, set up weather alerts, or get severe weather warnings for any location.
Use Progressive Disclosure
Don't dump everything in SKILL.md. Use the three-level system:
- Frontmatter (~100 words) — Always loaded
- SKILL.md body (<500 lines) — Loaded when triggered
- References (unlimited) — Loaded on demand
Test with Real Users
The best skills come from iteration:
- Build a minimal version
- Use it on real tasks
- Notice what's missing or confusing
- Improve and repeat
Example Skills
Minimal Skill (Instructions Only)
git-commit-message/
└── SKILL.md
---
name: git-commit-message
description: Generate conventional commit messages. Use when asked to write a commit message, format a commit, or follow conventional commits.
---
# Git Commit Message
Generate commit messages following the Conventional Commits spec.
## Format
<type>(<scope>): <subject>
<body> <footer> ```Types
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Formatting, no code changerefactor: Code change that neither fixes nor addstest: Adding testschore: Maintenance
Examples
feat(auth): add OAuth2 login support
Implements Google and GitHub OAuth providers.
Closes #123
fix(api): handle null response from payment gateway
### Script-Heavy Skill
image-resize/ ├── SKILL.md └── scripts/ └── resize.py
### Reference-Heavy Skill
company-api/ ├── SKILL.md └── references/ ├── endpoints.md ├── authentication.md └── error-codes.md
---
## Troubleshooting
### Skill Not Appearing in List
- Check the SKILL.md has valid YAML frontmatter
- Ensure `name` and `description` fields exist
- Verify the skill is in `~/.openclaw/workspace/skills/`
### Skill Not Triggering
- Review the `description` — does it match what users say?
- Check for typos in the skill name
- Restart the gateway: `openclaw gateway restart`
### Scripts Not Running
- Check file permissions: `chmod +x scripts/*.py`
- Verify the shebang line: `#!/usr/bin/env python3`
- Test the script directly: `python scripts/myscript.py`
### "File Not Found" Errors
- Use relative paths from the skill directory
- Don't use absolute paths or `~/`
- Check the file actually exists
---
## What's Next?
- **[Browse Skills](/skills)** — See what others have built
- **[Post-Install Setup](/guides/post-install)** — Make sure OpenClaw is configured
- **[ClawHub](https://clawhub.com)** — Publish and discover skills
### Community
Share your skills and get feedback:
- **Discord**: [discord.gg/clawd](https://discord.gg/clawd)
- **GitHub**: [github.com/openclaw/openclaw](https://github.com/openclaw/openclaw)