AI EngineeringZero to ProductionHome·About·Contact
Anthropic Skills · Chapter K4

Agent Skills

Package reusable know-how as Skills Claude loads on demand: SKILL.md, triggering descriptions, progressive disclosure (with a cost model), bundled helpers, distribution, and an org Skills library.

⏱️ ~2.5 hours🧪 6 labs🎯 Beginner→Tech-lead

Learning objectives

  • Explain what an Agent Skill is and when it beats a prompt or CLAUDE.md.
  • Write a SKILL.md with a triggering description and bundled files.
  • Understand progressive disclosure and why it keeps Skills cheap.
  • Distribute and govern Skills across a team.
▶ Runnable companionCode saved under code/ak4-agent-skills/. Python runs offline; configs are ready to use.

1 · What a Skill is essential

An Agent Skill is a folder with a SKILL.md that packages reusable know-how — instructions, and optionally scripts and reference files — that Claude loads on demand when a task matches. It turns "the way we do X" into a versioned, shareable capability.

Task arrives user request name+desc match? cheap check load SKILL.md body on match use bundled files as needed
🗺️ How to read this diagram

This picture shows the life of a request when Skills are installed — and the one idea that makes Skills cheap: Claude does not read every Skill up front. It reads only a tiny label for each, and opens the full Skill only when your task actually matches it.

  • Box 1 · Task arrives — you send a normal request ("write me an incident report").
  • Box 2 · name+desc match? — for each installed Skill, Claude compares your task against just the Skill's name and one-line description. The caption calls this a "cheap check" because those labels are tiny (a few words), so checking them costs almost nothing even with dozens of Skills.
  • Box 3 · load SKILL.md bodyonly on a match does Claude read the full instructions. A Skill that doesn't match is never opened, so you don't pay for it.
  • Box 4 · use bundled files — once loaded, the Skill can pull in its extra files (templates, scripts) as the instructions call for them.
  • Read it left to right: each arrow is "next step". The flow narrows from "look at every Skill's label" down to "fully load just the one that fits".

In short: "Progressive disclosure" = reveal detail only when needed. Labels are always visible (cheap); the heavy body appears just-in-time. That is why a team can install many Skills without slowing every chat.

Skill vs CLAUDE.md vs promptPrompt: one-off. CLAUDE.md: always-on project context. Skill: reusable know-how for a recurring, specific task, loaded only when relevant — so you can have many without bloating every session.

2 · Anatomy of a Skill essential

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
layout · a Skill folder
layout.txt.claude/skills/
└── incident-report/
    ├── SKILL.md            # instructions + trigger metadata
    ├── template.md         # a reference the body points to
    └── scripts/
        └── gather_logs.sh   # an optional helper the Skill can run
▶ How this works

A Skill is nothing exotic — it is just a folder on disk with a specific shape. This is a directory tree (the └── and ├── lines are branches, like a family tree of files). Reading it tells you exactly what files to create.

  1. .claude/skills/ is where Claude looks for Skills. Each Skill gets its own sub-folder — here incident-report/, whose name becomes the Skill's name.
  2. SKILL.md is the required file: the instructions plus the trigger metadata (covered next). Every Skill must have exactly one.
  3. template.md is an optional reference file — extra material the instructions can point to (a house format, a checklist). It loads only if the body asks for it.
  4. scripts/gather_logs.sh is an optional helper script the Skill can run. The # text after each file is just a comment explaining its purpose, not part of the name.

Try this: Recreate this tree yourself: mkdir -p .claude/skills/incident-report/scripts and add an empty SKILL.md. That empty folder is already a (do-nothing) Skill.

config · SKILL.md
SKILL.md---
name: incident-report
description: >
  Write a postmortem/incident report in our house format. Use whenever the user
  asks for an incident report, postmortem, or RCA writeup.
---

