Anatomy of a good Claude skill
A Claude Skill is a folder. Inside, a SKILL.md file with YAML frontmatter and Markdown instructions tells Claude how to do one job well, and the folder can carry scripts, templates, and reference documents alongside it. Anthropic describes the structure as "organized like an onboarding guide you'd create for a new team member." A good onboarding guide doesn't hand a new hire the entire company wiki on day one. It tells them what to do first and points at the reference material they'll need later.
Most skills that fail to trigger, or trigger and misbehave, fail for the same reasons: a vague description Claude can't match against, a body that tries to explain everything up front, or instructions pitched at the wrong level of freedom. This guide builds one skill layer by layer so you can see where each failure comes from.
The worked example is changelog-entry: given a git diff, it writes a single changelog line in the project's existing style. You'll need .claude/skills/ in a project, or ~/.claude/skills/ for something personal, if you're working in Claude Code. Nothing else is required.
Step 1: Understand the three layers
Claude loads a skill in three stages, and the stage determines what belongs where.
Level 1, metadata, always loaded. The name and description from every installed skill's frontmatter sit in the system prompt from the start of the session. Anthropic's docs note that "until a Skill is triggered, only its name and description occupy context," which is why dozens of skills can be installed without a token penalty.
Level 2, instructions, loaded when triggered. Once the description matches the request, Claude reads the body of SKILL.md. The actual procedure lives here.
Level 3, resources, loaded on demand. Reference files, scripts, and templates that SKILL.md links to are read only when Claude decides it needs them.
The mistake that sinks most first drafts is writing as if only Level 2 exists: cramming edge cases and reference tables into the main body because that's the file you're looking at. Decide up front what's relevant to every invocation, and what's only relevant sometimes. That single decision does more for skill quality than any amount of prose polishing.
Step 2: Write a specific frontmatter
name allows lowercase letters, numbers, and hyphens only, capped at 64 characters. description is capped at 1,024 characters and must be non-empty. Anthropic's guidance is that Claude uses description "to choose the right Skill from potentially 100+ available Skills," so a description that only says what the skill does, and not when to reach for it, is half a description:
---
name: changelog-entry
description: Write a single changelog entry from a git diff, matching the project's existing changelog style and section (Added, Fixed, Changed). Use when the user asks to update the changelog, add a changelog entry, or summarize a diff for release notes.
---Write it in the third person: "Write a single changelog entry," not "I can write" or "You can use this." The description is injected verbatim into the system prompt, and Anthropic's guidance flags inconsistent point of view as a cause of discovery failures.
Step 3: Decide who can invoke it, and what it can touch
name and description aren't the only fields. Claude Code also recognizes when_to_use, argument-hint, arguments, disable-model-invocation, user-invocable, allowed-tools, disallowed-tools, model, context, agent, background, license, compatibility, and metadata. Two decisions come up often enough to plan for upfront.
Who can invoke it. By default Claude can decide to use a skill, and you can also run it with /name. Set disable-model-invocation: true for anything with a side effect you want to time yourself, the way you wouldn't want Claude deciding to deploy because your code looks ready; changelog-entry is a reasonable candidate, since it writes to a file. Set user-invocable: false for background knowledge that isn't a command in its own right, like a skill that just explains how a legacy subsystem works.
What it's allowed to do. allowed-tools grants the listed tools for the turn that invokes the skill, so Claude doesn't stop to ask permission mid-task. It's a temporary addition, not a restriction, and it clears when you send your next message:
---
name: changelog-entry
disable-model-invocation: true
allowed-tools: Bash(git diff *) Bash(git log *)
---Because a skill can grant its own tool access, read allowed-tools in any skill you didn't write before you run it, the way you'd read a shell script before executing it. disallowed-tools does the opposite, pulling tools out of the pool for the skill's duration.
Two more things worth knowing before you ship: argument-hint and arguments let a skill take positional input, like /changelog-entry Fixed; and if you might package the skill for claude.ai or the Skills API rather than Claude Code alone, its frontmatter is validated against a narrower spec: only name, description, license, compatibility, metadata, and allowed-tools survive. Anything else fails the upload outright.
Step 4: Keep the body lean
Anthropic's guidance is to keep the SKILL.md body under 500 lines and push anything larger into Level 3. The more useful discipline is upstream of that limit: assume Claude already knows things, and stop explaining them.
## Write the entry
Run `git diff --stat` to see which files changed, then `git diff` for
the full change. Summarize the user-facing effect in one line, in the
imperative mood, under the matching CHANGELOG.md section header.That's the whole instruction. It doesn't explain what a diff is, because Claude already knows. It spends its tokens on what's specific to this skill: which command to run, what tense to use, where the output goes.
Step 5: Match instructions to how fragile each step is
Not every step deserves the same kind of guidance. Anthropic frames this as a spectrum:
- High freedom (prose): multiple approaches are valid and context should decide. Summarizing a diff's user-facing effect has no single correct sentence.
- Medium freedom (a template or parameterized function): a preferred pattern exists but some variation is fine.
- Low freedom (an exact script, run exactly as written): the step is fragile or must run in a specific sequence. Anthropic's own example is a database migration:
python scripts/migrate.py --verify --backup, with an instruction not to modify the command.
Writing every step in prose, including the ones that are actually narrow bridges, is how skills end up doing subtly the wrong thing in ways that are hard to diagnose.
Step 6: Push detail into files, not prose
Reference files for anything Claude only needs sometimes: a style guide, a full schema. Link to them: "See references/changelog-style.md for section naming." Claude reads the file only if the task needs it.
Utility scripts for anything that shouldn't be regenerated each run. A script that parses git diff into structured hunks is worth writing once; Anthropic notes scripts are more reliable than freshly generated code and save the tokens that would otherwise inline it.
If the skill uses MCP tools, name them precisely: ServerName:tool_name, for example GitHub:create_issue. Without the server prefix, Claude may not resolve the tool when more than one MCP server is connected.
Step 7: Test it on real tasks
A skill's real-world behavior deviates from what you'd predict reading the file. Hand it real tasks and watch: does it fire on the trigger phrases you intended, does it use the reference files you wrote, does it skip a step you assumed was obvious. For changelog-entry, that means running it against three or four real diffs from the project's own history and checking the output against what a maintainer actually wrote. If you'll use the skill across model tiers, test each one: a skill that reads as complete to a stronger model can read as underspecified to a faster, more literal one.
Troubleshooting
| Symptom | Fix |
|---|---|
| Skill never triggers | Add explicit trigger phrases to description |
| Skill fires on unrelated requests | Narrow the description; remove generic verbs |
| Claude runs a side-effecting skill on its own | Add disable-model-invocation: true |
| Claude keeps stopping to ask permission mid-skill | Add allowed-tools for those commands |
| "Tool not found" for an MCP call | Use the full ServerName:tool_name form |
| Upload fails with "Unexpected key(s)" | Trim to the six-field spec: name, description, license, compatibility, metadata, allowed-tools |
| SKILL.md keeps growing | Extract to a linked reference file |
Verification checklist
descriptionnames concrete trigger phrases, not just what the skill does.- Invocation is deliberate:
disable-model-invocationoruser-invocableis set where it matters. - Every tool the skill runs without prompting is listed in
allowed-tools, and nothing broader. - The body reads as a procedure, not an explanation.
- Each step's freedom level matches how fragile it actually is.
- The skill has run against a few real tasks, not just the scenario it was designed around.
Conclusion
The pattern here is the one good technical writing has always followed: say what's needed, when it's needed, and no sooner. A Claude Skill makes that discipline structural, three levels loaded in order, each one earning its place before the next opens. Get the frontmatter specific enough to be found, keep the body lean, and push everything else into files Claude only opens when the task calls for it. For real skills built this way, see the related reading below.
References
Related reading, two skills built with the invocation and tool-access controls covered above:

