← All Posts

Building Custom Skills for Claude Code: A Complete Tutorial

From the guide: Claude Code Comprehensive Guide

Three sessions in a row, I pasted the same security checklist into Claude Code. The checklist had our team’s specific vulnerability patterns — the IDOR checks unique to our API design, the session handling rules for our auth flow, the data exposure rules for our PII fields. Each time, Claude applied them perfectly. Each time, I had to remember to paste them.

The moment you catch yourself re-explaining the same context is the moment you should build a skill.

TL;DR

Skills are model-invoked extensions — Claude discovers and applies them automatically based on context, without you explicitly calling them 1. The key to reliable skills is the description field: Claude uses LLM reasoning (not keyword matching) to decide when to activate each skill 1. Build skills for domain expertise that applies across sessions (security patterns, code style, business rules). Don’t build skills for one-off tasks — use slash commands instead.


Prerequisites: Familiarity with Claude Code’s extension system. For the comparison between skills, commands, and subagents, see the Skills section of the guide.

When to Build a Skill

Not every repeated prompt deserves a skill. The decision framework:

Situation Build a… Why
You paste the same checklist every session Skill Domain expertise that auto-activates
You run the same command sequence explicitly Slash command User-invoked action with predictable trigger
You need isolated analysis that shouldn’t pollute context Subagent Separate context window for focused work
You need a one-time prompt with specific instructions Nothing Just type it. Not everything needs abstraction.

Skills are for knowledge Claude always has available. Slash commands are for actions you explicitly trigger. If you’re deciding between the two, ask: “Should Claude apply this automatically, or should I decide when to run it?”

A common mistake: building a skill for something you do once a week. I built a git-rebase-helper skill that activated on any git-related prompt — rebases, merges, cherry-picks, even git status. The description was too broad, it polluted context on 80% of sessions where it wasn’t needed, and it competed with other skills for the 2% context budget 1. The fix was deleting the skill and using a slash command instead: /rebase when I actually needed it. Skills should encode stable domain expertise, not occasional workflows.

Tutorial: Build a Code Review Skill

Step 1: Create the directory

Skills live in four possible locations, from broadest to narrowest scope 1:

Scope Location Applies to
Enterprise Managed settings All users in your organization
Personal ~/.claude/skills/<name>/SKILL.md All your projects
Project .claude/skills/<name>/SKILL.md This project only
Plugin <plugin>/skills/<name>/SKILL.md Where plugin is enabled

For this tutorial, we’ll create a personal skill:

mkdir -p ~/.claude/skills/code-reviewer

Step 2: Write SKILL.md with frontmatter

Every skill needs a SKILL.md file with two parts: YAML frontmatter (between --- markers) that tells Claude when to use the skill, and markdown content with instructions Claude follows when the skill is invoked 1.

---
name: code-reviewer
description: Review code for security vulnerabilities, performance issues,
  and best practice violations. Use when examining code changes, reviewing
  PRs, analyzing code quality, or when asked to review, audit, or check code.
allowed-tools: Read, Grep, Glob
---

# Code Review Expertise

## Security Checks
When reviewing code, verify:

### Input Validation
- All user input sanitized before database operations
- Parameterized queries (no string interpolation in SQL)
- Output encoding for rendered HTML content

### Authentication
- Session tokens validated on every protected endpoint
- Permission checks before data mutations
- No hardcoded credentials or API keys in source

### Data Exposure
- PII masked in log output and error messages
- API responses don't leak internal IDs or stack traces
- Sensitive fields excluded from serialization defaults

Note allowed-tools: Read, Grep, Glob — this restricts the skill to read-only operations. The code reviewer can examine files but can’t modify them. Tool restrictions prevent skills from having unintended side effects.

Other useful frontmatter fields beyond name, description, and allowed-tools 1:

Field What it does
disable-model-invocation: true Prevents auto-activation; skill only activates via /skill-name
user-invocable: false Hides from the / menu entirely
model Override which model to use when the skill is active
context: fork Run in a forked subagent context (isolated context window)
argument-hint Hint shown during autocomplete (e.g., [filename] [format])
agent Run as a subagent with its own isolated context window
hooks Define lifecycle hooks (PreToolCall, PostToolCall) for the skill
$ARGUMENTS String substitution: replaced with user’s input after /skill-name
$USER_PROMPT String substitution: replaced with the user’s latest message
$SLASH_PROMPT String substitution: replaced with the full /skill-name <args> invocation

One caveat from the official documentation: context: fork “only makes sense for skills with explicit instructions that benefit from isolation” 1. Use it for analysis skills (code review, security audit) where you want a clean context, not for knowledge skills that should blend into the main conversation.

Step 3: Add supporting resources

Skills can reference additional files in the same directory 1:

~/.claude/skills/code-reviewer/
├── SKILL.md                    # Required: frontmatter + core expertise
├── SECURITY_PATTERNS.md        # Referenced: detailed vulnerability patterns
└── PERFORMANCE_CHECKLIST.md    # Referenced: optimization guidelines

Reference them from SKILL.md with relative links:

See [SECURITY_PATTERNS.md](SECURITY_PATTERNS.md) for OWASP Top 10 checks.
See [PERFORMANCE_CHECKLIST.md](PERFORMANCE_CHECKLIST.md) for query optimization.

Claude reads these files on-demand when the skill activates, using standard file-reading tools 1. Keep SKILL.md under 500 lines and move detailed reference material to supporting files 3 — shorter skill files reduce context injection overhead and keep Claude focused on the current task.

Step 4: Test activation

The skill activates the next time you start a Claude Code session. Test it:

# Ask Claude to review code — should trigger the skill automatically
claude "Review the authentication middleware in app/security/"

Verify the skill loaded using one of two methods 1:

# In an interactive session, ask Claude directly:
> What skills are available?

# Or check the context budget for excluded skills:
> /context

If the skill doesn’t activate, the problem is almost always the description field. See Step 5.

Step 5: The critical step — writing the description

The description field is the single most important line in your skill. Here’s what happens under the hood: at session start, Claude Code extracts every skill’s name and description and injects them into Claude’s context. When you send a message, Claude uses language model reasoning — not regex, not keyword matching, not embedding similarity — to decide if any skill is relevant. The official documentation states: “Claude matches your task against skill descriptions to decide which are relevant. If descriptions are vague or overlap, Claude may load the wrong skill — or miss one that would help” 1.

Independent analysis of the Claude Code source confirms the mechanism: skill descriptions are injected into an available_skills section of the system prompt, and the model uses standard language understanding to select relevant skills at invocation time 4. The LLM-based matching has important implications for how you write descriptions.

Bad description:

description: Helps with code

Claude has no idea when to activate this. “Helps with code” matches everything and nothing — and because matching is LLM reasoning, vague descriptions create unpredictable activation.

Better description:

description: Review code for bugs and issues

Too vague. What kind of bugs? What kind of issues? When should Claude use this instead of its built-in analysis?

Effective description:

description: Review code for security vulnerabilities, performance issues,
  and best practice violations. Use when examining code changes, reviewing
  PRs, analyzing code quality, or when asked to review, audit, or check code.

The effective description works because it includes: - What it does: Review code for specific issue types - When to use it: Examining changes, PRs, quality analysis - Trigger phrases: review, audit, check — words the user naturally types

One constraint to know: all skill descriptions share a context budget that “scales dynamically at 2% of the context window, with a fallback of 16,000 characters” 1. If you have many skills, keep each description concise — a verbose description competes with other skills for limited space. You can override the budget via the SLASH_COMMAND_TOOL_CHAR_BUDGET environment variable 2, but the better fix is shorter, more precise descriptions.

Test different descriptions. Start a fresh session, ask Claude to review code, and check if the skill activates. If not, add more trigger phrases. If it activates when it shouldn’t, make the description more specific.

Step 6: Iterate based on usage