# Incident report
1. Use the structure in `template.md` (timeline, impact, root cause, actions).
2. If logs exist, run `scripts/gather_logs.sh <service>`.
3. Timeline entries are UTC, newest last; every claim cites a log line or ticket.
4. "Action items" need an owner and a due date each.
5. No blame language — describe systems and events, not people.
▶ How this works

This is the heart of a Skill: the SKILL.md file. It has two parts — a small metadata header (between the --- lines) that tells Claude when to use the Skill, and a Markdown body that tells Claude how to do the task.

  1. The block fenced by --- at top and bottom is YAML front-matter (a tiny key: value settings header). name identifies the Skill; description is the all-important trigger line.
  2. The description: > uses a > so the text can wrap onto several indented lines and still count as one value. Notice it says "Use whenever the user asks for an incident report, postmortem, or RCA..." — it names the exact words a user would say, which is what makes Claude fire the Skill.
  3. Everything below the closing --- is the body: ordinary Markdown instructions. The # Incident report is a heading; the numbered list is the step-by-step procedure Claude follows.
  4. The body can reference the bundled files: step 1 points at template.md and step 2 runs scripts/gather_logs.sh — the same files from the folder tree above. The rules (UTC timeline, cite every claim, owners on action items, no blame language) capture "how we do it" once, reusably.

What the output means: There is no program output here — SKILL.md is instructions, not code. Its effect is that asking Claude for a postmortem now produces one in exactly this house format.

Try this: Rewrite the description to be vague ("Writes reports") and you'll see the problem the lesson warns about — a vague description often never triggers. Keep the concrete "use whenever..." phrasing.

The description IS the triggerClaude decides whether to load a Skill from its name + description alone. Write the description around when to use it ("use whenever the user asks for X") — vague descriptions never fire; precise ones do.

3 · Why progressive disclosure matters intermediate

Only each Skill's name + description are always in context (tiny). The full body loads only on a match. That's what lets a team ship dozens of Skills without every session paying for all of them — the context-economy lesson from PE2.

Python · model progressive-disclosure cost (runs)
disclosure_cost.pydef context_cost(skills, matched):
    """Always-loaded: name+desc (~20 tokens each). Body (~500) only if matched."""
    always = sum(20 for _ in skills)
    bodies = sum(500 for s in skills if s in matched)
    return always + bodies

skills = ["incident-report", "release-notes", "code-review", "data-clean", "sql-style"]
print("all 5 skills, none matched:", context_cost(skills, set()), "tokens (just metadata)")
print("all 5, one matched:       ", context_cost(skills, {"incident-report"}), "tokens")
print("if bodies always loaded:  ", 5*520, "tokens (5x waste)")
all 5 skills, none matched: 100 tokens (just metadata)
all 5, one matched:        600 tokens
if bodies always loaded:   2600 tokens (5x waste)
▶ How this works

This tiny program puts numbers on why progressive disclosure (the diagram above) saves money. It's a made-up but realistic cost model: it counts context tokens — the units Claude reads and you pay for — under different scenarios.

  1. context_cost(skills, matched) is a function taking a list of Skill names and the set of ones that matched this task. The docstring states the assumption: each Skill's name+desc costs ~20 tokens (always loaded), each full body ~500 tokens (loaded only on a match).
  2. always = sum(20 for _ in skills) adds 20 for every Skill — the cheap labels that are always present. bodies = sum(500 for s in skills if s in matched) adds 500 only for the matched ones. The total is always + bodies.
  3. The three print lines run three what-ifs: 5 Skills with none matched (labels only), 5 Skills with one matched, and the wasteful world where all 5 bodies load every time (5*520).

What the output means: none matched: 100 tokens (just 5×20 labels); one matched: 600 (100 labels + one 500 body); always loaded: 2600. The point: 100 vs 2600 — loading bodies only on demand is roughly a saving.

Try this: Change matched to two Skills, e.g. {"incident-report","code-review"}, and predict the number before running (100 + 2×500 = 1100). More matches cost more — but you still skip the Skills that didn't match.

4 · Advanced — a Skill that bundles a script advanced

