Haven't installed OpenClaw yet?
curl -fsSL https://openclaw.ai/install.sh | bashiwr -useb https://openclaw.ai/install.ps1 | iexcurl -fsSL https://openclaw.ai/install.cmd -o install.cmd && install.cmd && del install.cmdWorried it'll affect your machine? ClawTank — cloud deploy in 60s, zero risk to your files.
How to Create Custom OpenClaw Skills: A Complete Developer Tutorial
OpenClaw ships with built-in capabilities, but its real power comes from custom skills. A skill is a self-contained unit of functionality defined in a Markdown file. No compiled code, no build system -- just a Markdown file that tells OpenClaw what a skill does, what tools it can use, and how to behave.
What Is a Skill?
A skill is a SKILL.md file that defines metadata (name, triggers, version), instructions for the LLM, tool declarations (shell, HTTP, filesystem), and examples of expected behavior.
When a message arrives, OpenClaw's gateway matches it against registered skills. If a match is found, the skill's instructions and tool declarations are injected into the LLM prompt, giving the model the knowledge and permissions to complete the task. You extend OpenClaw without writing traditional code -- the LLM is the execution engine; the skill file is the blueprint.
Directory Structure
~/.openclaw/
workspace/
skills/
weather-checker/
SKILL.md
url-shortener/
SKILL.md
config.json
daily-digest/
SKILL.md
templates/
digest.md
Each skill gets its own directory under ~/.openclaw/workspace/skills/. The only required file is SKILL.md.
The SKILL.md Format
A skill file has YAML frontmatter and a Markdown body. Here is the simplest possible skill:
---
name: hello-world
description: Responds with a greeting
version: 1.0.0
triggers:
- "say hello"
- "greet me"
---
# Hello World
When the user asks you to say hello, respond with a friendly greeting
that includes the current date and time.
Full Frontmatter Reference
---
name: weather-checker
description: Checks current weather for any city using wttr.in
version: 1.0.0
triggers:
- "weather in {city}"
- "what's the weather"
- "forecast for {city}"
author: your-username
license: MIT
tags: [weather, utility, api]
tools:
- shell
- http
dependencies: []
config:
units:
type: string
default: metric
description: Temperature units (metric or imperial)
---
Triggers support exact phrases and parameterized patterns. {city} captures a value from the user's message via fuzzy matching. Tools declare which system capabilities the skill needs -- if a skill does not declare a tool, the LLM cannot access it during execution. This is a security feature[1].
Writing Effective Instructions
The Markdown body is injected directly into the LLM prompt. Good instructions produce reliable skills; vague instructions produce unpredictable behavior.
Three principles matter most:
Be specific about output format -- tell the LLM exactly what the response should look like. Specify exact commands -- do not assume the LLM knows the right shell command or API endpoint. Handle errors explicitly -- tell the LLM what to do when an API call fails or input is invalid.
Practical Example: Weather Checker
mkdir -p ~/.openclaw/workspace/skills/weather-checker
---
name: weather-checker
description: Checks current weather conditions for any city worldwide
version: 1.2.0
triggers:
- "weather in {city}"
- "what's the weather in {city}"
- "forecast for {city}"
- "temperature in {city}"
tools:
- shell
- http
config:
units:
type: string
default: metric
tags: [weather, utility]
---
# Weather Checker
You check current weather using the wttr.in API.
## Fetching Data
Run this command:
\```bash
curl -s "wttr.in/{city}?format=j1"
\```
Replace {city} with the user's city. URL-encode spaces as +.
## Parsing the Response
Extract from `current_condition[0]`: `temp_C`, `FeelsLikeC`,
`weatherDesc[0].value`, `humidity`, `windspeedKmph`, `winddir16Point`.
## Response Format
**Weather in {City}:**
- Temperature: {temp} ({feels_like} feels like)
- Conditions: {conditions}
- Humidity: {humidity}%
- Wind: {speed} km/h {direction}
## Error Handling
- curl fails: "The weather service is currently unavailable."
- City not found: "I couldn't find weather data for '{city}'."
- No city given: "Which city would you like the weather for?"
Test it:
