OpenClaw skills: viết custom skill bằng JavaScript cho agent của bạn

Chia sẻ bài viết

Mục lục
Minh hoạ viết custom skill JavaScript cho OpenClaw

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ới allowedHosts
  • fs-read: đọc file trong allowedPaths
  • fs-write: ghi file
  • exec: spawn subprocess (cẩn thận, ăn RAM)
  • secrets: truy cập ~/.openclaw/secrets.json với scope
⚠️ Lưu ý: Đừng cấp permission "all" cho skill third-party. Skill có quyền 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.cache 30-300s tùy data.
  • Timeout: luôn set AbortSignal.timeout trong 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.

Xem bảng giá VPS