Skip to main content
Skills are pluggable tool bundles that extend agent capabilities. A skill packages one or more tools with their implementation, letting agents do things that built-in tools don’t cover.

Overview

A skill consists of:
  1. Manifest (skill.toml or SKILL.md) - Declares metadata, runtime, tools, and requirements
  2. Entry Point - Python script, WASM module, Node.js module, or prompt-only Markdown
OpenFang ships with 60 bundled skills compiled into the binary and available immediately.

Supported Runtimes

Python

Easiest to write
  • Uses stdin/stdout JSON protocol
  • Not sandboxed (subprocess with env_clear())
  • Supports Python 3.8+

WASM

Most secure
  • Fully sandboxed (Wasmtime dual metering)
  • Resource limits enforced
  • Compile from Rust, C, Go, etc.

Node.js

OpenClaw compatible
  • JavaScript/TypeScript support
  • Not sandboxed (subprocess)
  • Auto-converts OpenClaw skills

Prompt Only

Expert knowledge
  • Markdown-based
  • Injected into system prompt
  • No code execution
  • 60 bundled skills included

Bundled Skills

OpenFang includes 60 expert knowledge skills compiled into the binary:
CategorySkills
DevOps & Infraci-cd, ansible, prometheus, nginx, kubernetes, terraform, helm, docker, sysadmin, shell-scripting, linux-networking
Cloudaws, gcp, azure
Languagesrust-expert, python-expert, typescript-expert, golang-expert
Frontendreact-expert, nextjs-expert, css-expert
Databasespostgres-expert, redis-expert, sqlite-expert, mongodb, elasticsearch, sql-analyst
APIs & Webgraphql-expert, openapi-expert, api-tester, oauth-expert
AI/MLml-engineer, llm-finetuning, vector-db, prompt-engineer
Securitysecurity-audit, crypto-expert, compliance
Dev Toolsgithub, git-expert, jira, linear-tools, sentry, code-reviewer, regex-expert
Writingtechnical-writer, writing-coach, email-writer, presentation
Datadata-analyst, data-pipeline
Collaborationslack-tools, notion, confluence, figma-expert
Careerinterview-prep, project-manager
Advancedwasm-expert, pdf-reader, web-search

Skill Manifest Format

Directory Structure

my-skill/
  skill.toml          # Manifest (required)
  src/
    main.py           # Entry point
  README.md           # Optional documentation

skill.toml

skill.toml
[skill]
name = "web-summarizer"
version = "0.1.0"
description = "Summarizes any web page into bullet points"
author = "openfang-community"
license = "MIT"
tags = ["web", "summarizer", "research"]

[runtime]
type = "python"           # python | wasm | node | builtin
entry = "src/main.py"

[[tools.provided]]
name = "summarize_url"
description = "Fetch a URL and return a concise bullet-point summary"
input_schema = { 
  type = "object", 
  properties = { 
    url = { type = "string", description = "The URL to summarize" } 
  }, 
  required = ["url"] 
}

[[tools.provided]]
name = "extract_links"
description = "Extract all links from a web page"
input_schema = { 
  type = "object", 
  properties = { 
    url = { type = "string" } 
  }, 
  required = ["url"] 
}

[requirements]
tools = ["web_fetch"]
capabilities = ["NetConnect(*)"]

Creating a Python Skill

Python skills communicate via JSON over stdin/stdout.

Protocol

1

OpenFang sends JSON to stdin

{
  "tool": "summarize_url",
  "input": {
    "url": "https://example.com"
  },
  "agent_id": "uuid-...",
  "agent_name": "researcher"
}
2

Script processes and writes result to stdout

{
  "result": "- Point one\n- Point two\n- Point three"
}
Or on error:
{
  "error": "Failed to fetch URL: connection refused"
}

Example: Web Summarizer

src/main.py
#!/usr/bin/env python3
"""OpenFang skill: web-summarizer"""
import json
import sys
import urllib.request


