Developing Skills
Learn how to create your own skills for Skill Engine using native binaries, Docker containers, or WASM components.
Skill Types
| Type | Best For | Complexity | Performance |
|---|---|---|---|
| Native | CLI tools, system commands | Low | Fastest |
| Docker | Complex dependencies, isolation | Medium | Good |
| WASM | Portable, sandboxed code | High | Very Fast |
Skill Structure
Every skill needs:
- Manifest (
SKILL.md) - Describes tools and parameters - Executables - Native binaries, Docker images, or WASM modules
- Configuration (optional) -
.skill-engine.tomlfor registration
Native Skills
Native skills wrap existing CLI tools or scripts.
Example: kubectl Wrapper
Directory structure:
kubernetes-skill/
├── SKILL.md
└── tools/
├── get.sh
├── logs.sh
├── describe.sh
└── apply.shSKILL.md:
---
name: kubernetes
version: 1.0.0
runtime: native
description: Kubernetes cluster management with kubectl
---
# Kubernetes Skill
Manage Kubernetes clusters using kubectl commands.
## Tools
### get
Get Kubernetes resources
Parameters:
- resource (required): Resource type (pods, services, deployments)
- namespace (optional): Namespace (default: default)
- output (optional): Output format (json, yaml, wide)
### logs
View pod logs
Parameters:
- pod (required): Pod name
- namespace (optional): Namespace
- tail (optional): Number of lines to show
- follow (optional): Follow log output
### describe
Describe a Kubernetes resource
Parameters:
- resource (required): Resource type
- name (required): Resource name
- namespace (optional): Namespacetools/get.sh:
#!/bin/bash
set -euo pipefail
# Parse parameters from Skill Engine
RESOURCE="${resource}"
NAMESPACE="${namespace:-default}"
OUTPUT="${output:-wide}"
# Execute kubectl
kubectl get "$RESOURCE" \
--namespace="$NAMESPACE" \
--output="$OUTPUT"tools/logs.sh:
#!/bin/bash
set -euo pipefail
POD="${pod}"
NAMESPACE="${namespace:-default}"
TAIL="${tail:-100}"
FOLLOW="${follow:-false}"
CMD="kubectl logs $POD --namespace=$NAMESPACE --tail=$TAIL"
if [ "$FOLLOW" = "true" ]; then
CMD="$CMD --follow"
fi
eval "$CMD"Register Native Skill
.skill-engine.toml:
[skills.kubernetes]
source = "./kubernetes-skill"
runtime = "native"
description = "Kubernetes cluster management"
[skills.kubernetes.instances.default]
capabilities.allowed_commands = ["kubectl", "helm"]
capabilities.timeout_ms = 30000Test Native Skill
# Run the skill
skill run kubernetes get --resource pods --namespace default
# Check logs
skill run kubernetes logs --pod nginx-xxx --tail 50Docker Skills
Docker skills run in isolated containers with all dependencies included.
Example: FFmpeg Video Processing
Directory structure:
ffmpeg-skill/
├── SKILL.md
└── Dockerfile (optional)SKILL.md:
---
name: ffmpeg
version: 1.0.0
runtime: docker
docker_image: linuxserver/ffmpeg:latest
docker_entrypoint: ffmpeg
description: Video and audio processing with FFmpeg
---
# FFmpeg Skill
Process video and audio files using FFmpeg in an isolated container.
## Tools
### convert
Convert video format
Parameters:
- input (required): Input file path
- output (required): Output file path
- format (optional): Output format (mp4, webm, avi)
- quality (optional): Quality preset (high, medium, low)
### extract-audio
Extract audio from video
Parameters:
- input (required): Input video file
- output (required): Output audio file
- codec (optional): Audio codec (mp3, aac, flac)
### create-thumbnail
Create video thumbnail
Parameters:
- input (required): Input video file
- output (required): Output image file
- timestamp (optional): Timestamp (default: 00:00:05)Register Docker Skill:
.skill-engine.toml:
[skills.ffmpeg]
source = "docker:linuxserver/ffmpeg:latest"
runtime = "docker"
description = "Video processing with FFmpeg"
[skills.ffmpeg.docker]
image = "linuxserver/ffmpeg:latest"
entrypoint = "ffmpeg"
volumes = ["${PWD}:/workdir"]
working_dir = "/workdir"
[skills.ffmpeg.instances.default]
capabilities.filesystem_access = true
capabilities.allowed_paths = ["${PWD}"]
capabilities.timeout_ms = 300000 # 5 minutes for video processingUse Docker Skill
# Convert video
skill run ffmpeg convert --input video.mp4 --output video.webm
# Extract audio
skill run ffmpeg extract-audio --input video.mp4 --output audio.mp3
# Create thumbnail
skill run ffmpeg create-thumbnail --input video.mp4 --output thumb.jpg --timestamp 00:00:10Custom Docker Image
Dockerfile:
FROM python:3.11-slim
# Install dependencies
RUN pip install requests pandas numpy
# Copy skill code
COPY tools/ /app/tools/
WORKDIR /app
# Entrypoint
ENTRYPOINT ["python"]Build and register:
# Build image
docker build -t my-python-skill:latest .
# Register in manifest
cat >> .skill-engine.toml <<EOF
[skills.my-python-skill]
source = "docker:my-python-skill:latest"
runtime = "docker"
[skills.my-python-skill.docker]
image = "my-python-skill:latest"
entrypoint = "python"
volumes = ["\${PWD}:/data"]
working_dir = "/data"
EOFWASM Skills
WASM skills are portable, sandboxed, and very fast.
Experimental
The WASM Skill SDK is currently experimental and the API may change in future releases. Use in production at your own risk.
Prerequisites
- Rust 1.75+ (rustup.rs)
wasm32-wasitarget:rustup target add wasm32-wasiwasm-pack:cargo install wasm-pack
Example: GitHub API Skill
Directory structure:
github-wasm-skill/
├── Cargo.toml
├── SKILL.md
└── src/
└── lib.rsCargo.toml:
[package]
name = "github-skill"
version = "1.0.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
# EXPERIMENTAL: API may change
skill-wasm-sdk = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.11", features = ["blocking", "json"] }
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Better optimizationsrc/lib.rs:
use skill_wasm_sdk::{skill_tool, ToolResult};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct ListReposParams {
owner: String,
#[serde(default)]
per_page: u32,
}
#[derive(Serialize)]
struct Repository {
name: String,
full_name: String,
description: Option<String>,
html_url: String,
stargazers_count: u32,
}
/// List GitHub repositories for a user
#[skill_tool]
fn list_repos(params: ListReposParams) -> ToolResult {
let url = format!(
"https://api.github.com/users/{}/repos?per_page={}",
params.owner,
if params.per_page == 0 { 30 } else { params.per_page }
);
let response = reqwest::blocking::get(&url)?;
let repos: Vec<Repository> = response.json()?;
ToolResult::success(serde_json::to_value(repos)?)
}
#[derive(Deserialize)]
struct CreateIssueParams {
owner: String,
repo: String,
title: String,
body: Option<String>,
}
/// Create a GitHub issue
#[skill_tool]
fn create_issue(params: CreateIssueParams) -> ToolResult {
let url = format!(
"https://api.github.com/repos/{}/{}/issues",
params.owner, params.repo
);
let client = reqwest::blocking::Client::new();
let response = client
.post(&url)
.json(&serde_json::json!({
"title": params.title,
"body": params.body.unwrap_or_default()
}))
.send()?;
ToolResult::success(response.json()?)
}SKILL.md:
---
name: github
version: 1.0.0
runtime: wasm
description: GitHub API integration
---
# GitHub Skill
Interact with GitHub repositories and issues via the GitHub API.
## Tools
### list_repos
List repositories for a user
Parameters:
- owner (required): GitHub username
- per_page (optional): Number of repos per page (default: 30)
### create_issue
Create a new issue
Parameters:
- owner (required): Repository owner
- repo (required): Repository name
- title (required): Issue title
- body (optional): Issue descriptionBuild WASM Skill
# Build for wasm32-wasi target
cargo build --target wasm32-wasi --release
# WASM module is at:
# target/wasm32-wasi/release/github_skill.wasmRegister WASM Skill
.skill-engine.toml:
[skills.github]
source = "./github-wasm-skill/target/wasm32-wasi/release/github_skill.wasm"
runtime = "wasm"
description = "GitHub API integration"
[skills.github.instances.default]
env.GITHUB_TOKEN = "${GITHUB_TOKEN}"
capabilities.network_access = true
capabilities.allowed_domains = ["api.github.com", "github.com"]
capabilities.max_memory_mb = 128
capabilities.timeout_ms = 30000Use WASM Skill
# List repositories
skill run github list_repos --owner torvalds
# Create issue
skill run github create_issue \
--owner myuser \
--repo myrepo \
--title "Bug report" \
--body "Description of the bug"Skill Development Best Practices
1. Clear Tool Descriptions
### tool_name
Short one-line description of what this tool does
Parameters:
- param1 (required): Clear description with examples
- param2 (optional): Description with default value (default: value)
- param3 (optional): Description with allowed values (options: a, b, c)
Example:
```bash
skill run myskill tool_name --param1 value --param2 value
### 2. Parameter Validation
**Native (bash):**
```bash
#!/bin/bash
set -euo pipefail
# Validate required parameters
if [ -z "${input:-}" ]; then
echo "Error: 'input' parameter is required" >&2
exit 1
fi
# Validate file exists
if [ ! -f "$input" ]; then
echo "Error: File '$input' not found" >&2
exit 1
fiWASM (Rust):
#[skill_tool]
fn process_file(params: ProcessParams) -> ToolResult {
if params.input.is_empty() {
return ToolResult::error("'input' parameter is required");
}
if params.count < 1 || params.count > 1000 {
return ToolResult::error("'count' must be between 1 and 1000");
}
// Process...
}3. Structured Output
Always return structured JSON:
# Good - JSON output
echo '{"status": "success", "files_processed": 42}'
# Bad - Plain text
echo "Processed 42 files"// Good - Structured result
ToolResult::success(serde_json::json!({
"status": "success",
"files_processed": 42,
"duration_ms": 1234
}))4. Error Handling
Native:
#!/bin/bash
set -euo pipefail
# Trap errors
trap 'echo "{\"error\": \"Script failed at line $LINENO\"}" >&2; exit 1' ERR
# Your code hereWASM:
#[skill_tool]
fn risky_operation(params: Params) -> ToolResult {
match perform_operation() {
Ok(result) => ToolResult::success(result),
Err(e) => ToolResult::error(&format!("Operation failed: {}", e))
}
}5. Logging and Debugging
Native:
# Log to stderr (doesn't pollute JSON output)
echo "Processing file: $input" >&2
# Output JSON to stdout
echo '{"result": "success"}'WASM:
use skill_wasm_sdk::log;
#[skill_tool]
fn my_tool(params: Params) -> ToolResult {
log::debug(&format!("Processing: {}", params.input));
// Tool logic...
log::info("Processing complete");
ToolResult::success(result)
}Testing Your Skills
Manual Testing
# Test native skill
./tools/get.sh <<EOF
export resource="pods"
export namespace="default"
EOF
# Test with skill run
skill run myskill mytool --param value
# Test with debug logging
LOG_LEVEL=debug skill run myskill mytoolIntegration Testing
See Testing Guide for comprehensive testing strategies.
Publishing Skills
1. Create GitHub Repository
git init
git add .
git commit -m "Initial skill implementation"
git remote add origin https://github.com/user/myskill
git push -u origin main2. Tag Release
git tag -a v1.0.0 -m "First release"
git push origin v1.0.03. Install from Git
Others can now install:
skill install https://github.com/user/myskill4. Submit to Catalog
Open a PR to the Skill Catalog to list your skill.
Next Steps
- Manifest Reference - Complete SKILL.md format
- Testing - Test your skills
- Environment Variables - Managing configuration
- Security Model - Capability system
- Examples - Real-world skill examples
Resources
- WASM SDK Documentation: github.com/kubiyabot/skill-wasm-sdk (Experimental)
- Example Skills:
/examples/native-skills/,/examples/docker-skills/,/examples/wasm-skills/ - Community: GitHub Discussions