Claude Code Skills: How to Write a SKILL.md That Actually Gets Used

How Claude Code skills work: folder layout, frontmatter, descriptions that trigger, steps with checks, scripts, testing, 3 example SKILL.md files, plugins.

A Claude Code skill is a folder with a SKILL.md file inside. The file has a short header and a procedure. When your request matches the header's description, Claude Code loads the procedure and follows it. That is the whole mechanism, and it is why most skills fail: the description is vague so the skill never loads, or the steps are vague so it loads and does nothing useful. This guide has 9 sections, 6 copy-paste blocks of prompts or commands, a 7-question Q&A, and takes about 27 minutes to read.

This guide shows you how to write a SKILL.md that actually gets used, from folder layout and frontmatter through testing, three annotated examples and plugins.

If you have never opened the tool, start with the Claude Code tutorial for beginners. For concepts first, read what Claude Code is. Skills are one module of the AI Mastery course, which builds a full skill library from scratch.

Claude Code Skills at a Glance

📄SKILL.mdRequired fileOne per skill folder
đŸˇī¸3 fieldsKey frontmatter fieldsname, description, argument-hint
📍2 scopesWhere skills liveProject folder or home folder
âŒ¨ī¸2 waysHow to triggerType /name or describe the task
đŸ“ĻPluginsHow to shareInstalled from marketplaces

Skill vs Slash Command vs CLAUDE.md

Claude Code has three ways to give it standing instructions. They solve different problems.

CLAUDE.md is always loaded. It sits in your project root (or your home folder for personal rules) and Claude reads it at the start of every session. Put things there that apply to every task: package manager, test command, branch naming, what never to touch. Every line costs you on every request, so keep it short.

A slash command is a prompt you trigger by typing /name. Historically these were single Markdown files in .claude/commands/. They load only when called, so they cost nothing until then. Good for a repeatable prompt with no supporting files.

A skill is a folder containing SKILL.md plus any scripts, templates and reference documents the procedure needs. You can trigger it with /name, but Claude can also load it on its own when your request matches the description. In current Claude Code the two systems have converged: a one-file skill behaves like a command.

Rule of thumb: rules go in CLAUDE.md, procedures go in skills.

Which One Do You Need?

Loaded: every session, automatically.

Best for: project facts and standing rules. "Use pnpm." "Never edit the migrations folder." "Run npm test before committing."

Cost: paid on every request, so keep it under a screen or two.

Cannot: bundle scripts or reference files.

Folder Structure and Frontmatter

Project skills go in .claude/skills/ inside the repository and travel with the code. Personal skills go in ~/.claude/skills/ and follow you across projects. Each skill is a folder named after the skill, with a SKILL.md inside.

my-project/
  .claude/
    skills/
      deploy-check/
        SKILL.md          <- required
        scripts/
          smoke-test.sh   <- optional helper
        reference/
          rollback.md     <- optional reading

Only the Markdown file is mandatory.

The file starts with a YAML frontmatter block between two --- lines, then the body. Three fields matter most.

---
name: deploy-check
description: Pre-deploy safety checklist for the web app. Use before any production restart or release.
argument-hint: "[service name]"
---

Body of the skill goes here.

Each field has one job.

name is the slash command. Lowercase, hyphens, no spaces, matching the folder name.

description is the most important line in the file. Claude reads it to decide whether to load the skill, and it shows in the picker when you type /. The next section is about it.

argument-hint is a placeholder shown in the picker. Whatever you type after the command name arrives in the body as $ARGUMENTS. Leave it empty if the skill takes no input.

Newer releases accept extra keys, for example to restrict which tools a skill may use or to stop Claude loading it automatically. The set changes between versions, so check the official docs before relying on one.

How Claude Decides to Load a Skill

At session start, Claude Code collects the name and description of every available skill and puts that list in the system prompt. The bodies are not loaded. When you type a request, Claude checks whether a description matches. If yes, the body is injected and the procedure runs. If no, the skill sits unused forever.

So the description is a trigger and a boundary at once. A bad one is a title.

# Bad: never triggers on a natural request
description: Deploy stuff

# Bad: too generic, fires on the wrong things
description: Helps with the website

# Good: specific verbs, trigger phrases, exclusions
description: Pre-deploy safety checklist for the web app.
  Checks traffic, uncommitted changes and last release,
  then restarts with cache purge. Use when the user says
  deploy, release, restart production or push live.
  Do NOT use for local dev servers.

