Skills inject domain-specific knowledge into the agent’s context. A skill is a SKILL.md document with YAML frontmatter, addressed at runtime by SkillKey { source_uuid, skill_name }, and included in the system prompt or per-turn context when its required capabilities are available.
This page is the task-first guide. For the low-level runtime vocabulary and identity model, see Skills reference.
What this guide is for
Use this guide when you want to:
- create or configure skill sources
- preload or inject skills into sessions
- understand
SkillKey identity and source provenance
- understand when to use
preload_skills vs skill_refs
Skill sources
Skills are discovered from multiple source transports:
- Embedded builtin skills from component crates
- Project/runtime root (
<context-root>/.rkat/skills/) when a context root is active
- Configured filesystem sources
- Git repositories cloned to local cache and refreshed on TTL
- HTTP endpoints fetched from remote skill servers
- Stdio sources
The canonical identity is the pair of source_uuid and skill_name. Two sources can both provide task-workflow without colliding because their source UUIDs differ. First-source-wins shadowing applies only when the same full SkillKey appears more than once.
Git sources
Configured in skills settings (typically in realm config.toml). Uses the system git executable:
[[repositories]]
name = "company"
source_uuid = "11111111-1111-1111-1111-111111111111"
type = "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 = { scheme = "token", token = "${GITHUB_TOKEN}" } # optional HTTPS token; env vars expanded
# auth = { scheme = "ssh-key", path = "~/.ssh/id_ed25519" } # or an SSH private key path
HTTP sources
[[repositories]]
name = "remote"
source_uuid = "22222222-2222-2222-2222-222222222222"
type = "http"
url = "https://skills.example.com/api"
auth = { scheme = "bearer", token = "${SKILLS_API_KEY}" } # or { scheme = "header", name = "...", value = "..." }
refresh_seconds = 300
timeout_seconds = 15
Filesystem and stdio sources
[[repositories]]
name = "local-shared"
source_uuid = "33333333-3333-3333-3333-333333333333"
type = "filesystem"
path = "/opt/meerkat-skills"
[[repositories]]
name = "generated"
source_uuid = "44444444-4444-4444-4444-444444444444"
type = "stdio"
command = "my-skill-server"
args = ["--json"]
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 | Canonical lowercase slug name for the skill. For filesystem skills, this must match the containing directory. |
description | String | Yes | One-line description |
requires_capabilities | Vec<String> | No (default []) | Lowercase 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.
Capability companion skills
Companion skills are the standard agent instruction-manual pattern for
nontrivial Meerkat-owned tool families.
They are:
- embedded in the crate or tool family that owns the capability
- gated with
requires_capabilities
- discoverable through the normal skills inventory
- loadable with
load_skill
- not automatically preloaded
Tool descriptions should stay short and schema-focused: what the tool does and
what arguments it accepts. Companion skills explain when, how, and why to use a
tool family.
Every nontrivial agent-facing built-in tool family should have human docs plus
a companion skill, or an explicit documented exemption.
Skill identity and resolution
Every runtime skill reference resolves to:
{
"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
"skill_name": "email-extractor"
}
The source identity registry applies source lifecycle gates and lineage/remap rules before load. Disabled or retired sources fail resolution; configured source rotations, renames, splits, and merges require explicit lineage and per-skill remap entries where needed.
Legacy slash-delimited strings are no longer accepted by wire skill_refs. Some local CLI paths still accept a bare skill slug as a convenience, but API and tool surfaces should use structured keys.
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 source_uuid="00000000-0000-4b11-8111-000000000001" skill_name="task-workflow">
<description>How to use task_create/task_update/task_list for structured work tracking</description>
</skill>
<skill source_uuid="00000000-0000-4b11-8111-000000000001" skill_name="shell-patterns">
<description>Background job patterns with shell and job management tools</description>
</skill>
</available_skills>
Identity is emitted as the two typed attributes that map 1:1 to the load_skill tool contract — never a single re-parseable slash id.When there are more than 12 skills, collection mode is used instead:<available_skills mode="collections">
<collection source="00000000-0000-4b11-8111-000000000001" count="8">Embedded skills</collection>
<collection source="dc256086-0d2f-4f61-a307-320d4148107f" count="3">Company skills</collection>
Use the browse_skills tool to list skills in a source or search.
Use the load_skill tool to activate a skill by SkillKey.
</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 source_uuid="00000000-0000-4b11-8111-000000000001" skill_name="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 |
skill-discovery-workflow | Skill Discovery Workflow | meerkat-tools | skills |
builtin-utilities-workflow | Builtin Utilities 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 |
schedule-workflow | Schedule Workflow | meerkat-schedule | schedule |
workgraph-workflow | WorkGraph Workflow | meerkat-workgraph | work_graph |
multi-agent-comms | Multi-Agent Comms | meerkat-comms | comms |
mob-communication | Mob Communication | meerkat | 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: my-deployment
description: Project-specific deployment procedures
requires_capabilities: []
---
# my-deployment
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. CLI preload convenience
The CLI --skill option accepts local skill slugs and materializes builtin-source SkillKey values for preload:
rkat run "Extract emails" --skill email-extractor
Use typed keys for API and tool surfaces.
2. Programmatic typed skill_refs (per-turn, recommended)
Pass tagged skill_refs in API requests to resolve and inject skills for a specific turn:
{
"method": "turn/start",
"params": {
"session_id": "...",
"prompt": "Extract emails from this text",
"skill_refs": [
{
"kind": "structured",
"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
"skill_name": "email-extractor"
}
]
}
}
Legacy skill_references strings are retired and rejected; use structured skill_refs.
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": [
{
"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
"skill_name": "extraction-email"
},
{
"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
"skill_name": "formatting-markdown"
}
]
}
}
See the Python SDK and TypeScript SDK for SDK wrappers. CLI --skill is the preload-oriented path for runtime-local skill loading; it is not the same surface as typed per-turn skill_refs.
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 skill list [--json] | List all skills with provenance |
| CLI | rkat skill inspect <skill-name> --source-uuid <uuid> [--json] | Inspect a skill’s full body |
| RPC | skills/list | List skills with provenance |
| REST | GET /skills | List skills |
| MCP | meerkat_skills tool (action: "list" or "inspect") | List skills or inspect one by typed skill_key |
| Rust SDK | SkillRuntime::list_all_with_provenance() | Programmatic access |
| Rust SDK | SkillRuntime::load_from_source() | Load a skill body by typed key (optional source-name filter) |
Shadowing
When multiple sources provide the same full SkillKey, the first source in the runtime composite wins. Introspection shows both the active skill and the shadowed entries. The common case is that matching skill_name values from different sources remain separate active skills because their source_uuid values differ:
[
{
"key": {
"source_uuid": "00000000-0000-4b11-8111-000000000001",
"skill_name": "task-workflow"
},
"name": "Task Workflow",
"scope": "builtin",
"is_active": true
},
{
"key": {
"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
"skill_name": "task-workflow"
},
"name": "Custom Task Workflow",
"scope": "project",
"is_active": true
}
]
Use a full SkillKey (skill_name plus source_uuid) when inspecting or loading a specific source.
Rust SDK
use meerkat::{AgentFactory, SkillRuntime, SkillFilter};
use meerkat_core::{Config, skills::{SkillKey, SkillName, SourceUuid}};
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.key,
entry.is_active,
entry.descriptor.source_name,
);
}
// Load a specific skill's body. The SkillKey already pins the source
// UUID; the optional second argument filters by repository *name*.
let key = SkillKey::new(
SourceUuid::parse("dc256086-0d2f-4f61-a307-320d4148107f")?,
SkillName::parse("task-workflow")?,
);
let doc = runtime.load_from_source(&key, None).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"
source_uuid = "55555555-5555-5555-5555-555555555555"
type = "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.
See also