A skill is a droppable folder that teaches the agent how to approach a kind of task — pure guidance, or guidance plus its own tools/MCP server — without touching core code. See src/skills/loader.py for the discovery mechanism.
.tuffy/skills/
my-skill/
SKILL.md required
tools.py optional
mcp.json optional
---
name: my-skill
description: One line — what this is for and when the model should reach for it.
---
Guidance body: step-by-step instructions for how to approach this kind of task. This is only
fetched on demand (via the `read_skill` tool) when the model decides the description matches
what the user's asking — it is NOT inlined into every system prompt.name and description are both required in the frontmatter — a SKILL.md missing a
description is skipped at startup with a printed warning. Keep the description to one line; it's
what's shown in /skills and in the system prompt for every other skill you have installed, so
a wordy description costs prompt budget on every turn regardless of whether this skill gets used.
Plain functions decorated with @registry.register(...), exactly like
src/tools/*.py — same decorator, same registry, auto-imported at
startup:
from src.tools.registry import registry
@registry.register(
name="my_skill_helper",
description="What this does.",
parameters={"arg": {"type": "string", "description": "..."}},
required=["arg"],
group="docs",
)
def my_skill_helper(arg: str) -> str:
...A single MCP server config (same shape as an entry in .tuffy/mcp.json — see
configure-mcp.md) that this skill wants connected. Tuffy merges it into the
MCP client's server list at startup automatically.
Run /skills to confirm it's listed with the right description. If it shipped tools, run
/tools and check they appear (group docs unless the tool specified otherwise). Ask something
that should trigger the skill and confirm the model calls read_skill before acting on it.
.tuffy/skills/scratchpad/ is a working example that ships its
own tools.py (scratch_note/scratch_list/scratch_clear) alongside SKILL.md.