Three things make the third version work.

It names the actions in plain verbs. It lists the phrases a human would actually type; Claude matches on meaning, but your team's vocabulary raises the hit rate. And it says what the skill is not for, which stops it loading on an unrelated request and wasting context.

A practical method: write down the sentence you typed the last five times you wanted this procedure and make sure the description matches all five. Keep it under a few hundred characters; descriptions load into every session whether or not the skill fires.

Writing Procedural Steps With Checks

The best skill bodies read like a runbook for a careful new hire: numbered steps, a check after each, and an explicit action when a check fails. Claude follows numbered steps reliably. It follows "be careful" not at all.

A pattern that works in real skills has two parts: a data section that gathers live facts, and a task section that says what to do with them. Skill bodies support inline shell execution with the !`command` syntax: the command runs when the skill loads and its output replaces the placeholder, so Claude sees real state instead of guessing.

## Live state

**Uncommitted changes:**
!`git status --short | head -20`

**Last five commits:**
!`git log --oneline -5`

---

## Your task

1. Review the live state above. If there are uncommitted
   changes, STOP and ask the user whether to commit or stash.
2. Run `npm test`. If any test fails, STOP and report the
   failing test names. Do not deploy.
3. Run `bash scripts/deploy.sh`.
4. Verify: `curl -s -o /dev/null -w "%{http_code}" https://example.com/`
   must print 200. If not, run `bash scripts/rollback.sh` and report.
5. Report: what was deployed, the HTTP status, any warnings.

Notice the shape of each step.

Every step has a concrete command. Every command has a success condition. Every failure has a named next action: STOP and report, or an automatic rollback. Claude will not improvise a rollback if you never said one exists.

Use "STOP and ask the user" for anything destructive or outward-facing: deploys, payments, emails, deleting data, force pushes. Use "STOP and report" for failed checks. This mirrors the guardrail thinking in the guide to building an AI agent: the model plans and acts, a human approves the irreversible moves.

End with a report format, or you get narration. And keep each skill focused: one that handles deploy, rollback, log inspection and incident writeup is hard to trigger and hard to follow. Make four small ones.

Anatomy of a Step That Claude Follows

â–ļī¸One concrete action

A single command, file edit or question. Not "prepare the release", but "run npm run build and wait for it to finish".

🔍A visible success condition

Exit code, an expected line of output, a file that must exist, an HTTP status. Something Claude can actually observe.

🛑A named failure path

STOP and ask, STOP and report, retry once, or roll back. Never leave the failure case to imagination.

📋A report format

Tell Claude exactly what the final message should contain so you can read it in five seconds.

Including Scripts and Reference Files

Skills are folders, not single files, so you can ship the procedure with its tools. Two kinds of supporting file are common.

Scripts are helpers the procedure calls. If step three is a forty-line shell pipeline, put it in scripts/check-links.sh and have the step say "run bash scripts/check-links.sh and read its output". The Markdown stays readable, the script can be tested alone, and Claude cannot mangle a command it only has to invoke.

Reference files are documents Claude should read only when needed: an API cheat sheet, a style guide, a list of known error messages with fixes. Point at them conditionally.

3. Run `bash scripts/smoke-test.sh`.
4. If the output contains TIMEOUT, read `reference/timeouts.md`
   in this folder and apply the matching fix before retrying once.
5. If the output contains FORBIDDEN, STOP and tell the user
   to check their credentials. Do not retry.

This keeps the main file short.

Use paths relative to the skill folder, never absolute home-folder paths. That is what lets the skill work for a teammate and later move into a plugin. Never put secrets in a skill; skills get committed and shared. Reference an environment variable and say so: "read the token from $SERVICE_TOKEN; if unset, STOP and tell the user how to set it".

A template is a third useful file: templates/report.md with headings in place, and a final step that says "fill this template, do not change the headings". Consistent output across runs is one of the main reasons to write a skill at all.

Versioning and Testing a Skill

Treat a skill like code. It runs, it has inputs, it fails in specific ways, and it drifts as the project changes.

Version it in git. Project skills already live in the repo. For personal skills, make ~/.claude a git repository. Add a dated one-line changelog at the bottom of SKILL.md; when a skill misbehaves, that is the first thing you will want.