Skills can bundle runnable helpers the instructions invoke. Keep the helper a clean, testable unit (DF1). Here's a helper a Skill might ship, runnable on its own.

Python · a bundled skill helper (runs)
skill_helper.py# scripts/summarize_incidents.py — a helper the incident-report Skill calls
def summarize(events):
    """events: [(ts, service, severity)] -> a timeline + a headline count."""
    events = sorted(events)                       # chronological
    crit = sum(1 for _,_,sev in events if sev == "critical")
    timeline = [f"{ts} UTC — {svc} ({sev})" for ts, svc, sev in events]
    return {"critical_count": crit, "timeline": timeline}

r = summarize([("10:05","api","critical"), ("10:01","db","warning"), ("10:12","api","critical")])
print("criticals:", r["critical_count"])
for line in r["timeline"]: print(" ", line)
criticals: 2
  10:01 UTC — db (warning)
  10:05 UTC — api (critical)
  10:12 UTC — api (critical)
▶ How this works

This is an example of the bundled helper script a Skill can ship (the scripts/ file from the folder tree). The point is that a Skill isn't limited to prose — it can carry real, runnable code that does the fiddly work reliably every time.

  1. summarize(events) takes a list of incident events. The docstring shows the shape: each event is a tuple (ts, service, severity) — a timestamp, a service name, and how bad it was.
  2. events = sorted(events) puts them in chronological order (sorting tuples sorts by the first item, the timestamp). This guarantees a correct timeline no matter what order they arrived.
  3. crit = sum(1 for _,_,sev in events if sev == "critical") counts how many were critical by adding 1 for each match — the headline count. The timeline line builds a readable string per event with an f-string.
  4. It returns a dictionary with both results. The code below the function calls it on three sample events and prints the critical count, then each timeline line.

What the output means: criticals: 2 (two of the three events were critical), followed by the three events printed in time order10:01 before 10:05 before 10:12 — proving the sorted() worked even though 10:01 was listed second in the input.

Try this: Add a fourth event like ("10:08","db","critical") to the list and re-run: the count becomes 3 and the new line slots into the right chronological spot automatically.

5 · Distributing Skills advanced

Skills live at three scopes: project (.claude/skills/, committed — whole team gets them), personal (your user config, across all projects), and plugins (bundled, shared widely). Committing a Skill is how "our way" becomes the team default.

ScopeLocationWho gets it
Project.claude/skills/ (in the repo)everyone on the repo
Personalyour user Claude configyou, everywhere
Plugina shared plugin bundleanyone who installs it

6 · Professional — write a triggering description professional

The most common Skill failure is a description that never fires. Make it specific and action-oriented. Model what a good trigger looks like.

Python · score a Skill description (runs)
desc_score.pydef score_description(desc):
    d = desc.lower(); score, notes = 0, []
    if "use when" in d or "use whenever" in d: score += 1
    else: notes.append("say WHEN to use it (\"use whenever...\")")
    if len(desc) >= 40: score += 1
    else: notes.append("too short — name concrete triggers")
    if any(w in d for w in ["report","review","summary","writeup","clean","format"]):
        score += 1
    else: notes.append("name the task nouns users will say")
    return score, notes

print(score_description("Writes stuff"))
print(score_description("Write an incident report/postmortem. Use whenever the user asks for an RCA or postmortem writeup."))
(0, ['say WHEN to use it ("use whenever...")', 'too short — name concrete triggers', 'name the task nouns users will say'])
(3, [])
▶ How this works

The lesson's biggest warning is that a Skill with a weak description never triggers. This little program is a linter for descriptions — it scores one out of 3 and tells you what's missing, so you can fix a trigger before shipping it.

  1. d = desc.lower() makes a lowercase copy so the checks are case-insensitive. score, notes = 0, [] starts the tally at 0 with an empty list of complaints.
  2. Each if awards one point for a good trait: it contains "use when/whenever" (says when to fire); it's at least 40 characters (specific, not a stub); and it mentions a concrete task noun (report, review, summary...). When a check fails, the else appends a note explaining the fix.
  3. It returns the pair (score, notes). The two print lines score a deliberately bad description ("Writes stuff") and a good one.

