Introduction
OpenClaw's true power lies in its extensibility. If the Model is the brain, Skills are the hands. While the community registry on ClawdHub contains hundreds of ready-to-use skills, you might need internal integrations tied to your proprietary company APIs, database systems, or localized hardware.
This guide will walk you through building a "WeatherCheck" Skill in Node.js, exposing it to the OpenClaw Agent, and formatting the Model Context Protocol (MCP) correctly.
1. Understanding the Architecture (MCP)
OpenClaw supports Anthropic's open standard: the Model Context Protocol (MCP). This means you don't need to write brittle custom parsers. You build a standard JSON-RPC server (or an HTTP SSE stream) that exposes:
- Resources: Resources: Static context the model can choose to read (e.g., "Company API Docs").
- Tools: Tools: Actions the model can decide to execute (e.g., "fetch_weather_by_city").
2. Project Setup
Let's create a new Node.js package using the official MCP SDK.
mkdir my-first-skill && cd my-first-skill
npm init -y
npm install @modelcontextprotocol/sdk3. Writing the Skill Server
Create an index.js file. We will define an MCP server that uses the stdio transport (the simplest way for OpenClaw to spawn and talk to your local skill).
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Initialize the standard MCP server
const server = new McpServer({
name: "WeatherCheck-Skill",
version: "1.0.0"
});
// Register a Tool that the AI can call
server.tool("get_weather",
"Fetch current weather for a specific city",
{
city: z.string().describe("The name of the city (e.g. London, Beijing)")
},
async ({ city }) => {
console.error(`Fetching hardware sensor or API for: ${city}`);
const mockTemp = Math.floor(Math.random() * 30);
return {
content: [{ type: "text", text: `The weather in ${city} is ${mockTemp}Β°C and Sunny.` }]
};
}
);
// Connect via standard I/O pipes
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("WeatherCheck Skill is running on stdio!");4. Registering the Skill in OpenClaw
Open your ~/.openclaw/config.json (or skills.yml depending on your version), and tell OpenClaw how to boot your new MCP server.
{
"mcpServers": {
"weather_check": {
"command": "node",
"args": ["/absolute/path/to/my-first-skill/index.js"]
}
}
}5. Testing the Integration
Restart your OpenClaw daemon. Ask the AI:
Hey, do I need an umbrella in Paris today?
If properly configured, the LLM will see the get_weather tool in its context, generate the JSON calling arguments for "Paris", and your index.js will return the mock text. The LLM will then read the text and reply to you naturally.
What a skill actually is
The word suggests something more elaborate than it is. A skill is a Markdown file with two required frontmatter fields, describing to the model when it should act and what to do. There is no plugin API to learn and nothing to compile.
| Field | Required | What it does |
|---|---|---|
| name | Yes | Lowercase letters, digits and hyphens. This, not the folder name, is the skill's identity. |
| description | Yes | One line under 160 characters. The model reads this when deciding whether the skill is relevant at all. |
| user-invocable | No | Whether it appears as a slash command. Defaults to true. |
| disable-model-invocation | No | Keeps it out of the system prompt so only you can trigger it. |
| homepage | No | Shown during skill discovery. |
The description does more work than anything else in the file. It is the only part the model sees when deciding whether to reach for the skill, so "Formats release notes from a git log" gets used and "Release notes helper" does not. Write it as the answer to "when should I use this?" rather than as a title.
Writing one and getting it loaded
The whole loop is four commands, and the only part that trips people up is the last one: skills are read when a session starts, so an existing conversation will not see a file you just wrote.
mkdir -p ~/.openclaw/workspace/skills/hello-world cd ~/.openclaw/workspace/skills/hello-world
Nesting is free β `personal/my-skill/SKILL.md` works as well as a top-level folder, because identity comes from the frontmatter rather than the path.
cat > SKILL.md <<'MD' --- name: hello-world description: A simple skill that prints a greeting. --- # Hello World When the user asks for a greeting, use the `exec` tool to run: ```bash echo "Hello from your custom skill!" ``` MD
The body is plain Markdown addressed to the model. Describe the trigger condition first and the procedure second; a skill that explains what to do but not when to do it will sit unused.
# Skills are picked up on a fresh session, not live. # In a chat, start one with: /new # # Or test it from the shell: openclaw agent --message "give me a greeting"
This is the step people miss. The gateway watches these files, but an in-flight session has already assembled its prompt. `/new` is what makes the skill visible.
# Invoke it directly by name instead of hoping it gets chosen: # /skill hello-world
If `/skill hello-world` works but asking in natural language does not, the skill is fine and the description is the problem. That distinction saves a lot of guessing.
Installing other people's, and publishing yours
ClawHub is the public registry. Two different tools are involved and mixing them up is the usual first stumble: `openclaw skills` finds and installs, while the separate `clawhub` CLI handles authenticated operations like publishing.
npm i -g clawhub clawhub login
Publishing needs an account, so it lives in its own tool rather than in the agent's CLI. Note that `openclaw skills` has no publish subcommand β looking for one is a common dead end.
clawhub skill publish ./my-skill --slug my-skill
The path is the folder containing SKILL.md. `--slug`, `--version`, `--changelog` and `--tags` are available; the registry tracks versions, downloads and a security scan summary.
openclaw skills search "calendar" openclaw skills install @openclaw/demo openclaw skills update --all
These go into your active workspace and record where they came from, which is what makes `skills update --all` able to do anything sensible later.
Before you install a skill someone else wrote
- βA skill is instructions to an agent that can run commands. Read the SKILL.md before installing it, the same way you would read a shell script before piping it to bash.
- βThe registry shows a security scan summary. That is a signal, not a guarantee, and it says nothing about whether the instructions are a good idea for your setup.
- βSkills inherit your tool policy. A skill telling the agent to run something is still bounded by tools.allow and tools.deny β which is the layer worth getting right before you install anything.
- βA skill that never triggers is almost always a description problem, not a loading problem. Test with /skill first to tell the two apart.
Publishing to ClawdHub
Once your skill is polished and tested, package your repository and submit a Pull Request to the Starmadebydata/clawdhub-registry. Community adoption starts here! For more details, see the official publishing guidelinesγ