Test the trigger. In a fresh session, type three natural requests that should load the skill and two that should not. Adjust the description and repeat. Five minutes, and it catches most "my skill never fires" problems.

Test the procedure. Run it on a real, low-stakes case. The most common bug is a step phrased as a suggestion ("you might want to run the tests") that Claude treats as optional. Rewrite it as an instruction with a condition.

Break it on purpose. Rename the test script or unset a variable and run the skill. If it charges ahead, your STOP conditions are too weak. Make them explicit and put them in capitals; Claude notices emphasis.

Use the eval tooling. Recent releases include tooling for evaluating plugins and flagging weak skill descriptions; names and flags change, so run claude --help rather than copying an old post. And re-read skills after any infrastructure change; a deploy skill written for one hosting setup will confidently run the wrong commands after a migration.

Three Example Skills, Annotated

Trimmed to fit on a page, but the shape is what runs in practice. Copy the structure, not the specifics.

1. Deploy checklist

--- name: deploy-check description: Pre-deploy checklist for the web app. Checks uncommitted changes, runs tests, deploys, verifies the site is up. Use for deploy, release, push live, restart prod. argument-hint: "[optional note]" --- ## Live state **Uncommitted:** !`git status --short | head` **Last commits:** !`git log --oneline -3` ## Your task 1. If there are uncommitted changes, STOP and ask whether to commit, stash or abort. 2. Run `npm test`. On any failure, STOP and list the failing tests.

Do not continue. 3. Run `bash scripts/deploy.sh`. 4. Verify `curl -s -o /dev/null -w "%{http_code}" https://example.com/` prints 200. Otherwise run `bash scripts/rollback.sh` and report. 5. Report in 4 lines: commit deployed, test result, HTTP status, warnings. Mention $ARGUMENTS if provided.

Live data first, then numbered steps.

The description lists four trigger phrases. State is gathered before Claude has to think. Step one hard-stops on a dirty working tree, step four pairs the verify with a rollback, and the report is fixed at four lines.

2. Code review

--- name: review description: Structured code review of the current diff or a named file. Finds bugs, security issues and missing tests, ranked by severity. Use for review, check my changes, look over this PR. Not for formatting or style-only passes. argument-hint: "[file or PR number]" --- ## Diff under review !`git diff --stat HEAD~1 2>/dev/null | tail -15` ## Your task 1. If $ARGUMENTS names a file, review only that file.

Otherwise review the full diff of the last commit. 2. Read `reference/checklist.md` in this folder. It lists the project's known failure patterns. Check each one. 3. For every finding give: file and line, severity (critical / major / minor), why it matters, a concrete fix. 4. Do NOT edit any file. This skill is read-only. 5. End with one line: SHIP, SHIP WITH FIXES, or BLOCK. If nothing was found, say so instead of inventing issues.

Read-only by instruction.

The "not for" clause keeps a formatting request from loading a bug hunt. A reference file holds the project-specific checklist so the body stays generic. Step four makes the skill read-only, and step five forbids padding: an honest "nothing found" beats three invented nitpicks.

3. Weekly report

--- name: weekly-report description: Builds the Monday status report from git history, the issue tracker export and last week's report. Use for weekly report, status update, what happened last week. argument-hint: "[week ending date]" --- ## Inputs **Commits this week:** !`git log --since="7 days ago" --oneline` **Previous report:** read `reports/latest.md` if it exists. ## Your task 1. Group the commits into 3 to 6 themes.

Ignore merge commits and dependency bumps. 2. Run `python3 scripts/issues.py --week $ARGUMENTS` and read the opened and closed counts from its output. If it fails, STOP and report the error. Do not estimate numbers. 3. Fill `templates/report.md`. Keep every heading. Under Risks, list only items backed by a commit or an issue. 4. Save to `reports/YYYY-MM-DD.md` and copy to `reports/latest.md`. 5. Report the file path and the theme headings, nothing else.

Numbers come from a script, never from guessing.

If the script fails, the skill stops rather than estimates. That one line prevents the worst failure mode of AI-written reports: confident invented figures. The template keeps output consistent week to week.

