
OpenClaw có sẵn vài chục skill, nhưng cái hay nhất của nó là bạn viết được skill của riêng mình bằng JavaScript hoặc TypeScript trong vài chục dòng code. Agent sẽ tự gọi skill khi context thấy phù hợp - giống tool-calling kiểu MCP nhưng nhẹ hơn nhiều. Bài này hướng dẫn viết một custom skill A-Z: structure, schema, handler, test, đóng gói.
Skill OpenClaw khác gì MCP, function-calling, Claude tool-use?
Tất cả là tool-use, nhưng khác về layer:
| Pattern | Layer | Format |
|---|---|---|
| OpenAI function-calling | API request | JSON Schema per request |
| Claude tool-use | API request | JSON Schema per request |
| MCP (Model Context Protocol) | Server process bên ngoài | JSON-RPC qua stdio/SSE |
| OpenClaw Skill | Folder local trong gateway | SKILL.md (markdown + YAML) + script optional |
Skill OpenClaw là layer trên cùng: agent đọc SKILL.md để hiểu "khi nào dùng skill này", rồi gọi index.js với input theo schema, nhận output, trả về user. Không cần spawn process riêng như MCP, không cần inject schema mỗi request như function-call thuần.
Cấu trúc một skill chuẩn
~/.openclaw/skills/vn-stock-quote/
├── SKILL.md (bắt buộc)
├── tool.json (optional, JSON Schema)
├── index.js (handler chính)
├── package.json (optional, nếu có dep ngoài)
├── scripts/
│ └── fetch-vps-list.sh
├── references/
│ └── api-docs.md
└── assets/
└── icon.png
Chỉ SKILL.md là bắt buộc. Skill có thể là 100% markdown (skill "kiến thức" - chỉ hướng dẫn agent dùng tool sẵn có) hoặc có index.js (skill có code chạy thật).
Skill mẫu: lấy giá cổ phiếu VN từ vnstock API
Build skill vn-stock-quote: user hỏi "VN30 hôm nay sao?" hay "giá VIC bao nhiêu?", agent tự gọi skill, trả về giá realtime.
Bước 1: Tạo folder skill
mkdir -p ~/.openclaw/skills/vn-stock-quote
cd ~/.openclaw/skills/vn-stock-quote
Bước 2: Viết SKILL.md
---
name: vn-stock-quote
description: |
Tra cứu giá cổ phiếu Việt Nam (HOSE, HNX, UPCOM) realtime.
Dùng skill này khi user hỏi về giá mã chứng khoán VN, index VN30, HNX-Index,
hoặc cần snapshot thị trường VN trong giờ giao dịch.
metadata:
openclaw:
runtime: node
permissions: [network]
allowedHosts: ["api.vnstock.vn"]
cacheStrategy: ttl-60s
---
# Khi nào dùng
Trigger khi user:
- Hỏi giá một mã VN cụ thể: "giá FPT", "VIC bao nhiêu", "HPG hôm nay"
- Hỏi index: "VN30", "VN-Index", "HNX-Index"
- Hỏi top gainer/loser trong phiên
KHÔNG dùng khi:
- User hỏi về mã US/quốc tế (AAPL, TSLA) - dùng skill yfinance thay.
- User hỏi phân tích kỹ thuật sâu - skill này chỉ trả giá raw.
# Input schema
\`\`\`json
{
"type": "object",
"properties": {
"symbol": {
"type": "string",
"description": "Mã chứng khoán VN viết hoa, ví dụ FPT, VIC, VN30"
},
"metrics": {
"type": "array",
"items": { "enum": ["price", "change", "volume", "high", "low"] },
"default": ["price", "change"]
}
},
"required": ["symbol"]
}
\`\`\`
# Output
JSON với field price (VND), change (%), volume (cổ phiếu), timestamp ICT.
Nếu ngoài giờ giao dịch, agent phải nói rõ "giá đóng cửa phiên trước".
# Lỗi thường gặp
- Mã không tồn tại: skill trả error code SYMBOL_NOT_FOUND. Agent nói "Mã X không có trên sàn VN, bạn check lại nhé."
- Ngoài giờ giao dịch (giờ giao dịch HOSE: 9h00-11h30, 13h00-14h45 ICT): skill vẫn trả nhưng đánh dấu stale=true.
Bước 3: Viết handler index.js
// ~/.openclaw/skills/vn-stock-quote/index.js
const VNSTOCK_BASE = "https://api.vnstock.vn/v1";
/**
* OpenClaw skill handler.
* @param {object} input - validated theo schema trong SKILL.md
* @param {object} ctx - context từ gateway: cache, logger, env
*/
export async function handle(input, ctx) {
const { symbol, metrics = ["price", "change"] } = input;
const cacheKey = `vnstock:${symbol}:${metrics.join(",")}`;
// Cache 60s tránh spam API
const cached = await ctx.cache.get(cacheKey);
if (cached) return cached;
const url = `${VNSTOCK_BASE}/quote/${encodeURIComponent(symbol)}`;
ctx.logger.info({ symbol, url }, "fetching vnstock");
let res;
try {
res = await fetch(url, {
headers: { "User-Agent": "openclaw-vn-stock-quote/1.0" },
signal: AbortSignal.timeout(5000),
});
} catch (e) {
return { error: "NETWORK_ERROR", message: e.message };
}
if (res.status === 404) {
return { error: "SYMBOL_NOT_FOUND", symbol };
}
if (!res.ok) {
return { error: "UPSTREAM_ERROR", status: res.status };
}
const data = await res.json();
const tradingHour = isTradingHourICT();
const result = {
symbol,
timestamp: new Date().toISOString(),
stale: !tradingHour,
price: data.last_price,
change: data.change_percent,
volume: data.total_volume,
high: data.high_price,
low: data.low_price,
};
// Filter chỉ metrics user request
const filtered = { symbol: result.symbol, stale: result.stale, timestamp: result.timestamp };
for (const m of metrics) filtered[m] = result[m];
await ctx.cache.set(cacheKey, filtered, { ttl: 60 });
return filtered;
}
function isTradingHourICT() {
const now = new Date();
const ict = new Date(now.getTime() + 7 * 3600 * 1000);
const day = ict.getUTCDay();
if (day === 0 || day === 6) return false; // weekend
const hour = ict.getUTCHours();
const min = ict.getUTCMinutes();
const t = hour * 60 + min;
return (t >= 540 && t <= 690) || (t >= 780 && t <= 885);
}
Bước 4: Khai báo package.json (nếu cần)
{
"name": "openclaw-skill-vn-stock-quote",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"engines": { "node": ">=22.16" }
}
Skill này không cần dep ngoài (chỉ fetch built-in của Node 22+), nên không cần npm install. Nếu skill bạn dùng cheerio, axios, hay zod, chạy npm install trong folder skill.
Test skill từ CLI
OpenClaw có CLI openclaw skills exec để test mà không cần qua chat:
openclaw skills exec vn-stock-quote --input '{"symbol":"FPT","metrics":["price","change","volume"]}'
Output dạng:
{
"symbol": "FPT",
"stale": false,
"timestamp": "2026-05-29T03:42:11.234Z",
"price": 142500,
"change": 1.42,
"volume": 2384700
}
Test edge case:
# Mã không tồn tại
openclaw skills exec vn-stock-quote --input '{"symbol":"ZZZ"}'
# {"error":"SYMBOL_NOT_FOUND","symbol":"ZZZ"}
# Test timeout (giả lập)
OPENCLAW_FAKE_NETWORK=slow openclaw skills exec vn-stock-quote --input '{"symbol":"FPT"}'
Register skill vào agent
Skill nằm trong ~/.openclaw/skills/ được auto-discover. Verify:
openclaw skills list
# vn-stock-quote (local, enabled)
# web-search (registry)
# ...
Nếu skill bị disable, bật:
openclaw skills enable vn-stock-quote
Test end-to-end qua chat Telegram/Discord: gửi "giá FPT bao nhiêu?". Agent đọc skill description, match, gọi handler, format kết quả tiếng Việt cho user.
Permissions và sandbox
OpenClaw chạy skill trong sandbox VM2-like, hạn chế quyền theo declare trong frontmatter:
metadata:
openclaw:
permissions: [network, fs-read]
allowedHosts: ["api.vnstock.vn"]
allowedPaths: ["/var/data/vnstock"]
timeout: 5000
memoryLimit: 128MB
Permission có sẵn:
network: được fetch, nhưng chỉ tớiallowedHostsfs-read: đọc file trongallowedPathsfs-write: ghi fileexec: spawn subprocess (cẩn thận, ăn RAM)secrets: truy cập~/.openclaw/secrets.jsonvới scope
exec + network = có thể exfil data toàn bộ. Đọc kỹ permissions trước khi install skill từ registry.Skill dùng secret (API key)
Nếu skill cần API key (ví dụ OpenWeather), không hardcode. Lưu vào secrets vault:
openclaw secrets set openweather.apiKey
# prompt nhập key, lưu encrypted vào ~/.openclaw/secrets.enc
Trong skill access qua ctx.secrets:
export async function handle(input, ctx) {
const apiKey = await ctx.secrets.get("openweather.apiKey");
if (!apiKey) return { error: "MISSING_API_KEY" };
// ... gọi API
}
SKILL.md phải declare:
metadata:
openclaw:
permissions: [network, secrets]
requiredSecrets: ["openweather.apiKey"]
Khi user enable skill mà chưa set secret, gateway báo lỗi rõ và nhắc set.
Package và share skill
Khi skill ổn, đóng gói:
cd ~/.openclaw/skills/vn-stock-quote
openclaw skills package
# tạo vn-stock-quote-0.1.0.tar.gz
Push lên GitHub. Người khác install:
openclaw skills install github:tnduyen/openclaw-skill-vn-stock-quote
Nếu skill public ổn, submit vào ClawHub registry để hiện trong openclaw skills search của mọi user.
Gotchas khi viết skill
- Description quan trọng hơn bạn nghĩ: agent dựa vào
descriptionđể quyết định gọi skill nào. Mô tả mơ hồ = agent gọi sai hoặc không gọi. Viết cụ thể "khi nào dùng, khi nào KHÔNG dùng". - Tham số default: declare default trong schema, đừng đợi agent generate đầy đủ - tốn token.
- Return error rõ ràng: trả
{error: "CODE", message: "human readable"}, agent sẽ relay cho user. Throw exception là gateway log + agent confused. - Cache aggressively: skill gọi API ngoài tốn latency. Set
ctx.cache30-300s tùy data. - Timeout: luôn set
AbortSignal.timeouttrong fetch. Skill treo >10s sẽ block agent. - Đừng dùng console.log: dùng
ctx.logger.info/warn/error, log sẽ vào journald của gateway, có trace ID match với chat. - Schema validation chặt: nếu input không khớp schema, return error sớm, đừng để skill crash giữa chừng.
So sánh với MCP server
Nếu skill bạn cần state phức tạp (database, long-running process, GPU), MCP server thường ngon hơn. Skill OpenClaw hợp cho stateless tool, fetch API, transform data, simple I/O. Tham khảo bài OpenClaw là gì để xem các pattern khác.
Skill markdown-only (không cần code)
Đôi khi skill bạn cần chỉ là "hướng dẫn agent cách dùng tool có sẵn". Không cần handler JS. Ví dụ skill vn-time-format:
---
name: vn-time-format
description: Format thời gian theo phong cách Việt Nam (DD/MM/YYYY, 24h, ICT)
---
# Khi nào dùng
Mọi lúc agent cần in datetime cho user VN. Override default ISO format.
# Quy tắc
- Ngày: DD/MM/YYYY (15/05/2026), KHÔNG dùng MM/DD/YYYY.
- Giờ: HH:mm 24h (18:30), KHÔNG dùng AM/PM.
- Timezone: ICT (UTC+7), suy ra "lúc 14h30 chiều thứ Bảy".
- Tương đối: "hôm nay", "ngày mai", "thứ Hai tuần sau" khi context cho phép.
- Năm âm lịch: bonus, ví dụ "5/5 âm lịch (Tết Đoan Ngọ)" khi liên quan lễ tết.
# KHÔNG
- Không in epoch timestamp cho user thường.
- Không dùng "next Friday", "in 3 weeks" - dịch ra tiếng Việt.
Skill này không có index.js, chỉ là markdown. Agent đọc rule và apply mỗi khi cần format date. Nhẹ, không tốn runtime, vẫn cải thiện UX tiếng Việt rõ rệt.
Workflow chain skill nhiều bước
OpenClaw cho phép define workflow YAML để chain nhiều skill thành pipeline cố định:
# ~/.openclaw/workflows/morning-brief.yaml
name: morning-brief
trigger: schedule
schedule: "0 7 * * MON-FRI" # 7h sáng thứ Hai-thứ Sáu
steps:
- skill: calendar.list-today
output: events
- skill: gmail.unread-important
output: emails
- skill: vnstock.snapshot
input: { symbols: ["VN30", "FPT", "VIC"] }
output: market
- skill: weather.hanoi
output: weather
- skill: llm.compose
input:
template: |
Tóm tắt sáng cho anh:
- Lịch: {{events}}
- Mail quan trọng: {{emails}}
- Thị trường: {{market}}
- Thời tiết HN: {{weather}}
style: casual-vn
output: brief
- skill: telegram.send
input: { chat: "owner", text: "{{brief}}" }
Workflow này chạy mỗi sáng 7h, gửi brief vào Telegram cá nhân. Không cần agent quyết định, không tốn token reasoning - tiết kiệm và đáng tin.
Tóm lại
Viết skill OpenClaw: tạo folder, viết SKILL.md (description rõ), code handler trả JSON, test bằng openclaw skills exec, push lên git. Toàn bộ workflow trong 20-30 phút cho skill đơn giản.
Một VPS 4GB (gói VPS 50) chạy được vài chục skill local. Skill nặng (browser automation, code execution) nên VPS 6GB (VPS 80). Đặt VPS ở VN nếu skill gọi API VN nhiều như vnstock, GHN, MoMo - latency thấp hơn 50ms so với host nước ngoài.
Đọc thêm VPS cho vibe-coder để hiểu tổng thể self-host AI stack.
Bài viết liên quan
VPS chạy OpenClaw + multi-skill, đủ RAM cho browser automation
TND Cloud VPS Ceph SSD Enterprise NVMe, RAM ECC, khởi tạo 60 giây. Gói 4GB RAM 639k/tháng đủ cho 10+ skill stateless; gói 6GB RAM 999k/tháng cho skill nặng kèm browser hoặc code-exec.