def summarize_url(url: str) -> str:
    """Fetch a URL and return a basic summary."""
    req = urllib.request.Request(url, headers={"User-Agent": "OpenFang-Skill/1.0"})
    with urllib.request.urlopen(req, timeout=30) as resp:
        content = resp.read().decode("utf-8", errors="replace")

    # Simple extraction: first 500 chars as summary
    text = content[:500].strip()
    return f"Summary of {url}:\n{text}..."


def extract_links(url: str) -> str:
    """Extract all links from a web page."""
    import re

    req = urllib.request.Request(url, headers={"User-Agent": "OpenFang-Skill/1.0"})
    with urllib.request.urlopen(req, timeout=30) as resp:
        content = resp.read().decode("utf-8", errors="replace")

    links = re.findall(r'href="(https?://[^"]+)"', content)
    unique_links = list(dict.fromkeys(links))
    return "\n".join(unique_links[:50])


def main():
    payload = json.loads(sys.stdin.read())
    tool_name = payload["tool"]
    input_data = payload["input"]

    try:
        if tool_name == "summarize_url":
            result = summarize_url(input_data["url"])
        elif tool_name == "extract_links":
            result = extract_links(input_data["url"])
        else:
            print(json.dumps({"error": f"Unknown tool: {tool_name}"}))
            return

        print(json.dumps({"result": result}))
    except Exception as e:
        print(json.dumps({"error": str(e)}))


if __name__ == "__main__":
    main()

Using the OpenFang Python SDK

For cleaner code, use the Python SDK:
#!/usr/bin/env python3
from openfang_sdk import SkillHandler

handler = SkillHandler()

@handler.tool("summarize_url")
def summarize_url(url: str) -> str:
    # Your implementation here
    return "Summary..."

@handler.tool("extract_links")
def extract_links(url: str) -> str:
    # Your implementation here
    return "link1\nlink2"

if __name__ == "__main__":
    handler.run()

Creating a WASM Skill

WASM skills run in a sandboxed Wasmtime environment with enforced resource limits.

Building a WASM Skill

1

Write your skill in Rust

src/lib.rs
use std::io::{self, Read};

#[no_mangle]
pub extern "C" fn _start() {
    let mut input = String::new();
    io::stdin().read_to_string(&mut input).unwrap();

    let payload: serde_json::Value = serde_json::from_str(&input).unwrap();
    let tool = payload["tool"].as_str().unwrap_or("");
    let input_data = &payload["input"];

    let result = match tool {
        "my_tool" => {
            let param = input_data["param"].as_str().unwrap_or("");
            format!("Processed: {param}")
        }
        _ => format!("Unknown tool: {tool}"),
    };

    println!("{}", serde_json::json!({"result": result}));
}
2

Compile to WASM

cargo build --target wasm32-wasi --release
3

Reference in manifest

skill.toml
[runtime]
type = "wasm"
entry = "target/wasm32-wasi/release/my_skill.wasm"

Sandbox Limits

The WASM sandbox enforces:
  • Fuel limit - Maximum computation steps (prevents infinite loops)
  • Memory limit - Maximum memory allocation
  • Capabilities - Only capabilities granted to the agent apply
Limits are derived from the agent’s [resources] section in its manifest.

SKILL.md Format

The SKILL.md format uses YAML frontmatter and Markdown body:
SKILL.md
---
name: rust-expert
description: Expert Rust programming knowledge
---

# Rust Expert

## Key Principles
- Ownership and borrowing rules...
- Lifetime annotations...

## Common Patterns
...
All SKILL.md files pass through an automated prompt injection scanner that detects override attempts, data exfiltration patterns, and shell references before inclusion.

Installing Skills

From Local Directory

openfang skill install /path/to/my-skill
Reads the skill.toml, validates the manifest, and copies to ~/.openfang/skills/my-skill/.

From FangHub

openfang skill install web-summarizer
Downloads from the FangHub marketplace registry.

From Git Repository

openfang skill install https://github.com/user/openfang-skill-example.git

