Build Your First OpenClaw Skill from Scratch and Publish to ClawHub in 30 Minutes
This tutorial walks you through setting up the environment, designing the skill structure, writing the SKILL.md metadata, implementing core Python code, testing locally, and publishing the OpenClaw skill to ClawHub, including best practices for error handling, logging, versioning, and maintenance.
Introduction
The guide promises a 30‑minute end‑to‑end workflow for creating an OpenClaw Skill and publishing it on ClawHub so that thousands of users can invoke it.
Step 1: Environment Preparation
1.1 Install OpenClaw
# macOS / Linux
curl -fsSL https://install.openclaw.ai | bash
# Verify installation
openclaw --version
# First run
openclaw1.2 Configure API Key
# Method 1: Environment variable (recommended)
export ANTHROPIC_API_KEY="sk-ant-xxxx"
# Method 2: Config file (~/.openclaw/openclaw.json)
{
"agent": {
"model": {
"primary": "anthropic/claude-sonnet-4-5"
}
}
}1.3 Create Workspace
# Create workspace directory
mkdir -p ~/openclaw-skills
cd ~/openclaw-skills
# Create your first skill directory
mkdir -p weather-skill
cd weather-skill
# Initialise git repository (recommended for version control)
git initStep 2: Skill Structure Design
2.1 Minimal Structure (2 files)
your-skill/
├── SKILL.md # Required description file
└── README.md # Optional detailed usage guide2.2 Recommended Full Structure
your-skill/
├── SKILL.md # Core description (required)
├── README.md # Usage instructions
├── skill.json # Optional metadata
├── scripts/
│ ├── main.py # Main script
│ └── utils.py # Helper functions
├── assets/
│ └── icon.png # Optional icon
└── tests/
└── test_skill.py # Optional tests2.3 Naming Conventions
Skill name : lowercase + hyphens (e.g., weather-skill)
File name : lowercase + underscores (e.g., main.py)
Description : concise and clear (e.g., "Query real‑time weather")
Step 3: Write SKILL.md (Core Metadata)
SKILL.md is the soul of a Skill ; it tells OpenClaw when to invoke it.
3.1 Standard YAML Frontmatter
---
name: your-skill-name
description: One‑sentence core function
triggers:
- trigger phrase 1
- trigger phrase 2
- trigger phrase 3
metadata:
openclaw:
emoji: "🌤️"
requires:
bins: ["curl", "python3"]
homepage: https://github.com/your/skill
---
# Skill Name
## Function Description
Detailed description of what the skill does and which problem it solves.
## Usage Examples
... (add concrete examples)
## Parameter Specification
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| param1 | string | ✅ | Parameter 1 description |
| param2 | number | ⬜ | Parameter 2 description |
## Notes
- Usage limits
- Dependency conditions
- Common issues
## Changelog
- v1.0.0 – Initial version3.2 Real‑World Example: Weather Query Skill
---
name: weather-query
description: Query real‑time weather and 7‑day forecast for a city
triggers:
- 天气
- 气温
- 天气预报
- 今天多少度
- 会下雨吗
metadata:
openclaw:
emoji: "🌤️"
requires:
bins: ["curl"]
homepage: https://wttr.in/:help
---
# 天气查询 Skill
查询全球任意城市的实时天气、温度、湿度、风力等信息,支持 7 天预报。
## ✅ When to Use
- "今天天气怎么样?"
- "明天会下雨吗?"
- "北京的气温"
- "周末天气如何"
- "旅行前查看天气"
## ❌ When Not to Use
- Historical weather data → use a weather archive API
- Climate analysis or trends → use professional data sources
- Extreme weather alerts → check official meteorological agency
- Aviation/Maritime weather → use specialized services (METAR, etc.)
## Parameter Specification
| Parameter | Type | Required | Description |
|-----------|--------|----------|-------------|
| city | string | ✅ | City name (supports Chinese, English, airport code) |
| days | number | ⬜ | Forecast days (1‑7, default 1) |
## Notes
- ⚠️ Requires internet connectivity
- ⚠️ Some small cities may not be supported
- ⚠️ Data latency ~15 minutes
- ⚠️ Do not request too frequently (rate‑limited)Step 4: Implement Core Functionality
4.1 Choose Implementation Method
Python script – data processing, API calls – ★★ – example: weather query
Node.js script – web services, real‑time interaction – ★★ – example: GitHub integration
Shell script – system commands, file ops – ★ – example: file backup
Pure configuration – simple tool calls – ★ – example: calculator
4.2 Full Python Implementation
#!/usr/bin/env python3
"""
Weather Query Skill – core implementation
Reference: https://wttr.in/:help
"""
import requests, json, sys, os, logging
from typing import Optional, Dict, Any
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class WeatherSkill:
"""Weather query Skill core class"""
def __init__(self):
self.api_url = "https://wttr.in"
logger.info("WeatherSkill initialised")
def query_weather(self, city: str, days: int = 1) -> Dict[str, Any]:
"""Query weather for *city* and *days* (1‑7).
Raises:
TimeoutError – request timeout
ValueError – invalid arguments
"""
if not city or not city.strip():
raise ValueError("城市名称不能为空")
if days < 1 or days > 7:
raise ValueError("预报天数必须在 1-7 之间")
logger.info(f"查询天气:city={city}, days={days}")
try:
params = {"format": "j1", "lang": "zh-cn"}
url = f"{self.api_url}/{city}"
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
result = self._parse_weather_data(data, days)
logger.info(f"天气查询成功:{result}")
return result
except requests.Timeout:
logger.error("查询超时")
return {"error": "查询超时,请重试"}
except requests.RequestException as e:
logger.error(f"请求失败:{e}")
return {"error": f"查询失败:{str(e)}"}
except Exception as e:
logger.error(f"未知错误:{e}")
return {"error": f"未知错误:{str(e)}"}
def _parse_weather_data(self, data: dict, days: int) -> Dict[str, Any]:
"""Parse raw API data into a friendly dict"""
try:
current = data.get("current_condition", [{}])[0]
return {
"city": data.get("nearest_area", [{}])[0].get("areaName", [{}])[0].get("value", "未知"),
"temperature": current.get("temp_C", 0),
"feels_like": current.get("FeelsLikeC", 0),
"condition": current.get("weatherDesc", [{}])[0].get("value", "未知"),
"humidity": current.get("humidity", 0),
"wind_speed": current.get("windspeedKmph", 0),
"wind_dir": current.get("winddir16Point", "未知"),
"pressure": current.get("pressure", 0),
"visibility": current.get("visibility", 0),
"forecast": data.get("weather", [])[:days]
}
except Exception as e:
logger.error(f"解析数据失败:{e}")
return {"error": "数据解析失败"}
def format_output(self, data: Dict[str, Any]) -> str:
"""Format the result for display"""
if "error" in data:
return f"❌ {data['error']}"
output = f"""🌤️ {data['city']}天气
🌡️ 温度:{data['temperature']}°C (体感{data['feels_like']}°C)
💧 湿度:{data['humidity']}%
💨 风力:{data['wind_speed']}km/h {data['wind_dir']}
📝 状况:{data['condition']}
🔍 能见度:{data['visibility']}km
"""
if data.get("forecast"):
output += "
📅 预报:
"
for day in data["forecast"]:
date = day.get("date", "未知")
max_temp = day.get("maxtempC", 0)
min_temp = day.get("mintempC", 0)
output += f"- {date}: {min_temp}°C ~ {max_temp}°C
"
return output
def main():
"""Entry point"""
if len(sys.argv) < 2:
print("用法:python3 weather.py <城市名> [天数]")
print("示例:python3 weather.py 北京 3")
sys.exit(1)
city = sys.argv[1]
days = int(sys.argv[2]) if len(sys.argv) > 2 else 1
skill = WeatherSkill()
data = skill.query_weather(city, days)
print(skill.format_output(data))
if __name__ == "__main__":
main()4.3 Key Points
Error handling (mandatory)
try:
result = api_call()
except TimeoutError:
return {"error": "请求超时"}
except APIError as e:
return {"error": f"API 错误:{e}"}
except Exception as e:
return {"error": f"未知错误:{e}"}Logging (recommended)
import logging
logger = logging.getLogger(__name__)
logger.info(f"查询天气:{city}")
logger.error(f"查询失败:{error}")Parameter validation (mandatory)
if not city or not city.strip():
raise ValueError("城市名称不能为空")
if days < 1 or days > 7:
raise ValueError("预报天数必须在 1-7 之间")Step 5: Local Testing
5.1 Basic Test
# Verify script runs
python3 scripts/main.py 北京
# Expected output (example)
# 🌤️ 北京天气
# 🌡️ 温度:25°C (体感 27°C)
# ...5.2 Edge Cases
# Empty input
python3 scripts/main.py ""
# Special characters
python3 scripts/main.py "北京&上海"
# Simulate timeout by setting timeout=0.001 in the code5.3 Integration Test in OpenClaw
# 1. Copy skill into OpenClaw workspace
cp -r weather-skill ~/.openclaw/workspace/skills/
# 2. Restart OpenClaw or start a new session
# 3. Trigger the skill
@weather-skill 北京天气怎么样?
# Expected output
# 🌤️ 北京天气
# 🌡️ 温度:25°C
# ...5.4 Debugging Tips
Add debug prints
def debug_print(*args):
print(f"[DEBUG] {args}", file=sys.stderr)
debug_print("查询参数:", city, days)Use appropriate log levels (DEBUG during development, INFO in production)
Step 6: Packaging and Publishing
6.1 Install ClawHub CLI
# npm
npm i -g clawhub
# or pnpm
pnpm add -g clawhub
# Verify
clawhub --version6.2 Login to ClawHub
# First time login
clawhub login
# Check account
clawhub whoami6.3 Publish the Skill
# Publish a single skill
clawhub publish . \
--slug weather-skill \
--name "天气查询" \
--version 1.0.0 \
--tags latest
# Batch sync (recommended)
clawhub sync --all6.4 Version Management
# List published versions
clawhub list
# Patch bump (bug fix)
clawhub publish . --slug weather-skill --version 1.0.1 --bump patch
# Minor bump (new feature)
clawhub publish . --slug weather-skill --version 1.1.0 --bump minor
# Major bump (breaking change)
clawhub publish . --slug weather-skill --version 2.0.0 --bump major6.5 Pre‑publish Checklist
SKILL.md correctly formatted (complete YAML frontmatter)
README.md includes usage examples and parameter details
No hard‑coded credentials; use environment variables
Robust error handling (try‑except blocks)
Local tests pass for at least three scenarios
Semantic version number follows MAJOR.MINOR.PATCH
.gitignore excludes sensitive files (config.json, .env, *.key, __pycache__)
Step 7: Maintenance and Updates
7.1 Handling User Feedback
Channels: ClawHub comments, GitHub Issues, direct messages/email
Response template (issue template)
## Issue Description
[User‑reported problem]
## Reproduction Steps
1. Install the skill
2. Run command: @weather-skill 北京天气
3. Observe error: XXX
## Solution
[Proposed fix]
## Fixed in version
v1.0.17.2 Versioning Guidelines
MAJOR – breaking changes MINOR – new features PATCH – bug fixes
7.3 Metrics (Key Indicators)
Downloads : total installations – target 1000+
Active Users : weekly active users – target 100+
Rating : 4‑5 star ratio – target 80%+
Common Pitfalls and Solutions
SKILL.md format errors – symptom: skill not triggered. Fix: validate YAML (use yamllint) and ensure proper indentation. API key leakage – symptom: key appears in public repo. Fix: store key in environment variables or a config file excluded via .gitignore. Missing error handling – symptom: uncaught exceptions shown to users. Fix: wrap all external calls in try‑except and return friendly error messages. Insufficient testing – symptom: bugs reported after release. Fix: perform basic, edge‑case, and integration tests; involve peers for fresh perspectives. Poor trigger design – symptom: high false‑positive rate or no activation. Fix: use specific, high‑precision trigger phrases (e.g., "天气查询", "今天多少度").
Resources
OpenClaw official documentation
ClawHub skill marketplace
Skill development specifications
Example projects: weather-skill, coding-agent, file-processor
Tools: clawhub CLI, YAML Lint, Semantic Versioning guide
Conclusion
By following the seven steps—environment setup, structure design, SKILL.md authoring, core implementation, local testing, packaging & publishing, and ongoing maintenance—you can create a functional OpenClaw Skill, publish it to ClawHub, and iterate based on user feedback.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Frontend AI Walk
Looking for a one‑stop platform that deeply merges frontend development with AI? This community focuses on intelligent frontend tech, offering cutting‑edge insights, practical implementation experience, toolchain innovations, and rich content to help developers quickly break through in the AI‑driven frontend era.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