Skill Release Checklist

  • ✓Folder name matches the name field: lowercase letters and hyphens only
  • ✓Description names the actions, lists trigger phrases and says what the skill is NOT for
  • ✓Three natural requests load the skill and two nearby requests do not
  • ✓Every step has a concrete command and a visible success condition
  • ✓Every failure has a named next action: STOP and ask, STOP and report, retry, roll back
  • ✓Long commands live in scripts/, long reading in reference/, no secrets anywhere
  • ✓A report format is specified at the end of the task section
  • ✓A dated changelog line sits at the bottom of SKILL.md
  • ✓The skill ran once on a real low-stakes case and once with a deliberate failure
  • ✓The empty $ARGUMENTS case is handled explicitly in step one

Plugins and Marketplaces

A plugin lets you share a bundle of skills, subagents, hooks and MCP server configs with anyone. Structurally it is a folder with a manifest plus skills/, agents/ and hooks/ subfolders. Skills inside a plugin are exactly the SKILL.md folders described above, which is why portable skills (relative paths, no secrets) pay off.

A marketplace is a catalog of plugins, usually a git repository with an index file. You add a marketplace, browse it and install from it with the /plugin command. Anthropic maintains an official one; companies often run a private one. Confirm the current syntax in the docs.

/plugin marketplace add owner/repo-name
/plugin install plugin-name@marketplace-name
/plugin list

Installed plugin skills are namespaced.

A skill called review from a plugin called acme-tools appears as acme-tools:review, so two plugins cannot collide on common names and you can see where a skill came from.

Before installing a third-party plugin, read its SKILL.md files and hooks; a hook is a script that runs automatically on events. Treat it like a dependency with a postinstall script, the same prompt-injection awareness you need with MCP servers: anything that puts text or tools into the agent's context can steer it.

Sharing Skills as a Plugin: Trade-offs

✅Pros
  • +One install gives a teammate every skill, agent and hook in the bundle
  • +Namespacing prevents collisions with their own skills
  • +Versioned in a marketplace, so updates are a single command
  • +Forces you to remove hard-coded paths and secrets, which improves the skills
  • +Hooks and MCP configs travel with the skills that depend on them
❌Cons
  • −Every installed plugin adds skill descriptions to every session's context
  • −Third-party plugins run instructions and hooks with your permissions
  • −Plugin manifest format and commands have changed across 2026 releases
  • −A private marketplace needs someone to maintain the index and review changes
  • −Debugging a skill inside a plugin cache is slower than editing a local file

Common Mistakes

A title where the description should be. "Deploy helper" tells Claude nothing about when to load the skill.

Putting the procedure in CLAUDE.md. Now every request pays for a deploy runbook. Move it to a skill and leave a pointer: "For deploys, use /deploy-check."

Steps without checks. "Deploy the app" is not a step. "Run the deploy script, confirm the health endpoint returns 200, otherwise roll back" is.

Suggestions instead of instructions. "Consider running the tests" is optional. "Run npm test. On failure, STOP." is not.

Everything in one file. Long commands and reference material bury the steps. Split them out.

Absolute paths and secrets. Both break for the next user, and the second is a security problem the moment it is committed.

Never testing the trigger. The skill is perfect and nobody sees it fire.

If you fix one thing, fix the description; everything else is wasted if the skill never loads. For a guided path through skills, subagents, hooks and full agent builds with quizzes, see the full Claude Code and AI agents course. For the terminal-agent alternative, the Codex CLI tutorial shows how AGENTS.md plays a similar role.

Keep the official skills documentation and plugins documentation open while you write.

Claude Code Skills Questions and Answers

Go deeper: the full AI Mastery course

This guide covers one piece of Claude Code. The AI Mastery course is 60 lessons across 12 modules, beginner to advanced: Gemini, Claude Code (including a full skills, subagents and hooks module), OpenAI Codex, building AI agents, Seedance video generation and building websites with AI tools. Each module ends with a quiz, you keep lifetime access, and you can preview 2 lessons free.

About the Author

Dr. Lisa Patel
Dr. Lisa PatelEdD, MA Education, Certified Test Prep Specialist

Educational Psychologist & Academic Test Preparation Expert

Columbia University Teachers College

Dr. Lisa Patel holds a Doctorate in Education from Columbia University Teachers College and has spent 17 years researching standardized test design and academic assessment. She has developed preparation programs for SAT, ACT, GRE, LSAT, UCAT, and numerous professional licensing exams, helping students of all backgrounds achieve their target scores.