Skip to content

How to Create a Claude Code Skill: SKILL.md, Scope, and Publishing

By Martin Jeřábek, Founder & CEO

Published September 16, 2026 · 12 min read

A skill is one Markdown file. This is how to write one that actually fires when you need it, how it differs from CLAUDE.md, MCP servers and hooks, and what it takes to publish one other people can trust.

A Claude Code skill is a Markdown file. Not a plugin, not a package, not a service — a file called SKILL.md sitting in a directory on your machine. Claude Code reads the directory, registers a slash command named after it, and follows the instructions inside when you invoke it. There is no runtime to install, no account to create, and no network call involved in using one. That single fact explains almost everything else about how skills behave, including why writing a good one is a writing problem rather than an engineering problem.

Source: Anthropic's skills reference

Full disclosure of our bias: we publish three skills ourselves and curate a directory of nineteen. Everything below comes from writing, breaking and rewriting them on our own production codebase — including this website.

The anatomy of a SKILL.md

A skill lives at ~/.claude/skills/<name>/SKILL.md. The directory name becomes the slash command, so a directory called handoff gives you /handoff. The file itself is YAML frontmatter followed by Markdown instructions:

markdown
---
name: changelog
description: Generate a release changelog from git history. Use when the user asks for release notes, a changelog, or what shipped since the last tag.
---

# Changelog

Read the commits since the last git tag and group them by type.

1. Run `git log $(git describe --tags --abbrev=0)..HEAD --oneline`.
2. Group into Features, Fixes, and Internal.
3. Drop anything that only touches CI or formatting.
4. Write the result to CHANGELOG.md under a new version heading.

That is a complete, working skill. Two frontmatter fields and a numbered list. The instinct to reach for more structure is worth resisting until the simple version has actually failed you.

The description field is the trigger, not documentation

This is the single thing most first attempts get wrong. The description is not a summary for humans browsing a list — it is the text the model reads to decide whether this skill is relevant to what you just asked. A description that says “Changelog generator” tells the model almost nothing about when to reach for it. A description that names the situations — “Use when the user asks for release notes, a changelog, or what shipped since the last tag” — gets the skill invoked at the right moment.

Write the description as a list of the moments the skill should fire, in the words a user would actually type. Then reread it pretending you know nothing about the skill, and ask whether you could tell from that sentence alone when to use it.

Personal scope, project scope

There are two places a skill can live, and the choice is about who should have it. ~/.claude/skills/ is personal: those skills are available in every project you open on that machine, and nobody else gets them. A repository's own .claude/skills/ directory ships with the codebase, so the skill travels with the work and every contributor gets it on clone.

The rule we use: if the skill encodes how this project works, it belongs in the repo. If it encodes how you work, it belongs in your home directory. A deploy runbook for one service is project scope. A personal preference for how you like commits formatted is not something to impose on your colleagues.

Four things that get conflated

Skills, CLAUDE.md, MCP servers and hooks solve four different problems, and conversations about them go wrong when they are treated as alternatives. They are not.

  • SKILL.md — a Markdown file of instructions plus the slash command that invokes them. Local, and inert until you type the command.
  • CLAUDE.md — always-on project context, loaded every session whether you ask for it or not. Not a command; a standing brief.
  • MCP server — a separate service Claude connects to over a transport so it can reach systems a local file cannot. Usually needs credentials.
  • Hooks — shell scripts the harness runs on lifecycle events like session stop or prompt submit, with no model involvement at all.

The practical consequence: put standing facts in CLAUDE.md, put procedures behind a slash command in a skill, reach for an MCP server only when Claude genuinely needs to touch a system outside your filesystem, and use hooks when something must happen every time regardless of what the model decides.

Progressive disclosure: keep the skill small

Everything in a SKILL.md is loaded into context when the skill fires, and context is the budget that actually constrains long sessions. A 900-line skill that covers every edge case costs you that budget on every invocation, including the nine times out of ten when you needed the first twenty lines.

The fix is to keep SKILL.md as the decision layer and push detail into files beside it that Claude reads only when it needs them. A reference.md for the long lookup table, a scripts/ directory for anything deterministic enough to be code. Our own handoff skill does this: the main file is the four-mode decision tree, and a project-specific overlay sits at the bottom where it is ignored entirely in projects it does not apply to.

Anything you can express as a script should be a script, not instructions. A model asked to follow ten deterministic steps will occasionally improvise on step seven. A shell script will not.

Hooks: when something must happen every time

A skill fires when invoked. A hook fires whether or not anyone remembered it. That distinction is the whole reason to write one, and the clearest example we have is the auto-wrap pair that ships alongside our handoff skill.

