OpenClaw is powerful out of the box. It can browse the web, edit files, and run terminal commands. But the true magic of an autonomous agent isn't what it can do for you—it's what you can teach it to do.
If you are a developer (or even just a tinkerer), you aren't limited to the pre-installed skills. You can extend OpenClaw's capabilities by writing simple Python functions.
In this guide, we are going to build a Custom Skill from scratch.
We won't just print "Hello World." We're going to build something useful: A Crypto Price Checker. By the end of this tutorial, you will be able to ask OpenClaw "What's the price of Bitcoin?" and have it fetch real-time data from an API.
The Anatomy of a Skill
In OpenClaw, a "Skill" is essentially a python function decorated with metadata. This metadata tells the Large Language Model (LLM) how and when to use your tool.
A skill consists of three parts:
- The Function: The actual code that runs.
- The Docstring: A description of what the function does (this is the "prompt" for the AI).
- The Type Hints: Definitions of the input arguments.
Step 1: Setting Up Your Dev Environment
First, locate your OpenClaw skills directory. By default, this is in your home folder:
cd ~/.openclaw/custom_skills/
If this directory doesn't exist, create it. OpenClaw automatically watches this folder for new .py files using a hot-loader. You don't even need to restart the agent to see your changes!
Create a new file called crypto_price.py.
Step 2: Writing the Code
Let's write a standard Python function to fetch data from the CoinGecko API (which is free and doesn't require a key for simple usage).
import requests
from openclaw.sdk import skill
@skill
def get_crypto_price(coin_id: str = "bitcoin", currency: str = "usd") -> str:
"""
Fetches the current price of a cryptocurrency.
Use this tool when the user asks for the price, value, or market cap of a coin like Bitcoin, Ethereum, or Dogecoin.
Args:
coin_id (str): The ID of the coin (e.g., 'bitcoin', 'ethereum', 'solana'). Lowercase only.
currency (str): The target currency (e.g., 'usd', 'eur', 'jpy'). Defaults to 'usd'.
Returns:
str: A formatted string containing the price and 24h change.
"""
url = "https://api.coingecko.com/api/v3/simple/price"
params = {
"ids": coin_id,
"vs_currencies": currency,
"include_24hr_change": "true"
}
try:
response = requests.get(url, params=params)
data = response.json()
if coin_id not in data:
return f"Error: Could not find coin '{coin_id}'. Please check the spelling."
price = data[coin_id][currency]
change = data[coin_id][f"{currency}_24h_change"]
return f"The current price of {coin_id} is {price} {currency.upper()} ({change:.2f}% in 24h)."
except Exception as e:
return f"API Error: {str(e)}"
Key Concepts Breakdown
-
@skillDecorator: This imports fromopenclaw.sdk. It registers the function with the agent's tool registry. -
Type Hints (
coin_id: str): The LLM uses these to understand what kind of data to pass. If you omit these, the agent might guess wrong. -
The Docstring: This is the most critical part. Read it carefully:
"Use this tool when the user asks for the price..."
This natural language description is injected into the LLM's system prompt. It is how the "Brain" knows that
get_crypto_priceexists and is relevant to the user's request.
Step 3: Testing Your Skill
Save the file. Watch your OpenClaw terminal logs. You should see a green message:
[System] Loaded custom skill: get_crypto_price
Now, you can talk to your agent.
User: "How much is Ethereum right now?"
Agent (Internal Thought Process):
User is asking for the price of 'Ethereum'. I have a tool called
get_crypto_price. The docstring says it fetches crypto prices. I should callget_crypto_price(coin_id='ethereum').
Agent (Action): Calls the function.
Output: "The current price of ethereum is 2850 USD (1.2% in 24h)."
Agent (Final Response): "Ethereum is currently trading at $2,850 USD, up 1.2% in the last 24 hours."
Step 4: Handling Complex Logic
What if we want something more advanced? Let's add a parameter for historical data.
The beauty of OpenClaw is that the "Intelligence" handles the logic, and the "Skill" handles the execution.
You don't need to write code to parse "last Tuesday" into a date object. The LLM does that.
Update your function signature:
@skill
def get_historical_price(coin_id: str, date: str) -> str:
"""
Gets the price of a coin on a specific past date.
Args:
coin_id (str): The coin ID.
date (str): The date in DD-MM-YYYY format.
"""
# ... implementation calling CoinGecko history API ...
User: "What was Bitcoin worth on Christmas 2020?"
Agent (Reasoning):
User query: "Christmas 2020". I know Christmas is December 25th. The tool requires DD-MM-YYYY. I will convert "Christmas 2020" to "25-12-2020".
Agent (Action): Calls get_historical_price(coin_id='bitcoin', date='25-12-2020').
This is the power of Neuro-Symbolic AI. The Neural Network (LLM) handles the fuzzy, messy human language, and the Symbolic Code (Python) handles the precise API interaction.
Best Practices for Custom Skills
1. Fail Gracefully
AI agents are persistent. If your code throws a hard exception, the agent might crash or get stuck in a loop. Always wrap your external calls in try/except blocks and return a helpful string error message (like "API timeout" or "Invalid ID") so the agent can try again or apologize to the user.
2. Keep Functions Atomic
Don't write one massive function called do_everything(). Break it down.
get_weather()send_email()read_file()
This allows the agent to chain them together: "Get the weather, write it to a file, and email it to Bob."
3. Use Enums for Strict Options
If an argument only has a few valid options, tell the LLM!
from typing import Literal
def set_light_color(color: Literal["red", "blue", "green"]):
"""Sets the room lighting."""
# ...
This prevents the model from trying to set the light to "super-dark-purple-ish-grey."
Conclusion: You Are The Architect
OpenClaw is designed to be the "Operating System" for your personal AI. And like any OS, it's defined by the applications (skills) you install on it.
By writing just 20 lines of Python, you've given your agent a new sense—the ability to perceive financial markets. What other senses can you give it?
- A tool to check your local bus schedule?
- A script to summarize your unread Git commits?
- A hook to post to your WordPress blog?
The code is the easy part. The limit is your imagination.
Happy coding!




