Ratel Docs
Features

Tools, MCP & Skills

What goes into Ratel's catalogs: local tools, MCP server tools, and skills — registered once, ranked per turn by search_capabilities, loaded on demand.

Ratel manages three kinds of capabilities: local tools (functions in your code), MCP server tools (from upstream servers), and skills (Markdown playbooks).

They live in two catalogs — ToolCatalog for tools, SkillCatalog for skills — behind one contract. Everything is registered once, ranked per turn by search_capabilities, and loaded on demand. See Architecture for how the pieces fit.

Tools & MCP servers

A local tool is a schema plus a handler you register directly. An upstream MCP server contributes its tools through the same path.

Both land in the same ToolCatalog and rank as one surface: search_capabilities scores them together, and invoke_tool runs whichever wins. The agent never cares where a tool came from.

See Register tools (TypeScript, Python) for the how.

Skills

This section is the concept: reusable playbooks your agent discovers at runtime. Ratel's own audit and engagement skills for coding agents ship with the Quick start.

A skill is a reusable Markdown playbook — a deploy runbook, a debugging checklist — registered with ranked metadata so an agent finds it at the moment it matters. Where a tool is executed, a skill is read, not executed: the body is the payload.

The agent discovers a skill through search_capabilities and pulls the full body into context with get_skill_content — two of the three progressive disclosure tools. Then it follows the instructions.

Skills vs tools

A tool is one operation: the model fills in arguments, the runtime executes. A skill is instructions: a multi-step procedure the agent carries out itself. When the knowledge is "how to do this well" rather than "one operation to run", instructions beat schemas.

ADR-0005 makes two properties load-bearing:

  • Read, not executed. Nothing runs when a skill loads.
  • Advisory. A task completes without the skill, just worse — so surfacing fails silently and must be measurable. The SDK emits skill_search and skill_invoke trace events; see Telemetry.

The advisory property is why there is no standalone skill-search tool. The agent must search for tools anyway, so skills ride in the same search_capabilities response: the tool's necessity carries the skill. ADR-0005 rejected the alternatives — separate skill-search (skippable), one merged ranked list (starvation, incomparable scores), and a related-skills side-channel (a redundant third mechanism).

The skill shape

A skill is { id, name, description, tags, tools, metadata, body }. Only id, name, and description are required; the rest defaults to empty in both SDKs.

FieldRequiredIndexed for rankingPurpose
idyesnolookup key for get_skill_content
nameyesyesranking signal, matched whole and identifier-split
descriptionyesyesranking signal; shown in the skills bucket, compacted to 160 chars
tagsno, []yesauthor-declared labels and task phrases ("frontend", "login form") folded into the indexed text
toolsno, []noids of tools the playbook calls; pulled into the tools bucket when the skill matches
metadatano, {}nofree-form context for higher layers, e.g. {"stacks": ["react"]}; never matched as query terms
bodyno, ""nothe Markdown payload returned by get_skill_content

Worth memorizing: tags are query terms; metadata is carried context, never matched. body is deliberately excluded from the index, so a 15 KB playbook never skews relevance.

A separate corpus, an independent budget

SkillCatalog is the on-demand analog of ToolCatalog: register once, search ranks by relevance, the body is fetched only on load. Its separate corpus ranks name, description, and tagsBM25 by default, semantic or hybrid when enabled.

search_capabilities returns two independently ranked buckets, tools and skills, each with its own top-K budget (topKSkills defaults to 3, topKTools to 5, each capped at 50). Scores are never compared across the two corpora, so a relevant skill can never be crowded out by a pile of matching tools.

Declared tools ride in

When a skill matches, search_capabilities also pulls its declared tools into the tools bucket — playbook and toolkit in one turn. The mechanics:

  • Additive: pulled-in tools do not count against topKTools.
  • Score 0: a query-matched tool keeps its score; a skill-declared dependency rides in at 0.
  • Deduped: a tool that matched both ways appears once.
  • Must exist in the ToolCatalog: unknown declared ids are silently skipped.

Wiring

Pass the SkillCatalog as the second argument to the search factory, and hand the agent get_skill_content built from the same catalog. Without it, the skills bucket is always [].

Register skills before building the search tool: its description advertises the skills bucket and get_skill_content only when the catalog is non-empty at construction time.

from ratel_ai import Skill, SkillCatalog, ToolCatalog, get_skill_content_tool, search_capabilities_tool

catalog = ToolCatalog()  # ...register tools...
skills = SkillCatalog()
skills.register(
    Skill(
        id="vercel-deploy",
        name="vercel-deploy",
        description="How to deploy to Vercel: env vars, preview vs production, rollbacks.",
        tags=["deploy", "ship to production"],      # indexed: query terms
        tools=["vercel__deploy", "fs__read_file"],  # dependency edge, not indexed
        metadata={"stacks": ["next", "vercel"]},    # carried context, never matched
        body="## Deploying to Vercel\n1. ...",
    )
)

search = search_capabilities_tool(catalog, skills)  # 2nd argument: skills bucket is live
load = get_skill_content_tool(skills)               # id: "get_skill_content"
import { SkillCatalog, ToolCatalog, getSkillContentTool, searchCapabilitiesTool } from "@ratel-ai/sdk";

const catalog = new ToolCatalog(); // ...register tools...
const skills = new SkillCatalog();
skills.register({
  id: "vercel-deploy",
  name: "vercel-deploy",
  description: "How to deploy to Vercel: env vars, preview vs production, rollbacks.",
  tags: ["deploy", "ship to production"],     // indexed: query terms
  tools: ["vercel__deploy", "fs__read_file"], // dependency edge, not indexed
  metadata: { stacks: ["next", "vercel"] },   // carried context, never matched
  body: "## Deploying to Vercel\n1. ...",
});

const search = searchCapabilitiesTool(catalog, skills); // 2nd argument: skills bucket is live
const load = getSkillContentTool(skills);               // id: "get_skill_content"

Ratel Local loads skills from disk and manages them from the CLI — see Managing skills.

How the agent uses a skill

search_capabilities surfaces the skill

The user says "ship this to production" and no deploy tool is in context. The agent searches; the skill ranks in the skills bucket, and its declared tools join the tools bucket at score 0:

{
  "tools": {
    "groups": [
      { "server": { "name": "vercel" }, "hits": [{ "toolId": "vercel__deploy", "score": 0, "description": "...", "inputSchema": {} }] },
      { "server": { "name": "fs" }, "hits": [{ "toolId": "fs__read_file", "score": 0, "description": "...", "inputSchema": {} }] }
    ]
  },
  "skills": [
    { "skillId": "vercel-deploy", "score": 1.42, "description": "How to deploy to Vercel: env vars, preview vs production, rollbacks." }
  ]
}

get_skill_content pulls the body

get_skill_content({ "skillId": "vercel-deploy" }) returns { "body": "## Deploying to Vercel\n1. ..." }. An unknown id returns a structured { error, isError: true } pointing back to search_capabilities — never a crash.

The agent follows the playbook

The full Markdown is now in context. The agent works through the steps, running the tools via invoke_tool. Loading the skill executed nothing; the model does the work.

Next steps

On this page