List Installed Skills

openfang skill list
Output:
3 skill(s) installed:

NAME                 VERSION    TOOLS    DESCRIPTION
----------------------------------------------------------------------
web-summarizer       0.1.0      2        Summarizes any web page
data-analyzer        0.2.1      3        Statistical analysis tools
code-formatter       1.0.0      1        Format code in 20+ languages

Remove Skills

openfang skill remove web-summarizer

Using Skills in Agents

Reference skills in the agent manifest’s skills field:
agent.toml
name = "my-assistant"
version = "0.1.0"
description = "An assistant with extra skills"
author = "openfang"
module = "builtin:chat"
skills = ["web-summarizer", "data-analyzer"]

[model]
provider = "groq"
model = "llama-3.3-70b-versatile"

[capabilities]
tools = ["file_read", "web_fetch", "summarize_url"]
memory_read = ["*"]
memory_write = ["self.*"]
The kernel loads skill tools and prompts at agent spawn time.

Publishing to FangHub

FangHub is the community skill marketplace for OpenFang.

Preparing Your Skill

1

Complete metadata

Ensure skill.toml has: name, version, description, author, license, tags
2

Add documentation

Include a README.md with usage instructions
3

Test locally

openfang skill install /path/to/my-skill
# Spawn an agent and test the skill's tools

Searching FangHub

openfang skill search "web scraping"
Output:
Skills matching "web scraping":

  web-summarizer (42 stars)
    Summarizes any web page into bullet points
    https://fanghub.dev/skills/web-summarizer

  page-scraper (28 stars)
    Extract structured data from web pages
    https://fanghub.dev/skills/page-scraper

Publishing

Publishing to FangHub (coming soon):
openfang skill publish
Validates manifest, packages the skill, and uploads to the registry.

OpenClaw Compatibility

OpenFang can install and run OpenClaw-format skills.

Automatic Conversion

openfang skill install /path/to/openclaw-skill
If the directory contains an OpenClaw-style skill (Node.js package), OpenFang:
  1. Detects the OpenClaw format
  2. Generates a skill.toml from package.json
  3. Maps tool names to OpenFang conventions
  4. Copies the skill to the OpenFang skills directory

Manual Conversion

If automatic conversion doesn’t work, create a skill.toml manually:
skill.toml
[skill]
name = "my-openclaw-skill"
version = "1.0.0"
description = "Converted from OpenClaw"

[runtime]
type = "node"
entry = "index.js"

[[tools.provided]]
name = "my_tool"
description = "Tool description"
input_schema = { 
  type = "object", 
  properties = { 
    input = { type = "string" } 
  }, 
  required = ["input"] 
}
Skills imported via openfang migrate --from openclaw are scanned and reported in the migration report with reinstallation instructions.

Best Practices

Keep it Focused

One skill should do one thing well

Minimal Requirements

Only request the tools and capabilities you actually need

Descriptive Names

The LLM reads tool names and descriptions to decide when to use them

Clear Schemas

Include descriptions for every parameter so the LLM knows what to pass

Handle Errors

Always return JSON error objects rather than crashing

Version Carefully

Use semantic versioning; breaking changes require major version bumps

Test Thoroughly

Verify your skill works with different agent templates and providers

Document Well

Include setup steps, dependencies, and example usage

CLI Commands

# Install a skill (local directory, FangHub name, or git URL)
openfang skill install <source>

# List all installed skills
openfang skill list

# Remove an installed skill
openfang skill remove <name>

# Search FangHub for skills
openfang skill search <query>

# Create a new skill scaffold (interactive)
openfang skill create

Creating a Skill Scaffold

openfang skill create
Interactive prompts for:
  • Skill name
  • Description
  • Runtime type (python/node/wasm)
Generates:
~/.openfang/skills/my-skill/
  skill.toml        # Pre-filled manifest
  src/
    main.py         # Starter entry point

Next Steps

Creating Agents

Build custom agents that use your skills

API Reference

Skill API endpoint documentation