A long session does not fail loudly. Recall gets patchier and answers get vaguer as the prompt grows, and the moment you notice is well past the moment it started. These hooks read the session transcript and sum input_tokens, cache_creation_input_tokens and cache_read_input_tokens from the last assistant message. That total is the exact prompt size of the most recent API call — system prompt, CLAUDE.md, every loaded skill, every file read this session. Most context warnings guess from message counts or transcript byte size, which is meaningless once the window is a million tokens wide.

json
{
  "hooks": {
    "Stop": [{ "hooks": [{ "type": "command",
      "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/auto-wrap.sh\"", "timeout": 10 }] }],
    "UserPromptSubmit": [{ "hooks": [{ "type": "command",
      "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-warn.sh\"", "timeout": 10 }] }]
  }
}

Two thresholds, asking for different things. At 20% of the window a soft nudge asks for a checkpoint: write the state file, commit nothing, keep working. At 22% the hard nudge asks for the full wrap — stop new work and end the session. Splitting them is the point, because a soft nudge that ended the session mid-task only ever got ignored, and a checkpoint costs about two tool calls, so it can always be obeyed. Each threshold fires at most once per session, and a hook cannot compact or discard context — it is an instruction to the model, nothing more, so firing one never costs you anything.

The cost is two dependencies and a manual step: the hooks are shell scripts, so they need bash and jq, which on Windows means Git Bash. The thresholds and the assumed window size are all environment variables, so none of the three numbers is baked in.

MCP servers: when a file is not enough

A skill can tell Claude how to do something. It cannot give Claude access to a system it has no path to. That is what MCP servers are for — your issue tracker, a real browser, a production database, an error feed. Skills need no network and no credentials; MCP servers are network services and usually need both. The setup is one command per server:

bash
claude mcp add --transport http github https://api.githubcopilot.com/mcp
claude mcp add --transport http context7 https://mcp.context7.com/mcp
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
claude mcp add playwright -- npx @playwright/mcp@latest

Context7 is the one we would suggest first if you only add one: it injects version-aware library docs and API signatures on demand, which removes a whole category of confidently wrong API calls. Playwright is the one that changes how verification works, because it gives Claude a real browser to check its own UI changes in rather than asserting they look right.

Testing a skill before you trust it

A skill has two failure modes and they need different tests. The first is that it never fires — you ask for the thing and the model does it from scratch, ignoring your carefully written file. That is almost always the description field, and the test is to phrase the request three different ways and see whether the skill triggers each time. The second is that it fires and does the wrong thing, which is an instruction problem: run it on a case you already know the answer to, and read what it actually did rather than what it reported doing.

If a skill touches anything destructive, pin its guardrails with a test script rather than trusting the prose. We learned this the expensive way on a safety hook whose pattern was too broad: a too-broad pattern announces itself immediately by blocking legitimate work, but a too-narrow one never announces itself at all — and narrowing happens under exactly the frustration that invites overshooting.

Publishing one other people can use

There is no registry for bare SKILL.md files and no claude skills install command. Publishing a skill means putting the file in a public repository and telling people where it is. Packaged plugins do have an official marketplace, but a single skill does not need one — which is also why every entry in our directory links out to its author's repository instead of installing anything on a visitor's behalf.

Three things make a published skill usable by strangers. Put a licence in the repo, because a skill with no licence is one a company's legal review will reject. Say plainly what it writes and how to undo it — ours leaves a git clone in your working directory and files under ~/.claude/skills/, and the uninstall instructions remove both. And do not publish adoption numbers you cannot source: there is no telemetry in a Markdown file, no install counter, and nothing stopping anyone from inventing a figure, which is exactly why a directory quoting install counts deserves the question of where the number came from and what it counts.

Source: our own skills and their MIT licence

The two we wrote

Handoff is a session lifecycle in one skill. /handoff start reads a state file and briefs the session on where the last one stopped; /handoff wrap updates that file and commits; /handoff save is a fire exit that writes a snapshot in a handful of tool calls; /handoff docs is the mid-session checkpoint the auto-wrap hooks ask for. The state file is capped at roughly 120 lines and overflow rotates into an archive that is never read on start, which is the part that actually matters: a handoff file that grows without limit costs more context every session until it becomes the problem it was written to solve.

Build KB scans a project and writes a single chatbot-ready knowledge-base.md, organised into fixed sections and skipping any section the repository has no source for rather than inventing one. It exists because the alternative — pasting a repo into a chat window and asking for a summary — produces confident text about things the codebase never said.

Source: the full directory of Claude Code skills

Both are MIT licensed. Every other entry in our directory belongs to its author, under that author's terms — we hold no licence data for third-party skills and will not guess on your behalf.

Start with the smallest thing that annoys you

The best first skill is a procedure you have typed out more than twice. Not an ambitious framework — a checklist you keep repeating. Write it as a numbered list, give it a description that names the moments it should fire, and use it for a week. The edges you did not anticipate will show up on their own, and they are far better information than anything you could have planned for in advance.

Source: our complete guide to Claude Code