Skip to main content
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:
  1. Project/runtime root (<runtime-root>/.rkat/skills/) — highest precedence
  2. User (~/.rkat/skills/)
  3. Git repositories — cloned to local cache, refreshed on TTL
  4. HTTP endpoints — fetched from remote skill servers
  5. 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

SKILL.md format

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

FieldTypeRequiredDescription
nameStringYesDisplay name for the skill
descriptionStringYesOne-line description
requires_capabilitiesVec<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:
  1. Slash-prefix ID: /skill-id — matches against SkillDescriptor.id.
  2. 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:
ModeBehavior
ExplicitOnly explicit /skill-id or exact name matches

Rendering

The renderer produces two output formats:
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.

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:
IDNameCrateRequired capabilities
hook-authoringHook Authoringmeerkat-hookshooks
shell-patternsShell Patternsmeerkat-toolsbuiltins, shell
task-workflowTask Workflowmeerkat-toolsbuiltins
mcp-server-setupMCP Server Setupmeerkat-mcp(none)
memory-retrievalMemory Retrievalmeerkat-memorymemory_store
session-managementSession Managementmeerkat-sessionsession_store
multi-agent-commsMulti-Agent Commsmeerkat-commscomms

Configuration reference

SettingDefaultDescription
skills.enabledtrueEnable skill loading
skills.max_injection_bytes32768 (32 KiB)Maximum size of injected skill content
skills.inventory_threshold12Flat 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`.

Discovery tools

When skills are active and builtins are enabled, these tools are registered:
ToolDescription
browse_skillsList skill collections and search by name/description
load_skillLoad a skill’s full instructions into the conversation
skill_list_resourcesList skill-owned resources/artifacts
skill_read_resourceRead a specific skill-owned resource
skill_invoke_functionInvoke 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. 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

SurfaceMethodDescription
CLIrkat skills list [--json]List all skills with provenance
CLIrkat skills inspect <id> [--source <name>] [--json]Inspect a skill’s full body
RPCskills/listList skills with provenance
RPCskills/inspectInspect a skill by ID
RESTGET /skillsList skills
RESTGET /skills/{id}Inspect a skill
MCPmeerkat_skills tool (action: "list" / "inspect")List or inspect
Rust SDKSkillRuntime::list_all_with_provenance()Programmatic access
Rust SDKSkillRuntime::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.