How to Create Custom Claude Code Skills: Complete Tutorial (2026)
Learn how to build custom Claude Code Agent Skills step by step — SKILL.md structure, frontmatter fields, arguments, dynamic context, and real examples.
How to Create Custom Claude Code Skills: A Step-by-Step Tutorial
If you've ever pasted the same multi-step instructions into Claude Code more than twice — a deploy checklist, a code review rubric, a "how our API is structured" primer — you've already found your first candidate for a Claude Code Skill. Skills are the mechanism Claude Code uses to package reusable instructions, scripts, and reference material into something Claude can load automatically or you can trigger with a slash command.
Unlike copy-pasting into CLAUDE.md, a skill's content only loads into context when it's actually used. That means you can build a library of dozens of specialized skills — deployment runbooks, API conventions, research workflows — without permanently bloating every conversation with tokens you don't need most of the time.
This tutorial walks through building skills from scratch: the file structure, the frontmatter fields that control behavior, and three working examples you can copy today.
What a Claude Code Skill Actually Is
A skill is a directory containing a required SKILL.md file. That file has two parts:
--- markers — metadata that tells Claude when and how to use the skillThe directory name becomes the slash command. A skill at .claude/skills/deploy/SKILL.md gives you /deploy. Claude can also invoke matching skills automatically based on your conversation, using the description field to decide relevance — no typing required.
Skills follow the open Agent Skills standard, so skills you write for Claude Code are portable across other tools that adopt the same spec.
Skills vs. CLAUDE.md vs. Custom Commands
| Approach | When it loads | Best for |
|---|---|---|
CLAUDE.md | Every single turn, always | Facts about your codebase that are always relevant |
Custom command (.claude/commands/) | Only when invoked | Simple, fixed prompts (still works, but skills supersede it) |
Skill (.claude/skills/) | Only when invoked or matched | Procedures, checklists, workflows — anything that's long but situational |
If a section of your CLAUDE.md has grown from "a fact about the repo" into "a multi-step procedure," that's your signal to move it into a skill instead.
Where Skills Live
Skill location determines who can use it:
| Location | Path | Scope |
|---|---|---|
| Personal | ~/.claude/skills/ | All your projects |
| Project | .claude/skills/ | This repo only (commit it to share with your team) |
| Plugin | | Wherever the plugin is installed |
| Enterprise | Managed settings | Every user in your org |
Project skills also load from nested .claude/skills/ directories in monorepos, so a package at apps/web/ can ship its own skills that apply only when you're working inside it.
Step-by-Step: Build Your First Skill
Let's build a skill that summarizes uncommitted git changes and flags anything risky — a task you'd otherwise re-explain to Claude every time.
Step 1 — Create the directory:bashmkdir -p ~/.claude/skills/summarize-changesSKILL.md:
yaml---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---
## Current changes
!`git diff HEAD`
## Instructions
Summarize the changes above in two or three bullet points, then list
any risks you notice — missing error handling, hardcoded values, or
tests that need updating. If the diff is empty, say there are no
uncommitted changes.The ` !git diff HEAD ` line is dynamic context injection: Claude Code runs that shell command before Claude ever sees the prompt, and swaps in the actual output. Claude reasons over your real, current diff — not a guess based on open files.
What did I change?) or invoke it directly with /summarize-changes. Either path should return a summary grounded in your live diff.
The Frontmatter Fields That Matter Most
All frontmatter fields are optional except that description is strongly recommended — it's what Claude matches against your conversation to decide when to auto-load the skill. The fields worth knowing early on:
disable-model-invocation: true— only you can trigger the skill with/name. Use this for anything with side effects: deploys, commits, sending Slack messages. You don't want Claude deciding on its own that it's time to ship.allowed-tools— grants specific tools without a permission prompt while the skill is active, e.g.allowed-tools: Bash(git add ) Bash(git commit ).argument-hintandarguments— declare named parameters for$ARGUMENTS,$0/$1, or$namesubstitution in the skill body.context: forkplusagent— runs the skill in an isolated subagent (built-inExplore,Plan,general-purpose, or a custom agent), rather than inline in your current conversation.user-invocable: false— hides the skill from the/menu entirely; useful for background knowledge Claude should know but that isn't a meaningful action for a human to run.
Example: A Skill With Arguments
Skills that take arguments are ideal for repeatable, parameterized workflows — like fixing a specific ticket:
yaml---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---
Fix GitHub issue $ARGUMENTS following our coding standards.
1. Read the issue description
2. Understand the requirements
3. Implement the fix
4. Write tests
5. Create a commitRunning /fix-issue 123 replaces $ARGUMENTS with 123, and Claude receives the fully expanded instruction. For multiple positional values, use $0, $1, $2 (or declare named arguments in frontmatter) instead of one combined string:
yaml---
name: migrate-component
description: Migrate a component from one framework to another
---
Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests./migrate-component SearchBar React Vue maps cleanly to SearchBar, React, and Vue.
Example: A Skill That Bundles a Script
Skills aren't limited to prose — they can ship and execute real code. This keeps SKILL.md focused while offloading heavy lifting to a script:
textmy-skill/
├── SKILL.md # required — overview and navigation
├── reference.md # detailed docs, loaded only when needed
└── scripts/
└── helper.py # executed, not loaded into contextReference the script from SKILL.md using ${CLAUDE_SKILL_DIR}, which resolves correctly no matter where the skill is installed:
yaml---
name: codebase-visualizer
description: Generate an interactive tree visualization of your codebase. Use when exploring a new repo or identifying large files.
allowed-tools: Bash(python3 *)
---
Run the visualization script from your project root:
python3 ${CLAUDE_SKILL_DIR}/scripts/visualize.py .
This creates codebase-map.html and opens it in your default browser.This pattern generalizes well: dependency graphs, coverage reports, schema diagrams — anywhere a deterministic script beats free-form generation.
Injecting Dynamic Context the Right Way
The ` ! ` syntax you saw in the git-diff example is worth understanding properly, because it's the difference between a skill that reasons about stale assumptions and one that reasons about ground truth. Every backtick-wrapped command in a skill body runs before Claude sees any of the content — the output is spliced in as plain text, and Claude never executes the command itself. That matters for two reasons:
!command ` placeholders, so a command can't chain into a second expansion.For multi-line shell work, use a fenced block instead of the inline form:
`
markdown## Environmentnode --version
npm --version
git status --short
This is especially useful for skills that need an up-to-date snapshot of environment state — dependency versions, running processes, current branch — without asking Claude to go fetch it via separate tool calls first.
Running a Skill in an Isolated Subagent
By default, a skill's instructions run inline, in your current conversation, with full access to everything you've discussed so far. Sometimes that's the wrong shape — a research task or a large refactor plan can pollute your main context with exploration noise you don't need to keep around. Add context: fork to run the skill in a forked subagent instead:
yaml---
name: deep-research
description: Research a topic thoroughly
context: fork
agent: Explore
---
Research $ARGUMENTS thoroughly:
1. Find relevant files using Glob and Grep
2. Read and analyze the code
3. Summarize findings with specific file referencesWhen this runs, a new isolated context spins up, the skill body becomes that subagent's entire task (it has no access to your prior conversation), and only the final summary comes back to your main session. The agent field picks the execution environment — built-in options are Explore, Plan, and general-purpose, or you can point it at any custom subagent defined in .claude/agents/.
One caveat: context: fork only makes sense when your skill contains an actual task. If the body is just reference guidance ("use these API conventions"), a forked subagent receives guidance with nothing to act on and returns nothing useful.
Controlling Who Can Trigger a Skill
Two frontmatter fields govern invocation, and mixing them up is a common source of confusion:
| Frontmatter | You can run it | Claude can run it automatically |
|---|---|---|
| (default — neither set) | Yes | Yes |
disable-model-invocation: true | Yes | No |
user-invocable: false | No | Yes |
Use disable-model-invocation: true for anything you want to gate behind explicit human action — /deploy, /commit, /send-slack-message. Use user-invocable: false for the opposite case: background knowledge Claude should apply automatically (say, a legacy-system-context skill explaining a quirky old subsystem) where typing /legacy-system-context yourself wouldn't be a meaningful action.
You can also lock down skill access globally through permission rules rather than editing individual files:
text# Allow only specific skills
Skill(commit)
Skill(review-pr *)
# Deny specific skills
Skill(deploy *)This is worth doing on any shared or CI-facing Claude Code setup, since a project skill checked into .claude/skills/ can grant itself tool access via allowed-tools — review skills the same way you'd review a dependency before trusting a new repo.
Evaluating Whether a Skill Actually Works
Seeing Claude trigger your skill tells you it found it — not that the output is any good. The reliable way to check both is a baseline comparison: run the same realistic prompts in a fresh session with the skill available, then again with it disabled, and compare results. Do this in a fresh session each time; leftover context from writing the skill will hide gaps in your instructions that a new user would immediately hit.
Anthropic ships a skill-creator plugin that automates this loop:
text/plugin install skill-creator@claude-plugins-officialAfter installing and running /reload-plugins, ask Claude to evaluate my summarize-changes skill with skill-creator. It walks you through recording test cases, runs each one in an isolated subagent, grades the output against your assertions, and benchmarks pass rate, token cost, and duration with the skill on versus off — so you can see whether a skill is worth its context overhead before you commit to it.
Common Mistakes to Avoid
- Vague descriptions. If Claude isn't triggering your skill automatically, the
descriptionprobably doesn't contain the words a user would naturally say. Rewrite it with the actual trigger phrases in mind. - Skills that never end. A skill's rendered content stays in context for the rest of the session once invoked — it isn't re-read each turn. Keep the body under roughly 500 lines and push detail into linked reference files instead.
- No manual-only gate on destructive actions. Anything that deploys, deletes, or sends external messages should set
disable-model-invocation: trueso Claude can't fire it unprompted. - Forgetting to test with a fresh session. If you author a skill in the same session you're using it, leftover context masks gaps in your instructions. Test in a clean session, and ideally compare behavior with the skill temporarily disabled via
skillOverrides.
Troubleshooting: Skill Not Triggering
If Claude ignores a skill you expect it to use, work through these in order:
description. It needs to contain the phrases a user would naturally type, not internal jargon. "Use when the user asks what changed" beats "Handles diff summarization."What skills are available?/skill-name to isolate whether the problem is discovery (Claude doesn't know to use it) or execution (the instructions themselves don't work)./skill-name work, but Claude has no description to match against, so automatic triggering silently fails. Run Claude Code with --debug to surface the parse error.If a skill fires too often instead, tighten the description to be more specific, or add disable-model-invocation: true to remove it from automatic consideration entirely.
If you're maintaining a large skill library, run /doctor periodically. As the number of skills grows, Claude Code trims less-used descriptions to stay within a context budget — /doctor shows which skills are being shortened or dropped, which is often the real reason a skill that "used to work" stops triggering.
Sharing Skills With Your Team
Skills are just files, so distribution follows normal version control:
- Project skills — commit
.claude/skills/to your repo. Anyone who clones it and starts Claude Code inherits the skills automatically, no extra install step. - Plugins — package a
skills/directory inside a Claude Code plugin when you want to bundle skills together with agents, hooks, or MCP servers, or distribute them outside a single repo. Plugin skills are namespaced asplugin-name:skill-name, so they never collide with your personal or project skills. - Managed/enterprise — organizations can push skills to every developer through managed settings, which is the right layer for security or compliance procedures that shouldn't depend on an individual repo.
For a monorepo, nested .claude/skills/ directories let individual packages ship skills that only apply while you're working inside them — a deploy skill at the repo root and another inside apps/web/.claude/skills/ can coexist, with Claude resolving to apps/web:deploy when there's a naming collision.
Key Takeaways
- A skill is a directory with
SKILL.md: YAML frontmatter for behavior, markdown for instructions. - Personal skills (
~/.claude/skills/) apply everywhere; project skills (.claude/skills/) are scoped and shareable via git. descriptiondrives automatic triggering — write it the way a user would actually phrase the request.- Use
disable-model-invocation: truefor anything with side effects, andcontext: forkwhen a task should run isolated in a subagent. - Skills can bundle real scripts, not just prompts, referenced via
${CLAUDE_SKILL_DIR}.
Next Steps
Once you're comfortable authoring skills, pair them with Claude Code subagents for workflows that need isolated context, or read our CLAUDE.md best practices guide to decide what belongs in always-loaded memory versus an on-demand skill. If you're studying for the Claude Certified Architect exam, skill authoring patterns like these show up directly in the CCA curriculum — check out our CCA exam guide and practice test bank to get exam-ready.
Ready to Start Practicing?
300+ scenario-based practice questions covering all 5 CCA domains. Detailed explanations for every answer.
Free CCA Study Kit
Get domain cheat sheets, anti-pattern flashcards, and weekly exam tips. No spam, unsubscribe anytime.