Skills Development¶
OmniAgent supports two types of skills:
- Markdown Skills - SKILL.md files compatible with OpenClaw format
- Compiled Skills - Go packages that register callable tools with the agent
Overview¶
Markdown skills inject instructions into the system prompt, teaching the agent how to use external tools.
Compiled skills register actual Go functions as LLM tools, enabling direct function calling.
Skill Format¶
Skills are defined in SKILL.md files:
---
name: weather
description: Get weather forecasts
metadata:
emoji: "🌤️"
requires:
bins: ["curl"]
install:
- name: curl
brew: curl
apt: curl
---
# Weather Skill
You can check the weather using the `curl` command:
## Get Current Weather
```bash
curl "wttr.in/London?format=3"
Get Detailed Forecast¶
## Skill Packs
Skill packs are pre-bundled collections of markdown skills embedded via `go:embed`. They provide a convenient way to distribute and share skills.
### Using a Skill Pack
```go
import (
"github.com/plexusone/omniagent/agent"
skills "github.com/plexusone/omniagent-skills"
)
// Load all skills from a skill pack
agent, err := agent.New(config,
agent.WithSkillPack(skills.Default().FS()),
)
Filtering Skills¶
Load only specific skills using includes:
agent, err := agent.New(config,
agent.WithSkillPack(skills.Default().FS()),
agent.WithSkillIncludes("github", "weather", "tmux"),
)
Or exclude specific skills:
agent, err := agent.New(config,
agent.WithSkillPack(skills.Default().FS()),
agent.WithSkillExcludes("notion", "slack"),
)
Configuration File¶
Control skill loading via config:
skills:
enabled: true
packs:
- omniagent-skills # Informational only
paths:
- ~/.omniagent/skills
includes:
- github
- weather
excludes:
- deprecated-skill
max_injected: 20
Directory Override¶
Skills from directories take precedence over embedded pack skills with the same name. This allows customizing bundled skills:
Available Skill Packs¶
| Pack | Description | Skills |
|---|---|---|
| omniagent-skills | Default skill pack | 18 OpenClaw-compatible skills |
Agent Options¶
| Option | Description |
|---|---|
WithSkillPack(fs.FS) |
Register an embedded skill pack |
WithSkillDirs(...string) |
Set skill directories |
WithSkillIncludes(...string) |
Only load named skills |
WithSkillExcludes(...string) |
Skip named skills |
WithSkillManager(*Manager) |
Use a custom skill manager |
Skill Discovery¶
Skills are discovered from:
- Embedded skill packs (via
WithSkillPack) ~/.omniagent/skills/- Custom paths via
skills.pathsconfig
Managing Skills¶
List Skills¶
Output:
✓ 🎵 sonoscli - Control Sonos speakers via CLI
✓ 🐙 github - GitHub CLI operations
✗ ☀️ weather - Weather forecasts (missing: weather binary)
Show Skill Details¶
Check Requirements¶
Requirements¶
Skills can declare requirements that must be met:
Binary Requirements¶
metadata:
requires:
bins: ["gh", "jq"] # All required
anyBins: ["curl", "wget"] # At least one required
Environment Variables¶
Install Hints¶
Creating a Skill¶
1. Create Directory¶
2. Create SKILL.md¶
---
name: myskill
description: My custom skill
metadata:
emoji: "🔧"
---
# My Skill
Instructions for the AI agent on how to use this skill...
## Available Commands
- `mytool list` - List all items
- `mytool add <name>` - Add a new item
3. Verify¶
Best Practices¶
Keep Instructions Clear¶
Write instructions as if explaining to a human who knows nothing about your tool.
Include Examples¶
Show concrete examples of commands and expected output.
Declare Requirements¶
Always declare binary and environment requirements so users know what's needed.
Use Emojis Sparingly¶
One emoji in the metadata helps identify skills visually.
ClawHub Compatibility¶
OmniAgent is compatible with skills from ClawHub. Install skills using:
Or manually clone to your skills directory:
Compiled Skills¶
Compiled skills are Go packages that register functions as LLM tools. They provide better performance and type safety compared to markdown skills.
Creating a Compiled Skill¶
1. Implement the Skill Interface¶
package myskill
import (
"context"
"github.com/plexusone/omniagent/skills/compiled"
)
type MySkill struct {
// skill state
}
func New() *MySkill {
return &MySkill{}
}
func (s *MySkill) Name() string {
return "myskill"
}
func (s *MySkill) Description() string {
return "My custom skill description"
}
func (s *MySkill) Tools() []compiled.Tool {
return []compiled.Tool{
{
Name: "myskill_action",
Description: "Performs an action",
Parameters: map[string]compiled.Parameter{
"input": {
Type: "string",
Description: "The input value",
Required: true,
},
},
Handler: s.handleAction,
},
}
}
func (s *MySkill) Init(ctx context.Context) error {
// Initialize resources
return nil
}
func (s *MySkill) Close() error {
// Cleanup resources
return nil
}
func (s *MySkill) handleAction(ctx context.Context, params map[string]any) (any, error) {
input := params["input"].(string)
return map[string]any{"result": "Processed: " + input}, nil
}
2. Register with Agent¶
import "github.com/example/myskill"
skill := myskill.New()
agent, err := agent.New(config,
agent.WithCompiledSkill(skill),
)
Storage-Aware Skills¶
Skills that need persistent storage implement StorageAware:
import "github.com/plexusone/omnistorage-core/kvs"
type MySkill struct {
storage kvs.Store
}
func (s *MySkill) SetStorage(store kvs.Store) {
s.storage = store
}
Storage is automatically injected when using WithSessionsFromStorage or WithStorage:
agent.New(config,
agent.WithSessionsFromStorage(backend), // Injects storage
agent.WithCompiledSkill(mySkill), // Receives storage
)
Parameter Types¶
| Type | JSON Schema Type | Go Type |
|---|---|---|
"string" |
string | string |
"integer" |
integer | int, int64 |
"number" |
number | float64 |
"boolean" |
boolean | bool |
"array" |
array | []any |
"object" |
object | map[string]any |
Built-in Compiled Skills¶
OmniAgent includes these compiled skills:
| Skill | Tools | Description |
|---|---|---|
sessions |
sessions_list, sessions_history, etc. |
Session management |
Skill Lifecycle¶
agent.New()
│
├── WithCompiledSkill(skill)
│ └── RegisterCompiledSkill()
│ ├── SetStorage() if StorageAware
│ └── Register tools with ToolRegistry
│
├── InitCompiledSkills()
│ └── skill.Init(ctx) for each skill
│
... agent runs ...
│
└── agent.Close()
└── CloseCompiledSkills()
└── skill.Close() for each skill
Best Practices¶
- Return Structured Data - Return maps or structs, not plain strings
- Handle Errors Gracefully - Return user-friendly error messages
- Validate Parameters - Check required parameters before processing
- Use Descriptive Names - Tool names should be
skillname_actionformat - Document Parameters - Descriptions help the LLM use tools correctly
Remote Skills¶
Remote skills connect to external services and expose their capabilities as agent tools. OmniAgent supports two types of remote skills:
MCP Skills¶
MCP (Model Context Protocol) skills spawn external MCP servers and expose their tools to the agent.
import "github.com/plexusone/omniagent/agent"
agent, err := agent.New(config,
agent.WithMCPSkill(mcp.Config{
Name: "github",
Command: []string{"npx", "-y", "@modelcontextprotocol/server-github"},
Env: map[string]string{
"GITHUB_TOKEN": os.Getenv("GITHUB_TOKEN"),
},
}),
)
MCP Configuration¶
| Field | Description |
|---|---|
Name |
Skill identifier (must be unique) |
Description |
Human-readable description |
Command |
Command to spawn the MCP server |
Env |
Environment variables for the command |
LazyConnect |
Defer connection until first tool call |
ClientName |
Client identifier sent to server |
ClientVersion |
Client version sent to server |
Popular MCP Servers¶
| Server | Install Command |
|---|---|
| GitHub | npx -y @modelcontextprotocol/server-github |
| Filesystem | npx -y @modelcontextprotocol/server-filesystem |
| Brave Search | npx -y @anthropic/brave-search-mcp |
OpenAPI Skills¶
OpenAPI skills parse OpenAPI 3.x specifications and expose operations as tools.
import (
"github.com/plexusone/omniagent/agent"
openapi "github.com/plexusone/omniagent/skills/remote/openapi"
)
agent, err := agent.New(config,
agent.WithOpenAPISkill(openapi.Config{
Name: "petstore",
SpecURL: "https://petstore3.swagger.io/api/v3/openapi.json",
Auth: openapi.AuthConfig{
Type: openapi.AuthAPIKey,
APIKey: os.Getenv("PETSTORE_API_KEY"),
},
}),
)
OpenAPI Configuration¶
| Field | Description |
|---|---|
Name |
Skill identifier (must be unique) |
Description |
Human-readable description |
SpecURL |
URL to fetch OpenAPI spec |
SpecFile |
Path to local OpenAPI spec file |
BaseURL |
Override the server URL from spec |
Auth |
Authentication configuration |
IncludeOperations |
Filter to specific operation IDs |
ExcludeOperations |
Exclude specific operation IDs |
IncludeTags |
Filter to operations with tags |
LazyLoad |
Defer spec loading until first use |
RequestTimeout |
Timeout for API requests (seconds) |
Authentication Types¶
// API Key (header)
Auth: openapi.AuthConfig{
Type: openapi.AuthAPIKey,
APIKey: "your-api-key",
APIKeyName: "X-API-Key", // default
APIKeyIn: "header", // or "query"
}
// Bearer Token
Auth: openapi.AuthConfig{
Type: openapi.AuthBearer,
Token: "your-bearer-token",
}
// Basic Auth
Auth: openapi.AuthConfig{
Type: openapi.AuthBasic,
Username: "user",
Password: "pass",
}
Filtering Operations¶
Include only specific operations:
agent.WithOpenAPISkill(openapi.Config{
Name: "api",
SpecURL: "https://api.example.com/openapi.json",
IncludeOperations: []string{"getUser", "listUsers"},
})
Filter by tags:
agent.WithOpenAPISkill(openapi.Config{
Name: "api",
SpecURL: "https://api.example.com/openapi.json",
IncludeTags: []string{"users", "accounts"},
})
Exclude operations:
agent.WithOpenAPISkill(openapi.Config{
Name: "api",
SpecURL: "https://api.example.com/openapi.json",
ExcludeOperations: []string{"deleteUser", "adminEndpoint"},
})