What the output means: (0, [...three complaints...]) for "Writes stuff" — it earns no points and gets told exactly what to add. The concrete one scores (3, []): full marks, no complaints. The exercise asks you to reach a score of at least 2.

Try this: Paste your own draft description into a third print(score_description(...)) and aim for 3. Adding the words "use whenever the user asks for a ... report" usually flips all three checks to pass at once.

7 · Tech-lead — a Skills library for the org tech-lead

A lead builds a curated Skills library: the team's recurring tasks (reviews, reports, release notes, data routines) each as a versioned Skill, reviewed like code, with clear triggers. New hires inherit the org's know-how automatically.

Skills are institutional memory as codeWhen "how we write incident reports" or "our SQL style" lives in a committed, reviewed Skill, it survives turnover and stays consistent. A lead who curates that library turns tribal knowledge into a durable, shared asset.

Exercise AK4.1 — Author & score a Skill

Context: Authoring a real skill forces the two things that make skills work: a description specific enough to trigger reliably, and a bundled helper that does deterministic work the model can reason over.

Your task: Author a SKILL.md for a recurring task with a precise trigger (score it ≥2 with desc_score), bundle one reference file and one runnable helper, and confirm it fires only when it should.

Requirements:

  • The skill targets a recurring task you already do a particular way
  • The description is a precise trigger — it scores ≥2 with desc_score
  • It bundles one reference file and one runnable helper script, and the helper is tested
  • Confirm the skill fires when you ask for that task
  • Confirm it stays dormant on unrelated tasks

💡 Hint: Test both directions — that it triggers on the target task and that it stays quiet otherwise — a skill that always fires is as broken as one that never does.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Write a minimal SKILL.mdBeginner

Context: A Skill is just a folder with a SKILL.md: frontmatter that says what it is and when to load it, plus a body of guidance — the minimum viable unit of reusable know-how.

Your task: Write a minimal SKILL.md for a skill that formats commit messages, including its name and description frontmatter.

Requirements:

  • The skill is a folder containing a SKILL.md (e.g. .claude/skills/…/SKILL.md)
  • Frontmatter includes a name and a description
  • The description says when to load the skill, not just what it does
  • The body holds the actual guidance (the commit-message format rules)
  • Verify the exact frontmatter keys and folder location in the docs

💡 Hint: Name + description + body is the whole minimum — the description is what tells Claude to reach for it.

Show solution

The whole skill is a folder plus a SKILL.md with frontmatter + body:

# .claude/skills/commit-style/SKILL.md
---
name: commit-style
description: Write conventional-commit messages. Use when composing a git
  commit message from a diff.
---

Format: `type(scope): summary` where type is feat|fix|docs|refactor|test.
- Summary in imperative mood, <=72 chars, no trailing period.
- Add a body only when the "why" isn't obvious from the summary.

The name identifies the skill and the description tells Claude when to load it; the body is the actual guidance. That is the minimum viable skill. Verify the exact frontmatter keys and folder location in the docs.

Exercise 2 · Why progressive disclosure mattersIntermediate

Context: Progressive disclosure is how a large skill library stays cheap: only the one-line description sits in context by default, and detail loads on demand.

Your task: Explain the progressive-disclosure tiers and model which parts of a skill are in context at each stage.

Requirements:

  • Tier 0 (always): only the skill's description line is resident
  • Tier 1 (on use): the full SKILL.md body loads when the task matches
  • Tier 2 (on demand): bundled files/scripts load only when the body references them
  • Model the context cost per stage (idle vs triggered vs deep)
  • Explain the payoff: you pay for detail on demand, not up front

💡 Hint: Three stages, growing context: description only, then + body, then + referenced files.

Show solution