After a week of use, you’ll discover: - Patterns the skill should check but doesn’t — add them to SKILL.md - False activations on unrelated tasks — tighten the description, or add disable-model-invocation: true and require explicit /code-reviewer invocation - Missing context — add supporting resource files - Tool restrictions that are too tight or too loose — adjust allowed-tools

Skills are living documents. The first version is never the final version.

Advanced: Skills as a Prompt Library

Beyond single-purpose skills, the directory structure works as an organized prompt library:

~/.claude/skills/
├── code-reviewer/          # Activates on: review, audit, check
├── api-designer/           # Activates on: design API, endpoint, schema
├── sql-analyst/            # Activates on: query, database, migration
├── deploy-checker/         # Activates on: deploy, release, production
└── incident-responder/     # Activates on: error, failure, outage, debug

Each skill encodes a different facet of your team’s expertise. Together, they form a knowledge base that Claude draws from automatically based on context. A junior developer gets senior-level guidance without asking for it.

A note on skill count: More skills means more descriptions competing for the context budget 1. If you notice skills not activating, run /context to check whether any are being excluded. Prioritize fewer, well-described skills over many vague ones.

Sharing Skills with Your Team

Personal skills (~/.claude/skills/) are yours alone. Use for personal preferences, experimental patterns, or expertise specific to your workflow.

Project skills (.claude/skills/ in the repo root) are shared via git 1:

# Create project-level skill
mkdir -p .claude/skills/domain-expert
# ... write SKILL.md ...

# Commit and push
git add .claude/skills/
git commit -m "feat: add domain-expert skill for payment processing rules"
git push

When teammates pull, they get the skill automatically. No installation, no configuration. Git distribution is the most effective way to standardize expertise across a team.

Guidelines for shared skills: - Keep project skills focused on domain expertise (business rules, architecture patterns) - Keep personal skills for workflow preferences (formatting, commit style) - Document why the skill exists in a comment at the top of SKILL.md - Review skill changes in PRs like any other code

Key Takeaways

  • Build a skill when you catch yourself re-explaining context. If you paste the same checklist three times, it should be a skill.
  • The description field determines everything. Claude uses LLM reasoning to match your requests against descriptions 1. Invest more time in the description than in the skill content.
  • Use allowed-tools to constrain side effects. Read-only skills should be restricted to Read, Grep, Glob.
  • Share project skills via git. Team knowledge distribution with zero configuration 1.
  • Don’t over-abstract. A skill for every micro-pattern creates maintenance burden and competes for context budget. Build skills for expertise that’s stable, reusable, and valuable enough to maintain.

References


  1. Extend Claude with Skills — Claude Code Documentation — Skill structure, all 10 frontmatter fields, LLM-based matching, 2% context budget, directory scoping, and troubleshooting 

  2. Claude Code Source — SLASH_COMMAND_TOOL_CHAR_BUDGET — Environment variable override for skill description budget 

  3. Skill Authoring Best Practices — Claude API Documentation — 500-line limit, supporting files, and naming conventions 

  4. Inside Claude Code Skills: Structure, Prompts, Invocation — Mikhail Shilkov — Independent analysis of the discovery mechanism, context injection, and available_skills section - Claude Code Guide — Skills Section — Full reference for skill structure, frontmatter, and tool restrictions - Claude Code Hooks — Hooks complement skills: hooks enforce policy, skills provide expertise - Context Engineering Is Architecture — Skills as a layer in the seven-tier context hierarchy - AGENTS.md Patterns — Cross-tool project instructions (the Codex equivalent) 

Related Posts

Claude Code Hooks: Why Each of My 95 Hooks Exists

I built 95 hooks for Claude Code. Each one exists because something went wrong. Here are the origin stories and the arch…

7 min read

Context Window Management: What 50 Sessions Taught Me About AI Development

I measured token consumption across 50 Claude Code sessions. Context exhaustion degrades output before you notice. Here …

6 min read

Claude Code Hooks Tutorial: 5 Production Hooks From Scratch

Build 5 production Claude Code hooks from scratch with full JSON configs: auto-formatting, security gates, test runners,…

12 min read