Skills inject domain-specific knowledge into the agent’s context. A skill is a markdown document with YAML frontmatter that gets included in the system prompt or per-turn context when its required capabilities are available.
Skill sources
Skills are discovered from multiple sources, in precedence order:
- Project/runtime root (
<runtime-root>/.rkat/skills/) — highest precedence
- User (
~/.rkat/skills/)
- Git repositories — cloned to local cache, refreshed on TTL
- HTTP endpoints — fetched from remote skill servers
- Embedded (builtin skills from component crates)
A project skill with the same ID as a builtin skill overrides the builtin (first-source-wins).
Git sources
Configured in skills settings (typically in realm config.toml). Uses gitoxide (pure Rust, no git CLI dependency):
[[repositories]]
name = "company"
transport = "git"
url = "https://github.com/company/meerkat-skills.git"
git_ref = "main"
ref_type = "branch" # "branch", "tag", or "commit"
refresh_seconds = 300 # TTL for branch refs (tags/commits are immutable)
depth = 1 # shallow clone depth
# auth_token = "${GITHUB_TOKEN}" # optional, env vars expanded
HTTP sources
[[repositories]]
name = "remote"
transport = "http"
url = "https://skills.example.com/api"
# auth_bearer = "${SKILLS_API_KEY}"
cache_ttl_seconds = 300
Each skill is a markdown file with YAML frontmatter:
---
name: Shell Patterns
description: "Background job workflows: patterns and tips"
requires_capabilities: [builtins, shell]
---
# Shell Patterns
When running background jobs...
Frontmatter fields
| Field | Type | Required | Description |
|---|
name | String | Yes | Display name for the skill |
description | String | Yes | One-line description |
requires_capabilities | Vec<String> | No (default []) | Capability IDs that must be available |
The frontmatter is delimited by --- markers. The body (everything after the closing ---) is stored as the skill’s body field.
Capability gating
Skills are filtered by their requires_capabilities field before being shown or injected:
- A skill is included only if all its required capabilities are available in the current build/config.
- A skill with an empty
requires_capabilities list is always available.
Skill resolution
The resolver supports two reference formats:
- Slash-prefix ID:
/skill-id — matches against SkillDescriptor.id.
- Exact name match (case-insensitive):
"Shell Patterns" or "shell patterns" — matches against SkillDescriptor.name.
Resolution returns SkillError::NotFound if no match, or SkillError::Ambiguous if multiple skills match.
Resolution mode:
| Mode | Behavior |
|---|
Explicit | Only explicit /skill-id or exact name matches |
Rendering
The renderer produces two output formats:
Inventory section
Injection block
Generates an <available_skills> XML block listing all available skills for the system prompt:<available_skills>
<skill id="task-workflow">
<description>How to use task_create/task_update/task_list for structured work tracking</description>
</skill>
<skill id="shell-patterns">
<description>Background job patterns with shell and job management tools</description>
</skill>
</available_skills>
When there are more than 12 skills, collection mode is used instead:<available_skills mode="collections">
<collection path="extraction" count="8">Entity extraction skills</collection>
<collection path="formatting" count="3">Output formatting skills</collection>
Use the browse_skills tool to list skills in a collection or search.
Use the load_skill tool or /collection/skill-name to activate a skill.
</available_skills>
This section is injected into the system prompt via the extra_sections parameter of assemble_system_prompt() in meerkat/src/prompt_assembly.rs. Wraps the skill body in XML-style tags for per-turn injection:<skill id="shell-patterns">
# Shell Patterns
When running background jobs...
</skill>
Size limits
MAX_INJECTION_BYTES is set to 32 KiB (32 * 1024 bytes). If an injection block exceeds this limit, it is truncated with a warning log.
Built-in skills
The following skills are embedded in component crates:
| ID | Name | Crate | Required capabilities |
|---|
hook-authoring | Hook Authoring | meerkat-hooks | hooks |
shell-patterns | Shell Patterns | meerkat-tools | builtins, shell |
task-workflow | Task Workflow | meerkat-tools | builtins |
mcp-server-setup | MCP Server Setup | meerkat-mcp | (none) |
memory-retrieval | Memory Retrieval | meerkat-memory | memory_store |
session-management | Session Management | meerkat-session | session_store |
multi-agent-comms | Multi-Agent Comms | meerkat-comms | comms |
Configuration reference
| Setting | Default | Description |
|---|
skills.enabled | true | Enable skill loading |
skills.max_injection_bytes | 32768 (32 KiB) | Maximum size of injected skill content |
skills.inventory_threshold | 12 | Flat listing below, collection mode above |
Custom skills
Create a subdirectory under .rkat/skills/ with a SKILL.md file:.rkat/skills/
my-deployment/
SKILL.md
Example .rkat/skills/my-deployment/SKILL.md:---
name: Deployment Guide
description: Project-specific deployment procedures
requires_capabilities: []
---
# Deployment Guide
1. Run `make build` to create the release binary.
2. Deploy via `./deploy.sh production`.
Same structure under ~/.rkat/skills/. These are available across all projects but have lower precedence than project-level skills.
When skills are active and builtins are enabled, these tools are registered:
| Tool | Description |
|---|
browse_skills | List skill collections and search by name/description |
load_skill | Load a skill’s full instructions into the conversation |
skill_list_resources | List skill-owned resources/artifacts |
skill_read_resource | Read a specific skill-owned resource |
skill_invoke_function | Invoke a skill-defined function with structured arguments |
In collection mode (when there are more than 12 skills), the system prompt shows collection summaries. The LLM can use browse_skills to explore collections and load_skill to activate specific skills.
Invoking skills
Skills can be activated in three ways:
1. Slash reference in user message
Include /skill-id at the start of a message:
/extraction/email Extract the sender from this email
The agent detects the reference, resolves the skill, and injects its body before running the LLM.
2. Programmatic typed skill_refs (per-turn, recommended)
Pass skill_refs in the API request to resolve and inject skills for a specific turn:
{
"method": "turn/start",
"params": {
"session_id": "...",
"prompt": "Extract emails from this text",
"skill_refs": [
{
"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
"skill_name": "email-extractor"
}
]
}
}
Legacy skill_references strings remain boundary compatibility input only.
3. Preloading at session creation
Pass preload_skills to inject skills into the system prompt at session creation:
{
"method": "session/create",
"params": {
"prompt": "Hello!",
"preload_skills": ["extraction/email", "formatting/markdown"]
}
}
See the Python SDK and TypeScript SDK for SDK wrappers.
Skill introspection
Skill introspection lets you see all skills — including shadowed ones — with their provenance information. This is useful for debugging skill resolution, understanding which source provides which skills, and inspecting shadowed skills before overriding them.
Surfaces
| Surface | Method | Description |
|---|
| CLI | rkat skills list [--json] | List all skills with provenance |
| CLI | rkat skills inspect <id> [--source <name>] [--json] | Inspect a skill’s full body |
| RPC | skills/list | List skills with provenance |
| RPC | skills/inspect | Inspect a skill by ID |
| REST | GET /skills | List skills |
| REST | GET /skills/{id} | Inspect a skill |
| MCP | meerkat_skills tool (action: "list" / "inspect") | List or inspect |
| Rust SDK | SkillRuntime::list_all_with_provenance() | Programmatic access |
| Rust SDK | SkillRuntime::load_from_source() | Load from specific source |
Shadowing
When multiple sources provide a skill with the same ID, the first source wins (project > user > git > http > embedded). Introspection shows both the active skill and the shadowed entries:
[
{
"id": "task-workflow",
"name": "Custom Task Workflow",
"scope": "project",
"source": "company",
"is_active": true
},
{
"id": "task-workflow",
"name": "Task Workflow",
"scope": "builtin",
"source": "embedded",
"is_active": false,
"shadowed_by": "company"
}
]
Use --source <name> (CLI) or the source parameter (RPC/SDK) to load the body of a shadowed skill directly, bypassing first-wins resolution.
Rust SDK
use meerkat::{AgentFactory, SkillRuntime, SkillFilter};
use meerkat_core::Config;
let config = Config::load().await?;
let factory = AgentFactory::new(store_path);
// Build a SkillRuntime (requires "skills" feature)
if let Some(runtime) = factory.build_skill_runtime(&config).await {
// List all skills with provenance
let entries = runtime.list_all_with_provenance(&SkillFilter::default()).await?;
for entry in &entries {
println!("{}: active={}, source={}", entry.descriptor.id, entry.is_active, entry.descriptor.source_name);
}
// Load a specific skill's body (bypassing first-wins)
let doc = runtime.load_from_source(&"task-workflow".into(), Some("company")).await?;
println!("{}", doc.body);
}
Configuration
Skills are configured via SkillsConfig (realm-scoped at runtime, with optional user/project layering when those files are present):
# realm config (or ~/.rkat/skills.toml and .rkat/skills.toml in layered compatibility mode)
enabled = true
max_injection_bytes = 32768 # max size of injected skill content
inventory_threshold = 12 # flat listing below, collection mode above
[[repositories]]
name = "company"
transport = "git"
url = "https://github.com/company/skills.git"
When layered user/project files are used, project settings override user settings and repositories are merged by name.