Progressive disclosure keeps only the description resident until the skill is needed:

Tier 0 (always):  the skill's `description` line only  -> cheap, always in context
Tier 1 (on use):  the full SKILL.md body                -> loaded when relevant
Tier 2 (on demand): bundled files/scripts the body points to -> read as needed

def context_cost(stage):
    return {"idle": "description only",
            "triggered": "description + SKILL.md body",
            "deep": "description + body + referenced files"}[stage]
print(context_cost("idle"))       # description only
print(context_cost("triggered"))  # description + SKILL.md body

Only the one-line description sits in context by default; the body loads when the task matches, and bundled files load only when the body references them. That is how a large skill library stays cheap — you pay for detail on demand, not up front.

Exercise 3 · Bundle a script with a skillAdvanced

Context: Bundling a script beside a skill lets it do deterministic work — parsing, stats — instead of asking the model to eyeball it, and the script costs nothing until the body references it (Tier 2).

Your task: Sketch a skill folder that bundles a Python helper script and references it from SKILL.md.

Requirements:

  • The folder holds SKILL.md plus a bundled script (e.g. under scripts/)
  • The SKILL.md body tells the model to run the script and how to invoke it
  • The script does deterministic work; the model reasons over its reliable output
  • The script loads only when the body references it (Tier 2), so it's free until used
  • Verify path conventions in the docs

💡 Hint: Let the script do the exact computation and let the model narrate its output — point to it from the body so it stays lazy-loaded.

Show solution

Bundle the script beside SKILL.md and point to it from the body:

.claude/skills/csv-report/
  SKILL.md
  scripts/summarize.py       <- bundled helper

# SKILL.md body excerpt:
To summarize a CSV, run the bundled script:
    python scripts/summarize.py <path-to-csv>
It prints row count, column types, and per-column null rates.
Read its output, then write the narrative summary.

Bundling code lets the skill do deterministic work (parsing, stats) instead of asking the model to eyeball it — the model runs the script and reasons over reliable output. The script loads only when the body references it (Tier 2), so it costs nothing until used. Verify path conventions in the docs.

Exercise 4 · Write a triggering descriptionExpert

Context: Because the description is the only part always in context, it is the entire trigger signal — a skill that names the situation fires precisely, while a vague one sits unused or fires on the wrong task.

Your task: Write a strong versus weak skill description and explain why the strong one triggers correctly.

Requirements:

  • The weak version is vague about capability and gives no situational cue
  • The strong version names concrete triggering situations (what the user asks for)
  • It prescribes when to use the skill, not only what it does
  • Explain that the description is the only always-in-context part, so it is the whole trigger
  • Tie the specificity to firing at the right moment and not on the wrong task

💡 Hint: Describe the user's situation the skill answers ("when the user asks to…"), not the feature list.

Show solution

The description must name the situation, not just the capability:

Weak:   description: "Helps with spreadsheets."
        -> too vague; the model can't tell when it applies.

Strong: description: "Generate .xlsx spreadsheets with formulas and charts.
         Use when the user asks to create, edit, or export an Excel file
         or a report as a spreadsheet."
        -> names concrete triggers (create/edit/export .xlsx), so the
           model loads it exactly when relevant.

Because the description is the only part always in context, it is the entire trigger signal. Prescribe when to use the skill (the user-facing situations) — not just what it does — so it fires precisely and doesn't sit unused or fire on the wrong task.

Exercise 5 · Distribute a skill safelyProfessional

Context: A distributed skill runs with your agent's permissions, so vetting its bundled scripts is a security step, not a formality; versioning and pinning stop an upstream change from silently altering everyone's behavior.

Your task: Decide the packaging, versioning, and pre-install checks for sharing a skill across the org.

Requirements:

  • The skill is a self-contained folder (SKILL.md + any scripts)
  • The description is specific enough to trigger correctly
  • Bundled scripts are reviewed like code — they run with the agent's permissions
  • No secrets and no calls to untrusted hosts inside the scripts
  • Versioned in a shared repo/registry and pinned on install
  • Ships with a short README: what it does, when it triggers, what it can touch
  • Verify the current distribution/registry mechanism in the docs

💡 Hint: Treat it like importing a dependency that runs as you — review the code, pin the version, document the blast radius.

Show solution

Package it as a versioned folder and review it like code:

Distribution checklist:
  [ ] Skill is a self-contained folder (SKILL.md + any scripts).
  [ ] Description is specific enough to trigger correctly (see expert rung).
  [ ] Bundled scripts are reviewed — they run with the agent's permissions.
  [ ] No secrets, no network calls to untrusted hosts inside scripts.
  [ ] Versioned in a shared repo / registry; pin a version when installing.
  [ ] A short README: what it does, when it triggers, what it can touch.

A distributed skill runs with your agent's permissions, so vetting the bundled scripts is a security step, not a formality. Version and pin it so an upstream change can't silently alter behavior for everyone. Verify the current distribution/registry mechanism in the docs.

Exercise 6 · A Skills library for the org (tech-lead)Industry scenario

Context: A skill earns its place when the know-how recurs and is clearly triggerable; the library's health depends on non-overlapping descriptions so two skills don't fight to trigger, plus single ownership and review.

Your task: As tech lead, write the decision rule for what becomes a skill and the governance policy for an org Skills library.

Requirements:

  • The decision rule keys on: repeated, situation-specific, and whether it needs a deterministic step
  • One-off work is not a skill — a direct instruction is cheaper
  • Recurring + clearly-triggered → yes (bundle a script if deterministic work is needed)
  • Borderline cases may be a slash command or a CLAUDE.md line instead of a full skill
  • Governance: one owner per skill, non-overlapping descriptions, review + version in a shared repo, deprecate skills that stop triggering

💡 Hint: Two tests decide it: does the know-how recur, and can you write a description that won't collide with another skill's trigger?

Show solution

Make skills for repeated, situation-specific know-how; govern to avoid sprawl:

def is_skill_worthy(repeated, situation_specific, needs_deterministic_step):
    if not repeated:
        return "NO — one-off; a direct instruction is cheaper than a skill"
    if situation_specific or needs_deterministic_step:
        return "YES — recurring, clearly-triggered; bundle a script if it "\
               "needs deterministic work"
    return "MAYBE — could be a slash command instead of a full skill"

print(is_skill_worthy(True, True, True))    # YES
print(is_skill_worthy(False, True, False))  # NO

# Governance: one owner per skill; non-overlapping descriptions (so two
# skills don't fight to trigger); review + version in a shared repo;
# deprecate skills that stop triggering.

A skill earns its place when the know-how recurs and is clearly triggerable; otherwise a slash command or a line in CLAUDE.md is lighter. The library's health depends on non-overlapping descriptions (so triggering stays predictable), single ownership, and review — the same discipline as a shared code library.

✓ Checkpoint — you can move on when you can…

  • Explain when a Skill beats a prompt/CLAUDE.md.
  • Write a valid SKILL.md with a triggering description.
  • Explain progressive disclosure and its cost model.
  • Distribute Skills and curate an org library.

Knowledge check check yourself

✓ Knowledge check

How does progressive disclosure keep Agent Skills cheap, and what is loaded into context before a Skill matches?

Show answer
Only each Skill's name and description (tiny metadata labels) stay in context at all times; the full SKILL.md body loads only when a task matches. So comparing five Skills with none matched costs only the metadata (~100 tokens) instead of ~2600 tokens if every body were loaded.
✓ Knowledge check

Why is the description in a Skill's YAML front-matter considered the trigger, and what makes one reliable versus one that never fires?

Show answer
Claude decides whether to load a Skill by matching the task against its description, so the description is what actually fires it. A reliable one phrases it as "use when/whenever the user asks for [X]" with concrete task nouns (report, review, summary, clean, format); a vague description never matches and the Skill stays dormant.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in