---
title: "Your First Agent Skill"
description: "Learn how SKILL.md files work in Claude Code plugins, then build a production-quality agent skill from scratch. Covers frontmatter schema, body structure, testing, and iteration."
section: "getting-started"
order: 3
keywords: ["Claude Code skills", "SKILL.md", "agent skills", "write a skill", "Claude Code skill tutorial", "skill frontmatter", "auto-activating skills"]
officialLinks:
  - title: "Claude Code Skills Documentation"
    url: "https://docs.anthropic.com/en/docs/claude-code/skills"
  - title: "Claude Code Plugin Structure"
    url: "https://docs.anthropic.com/en/docs/claude-code/plugins"
relatedDocs:
  - "getting-started/first-plugin"
  - "concepts/skills"
  - "guides/write-a-skill"
---

## Overview

Skills are the core building blocks of Claude Code plugins. A skill is a single markdown file named `SKILL.md` that tells Claude *how* to perform a specific task -- code review, database migration, security scanning, or anything else you can express in structured instructions. When Claude detects that your request matches a skill's trigger conditions, it loads the skill automatically and follows its methodology.

This guide explains the anatomy of a SKILL.md file, walks you through building one from scratch, and shows how to test and refine it until it performs reliably.

## What is a SKILL.md file

A SKILL.md file lives inside a plugin at `skills/<skill-name>/SKILL.md`. It contains two parts:

1. **YAML frontmatter** -- metadata that tells Claude Code when and how to load the skill.
2. **Markdown body** -- the actual instructions Claude follows when the skill activates.

Here is a minimal example:

```markdown
---
name: quick-lint
description: |
  Run a fast lint check on the current file. Use when the user asks
  to lint, check style, or fix formatting issues.
allowed-tools: Read, Bash(npx:eslint)
version: 1.0.0
author: Your Name <you@example.com>
license: MIT
---

## Overview

You are a code linting specialist. When activated, run ESLint on the
target file and report results in a structured format.

## Steps

1. Identify the file to lint from the user's request.
2. Run `npx eslint <file> --format stylish` using the Bash tool.
3. Parse the output and summarize findings.
4. If there are auto-fixable issues, offer to run `npx eslint <file> --fix`.
```

When a user says "lint this file" or "check the formatting," Claude matches those phrases against the skill's `description` field and loads the instructions.

## Frontmatter schema

Every SKILL.md starts with a YAML frontmatter block delimited by `---`. The following fields are supported.

### Required fields

| Field | Type | Description |
|---|---|---|
| `name` | string | Unique identifier for the skill (kebab-case) |
| `description` | string | When to use this skill. Include trigger phrases that Claude matches against. |
| `allowed-tools` | string | Comma-separated list of tools the skill can use |
| `version` | string | Semantic version (e.g., `1.0.0`) |
| `author` | string | Author name and optional email |
| `license` | string | SPDX license identifier (e.g., `MIT`, `Apache-2.0`) |

### Optional fields

| Field | Type | Description |
|---|---|---|
| `model` | string | Override the LLM model (`sonnet`, `haiku`, `opus`) |
| `context` | string | Set to `fork` to run in a subagent context |
| `agent` | string | Subagent type (e.g., `Explore`) |
| `user-invocable` | boolean | Set to `false` to hide from the `/` menu |
| `argument-hint` | string | Autocomplete hint shown in the `/` menu |
| `hooks` | object | Lifecycle hooks (e.g., `pre-tool-call`) |
| `compatibility` | string | Environment requirements (e.g., `Node.js >= 18`) |
| `compatible-with` | string | Platform compatibility (`claude-code`, `cursor`) |
| `tags` | array | Discovery tags for search and categorization |

### The `description` field matters most

The `description` is the single most important field. Claude uses it to decide whether to activate the skill. Write it as a natural-language explanation of *when* the skill should fire, including specific trigger phrases:

```yaml
description: |
  Perform a security audit on JavaScript or TypeScript code. Use when the
  user asks to check for vulnerabilities, audit security, scan for XSS,
  or review authentication logic.
```

Poor descriptions that are too vague ("helps with code") or too narrow ("only for React useState hooks") cause skills to either fire too often or never fire at all.

### The `allowed-tools` field

This field is a whitelist of the tools Claude is permitted to use when the skill is active. Valid tools:

```
Read, Write, Edit, Bash, Glob, Grep, WebFetch, WebSearch,
Task, TodoWrite, NotebookEdit, AskUserQuestion, Skill
```

You can scope `Bash` to specific commands for tighter security:

```yaml
allowed-tools: Read, Edit, Bash(npm:*, npx:*, git:status)
```

This allows the skill to run any `npm` or `npx` command and `git status`, but nothing else.

## Markdown body structure

The body after the frontmatter closing `---` is the instruction set Claude follows. Use standard markdown with clear section headings.

### Recommended sections

A well-structured skill body includes these sections:

```markdown
## Overview
What this skill does and its core methodology.

## Steps
Numbered step-by-step procedure Claude follows.

## Output
The format and structure of the skill's output.

## Constraints
Boundaries, edge cases, and things to avoid.

## Examples
Concrete input/output examples that calibrate Claude's behavior.
```

You do not need every section in every skill. A simple linter skill might only need Overview and Steps. A complex security auditor benefits from all five.

### Writing effective instructions

Skills are prompt engineering. The same principles that make a good system prompt make a good skill body:

- **Be specific.** "Review the code" is weak. "Check for SQL injection, XSS, CSRF, insecure deserialization, and broken authentication" is strong.
- **Use numbered steps.** Claude follows numbered procedures more reliably than prose paragraphs.
- **Show the output format.** If you want structured output, include a template or example.
- **Set boundaries.** Tell Claude what *not* to do. "Do not modify files unless explicitly asked" prevents unwanted side effects.

### Referencing supporting files

Skills can reference other markdown files in the same skill directory using relative links:

```markdown
For the complete API reference, see [API docs](reference.md).
For usage examples, see [examples](examples/patterns.md).
```

Claude follows these links using the `Read` tool when it needs the referenced content. This keeps your main SKILL.md focused while allowing deep-dive documentation in supporting files.

For bash commands or dynamic context injection, use the `${CLAUDE_SKILL_DIR}` variable to reference the skill's directory:

```markdown
!`cat ${CLAUDE_SKILL_DIR}/templates/report.md`
```

## Build a skill from scratch

Let us build a practical skill: a code review assistant that evaluates pull request diffs against a quality checklist.

### Step 1: Create the directory structure

Inside your plugin directory, create the skill folder:

```bash
mkdir -p skills/pr-review
```

### Step 2: Write the SKILL.md

Create `skills/pr-review/SKILL.md`:

```markdown
---
name: pr-review
description: |
  Review code changes for a pull request. Use when the user asks to
  review a PR, check a diff, evaluate code changes, or assess code
  quality before merging.
allowed-tools: Read, Bash(git:*), Grep, Glob
version: 1.0.0
author: Your Name <you@example.com>
license: MIT
tags: [code-review, pull-request, quality]
---

## Overview

You are an expert code reviewer. When activated, analyze the current
git diff or a specified PR and evaluate changes against a structured
quality checklist. Provide actionable, specific feedback -- not
generic advice.

## Steps

1. Determine the scope of the review:
   - If the user specifies a branch or PR number, use
     `git diff main...<branch>` to get the diff.
   - If no branch is specified, use `git diff --staged` for staged
     changes or `git diff` for unstaged changes.

2. Read the diff output and identify all changed files.

3. For each changed file, evaluate against this checklist:
   - **Correctness**: Does the logic do what it claims?
   - **Error handling**: Are failure cases covered?
   - **Security**: Any hardcoded secrets, SQL injection, or XSS risks?
   - **Performance**: Any O(n^2) loops, missing indexes, or redundant I/O?
   - **Readability**: Are names clear? Is complexity justified?
   - **Tests**: Are new code paths covered by tests?

4. Search for related test files using Glob and Grep to verify
   test coverage for changed functions.

5. Produce a structured review report.

## Output

Format the review as follows:

### Summary
One paragraph overview of the changes and overall quality assessment.

### Findings

For each issue found:

**[SEVERITY] File:Line -- Title**
Description of the issue and why it matters.
Suggested fix with a code snippet if applicable.

Severity levels:
- **CRITICAL** -- Must fix before merge (security, data loss, crashes)
- **WARNING** -- Should fix (bugs, performance, maintainability)
- **SUGGESTION** -- Nice to have (style, naming, minor improvements)
- **PRAISE** -- Highlight well-written code worth calling out

### Verdict
APPROVE, REQUEST CHANGES, or NEEDS DISCUSSION with a one-line rationale.

## Constraints

- Do not modify any files. This is a read-only review.
- Do not review generated files (lockfiles, build output, vendor).
- If the diff is larger than 500 lines, summarize by file and focus
  detailed review on the highest-risk changes.
- Always include at least one PRAISE item to acknowledge good work.
```

### Step 3: Add the plugin manifest

If you do not already have a plugin, create the manifest at `.claude-plugin/plugin.json`:

```json
{
  "name": "my-code-tools",
  "version": "1.0.0",
  "description": "Personal code quality tools for Claude Code",
  "author": "Your Name <you@example.com>",
  "license": "MIT"
}
```

Also create a `README.md` at the plugin root:

```markdown
# My Code Tools

Personal code quality plugins for Claude Code.

## Skills

- **pr-review** -- Structured pull request code review with quality checklist.
```

Your directory structure should look like this:

```
my-code-tools/
  .claude-plugin/
    plugin.json
  README.md
  skills/
    pr-review/
      SKILL.md
```

## Test the skill

### Validate the structure

Run the validator to catch structural issues before testing in Claude Code:

```bash
# Using ccpi
ccpi validate skills/pr-review/SKILL.md

# Using the universal validator (if in the Tons of Skills repo)
python3 scripts/validate-skills-schema.py --enterprise skills/pr-review/
```

The validator checks frontmatter fields, required sections, allowed-tools syntax, and overall content quality.

### Test in Claude Code

Install your plugin locally and try the skill:

```bash
# Start Claude Code in a project with uncommitted changes
claude

# Ask for a review
Review the changes I have staged for commit.
```

If the skill activates, you will see Claude follow the methodology defined in your SKILL.md -- reading the diff, evaluating each file against the checklist, and producing a structured report.

If the skill does not activate, check:

- Is the plugin installed? Run `/plugin list` inside Claude Code.
- Does the `description` field match your request phrasing? Try using the exact trigger phrases from the description.
- Are there errors in the frontmatter? Run `ccpi validate` to check.

## Iterate and improve

Skills rarely work perfectly on the first try. Here is how to refine them.

### Tune the description

If the skill fires too often, narrow the trigger phrases. If it never fires, broaden them. The description should cover the most common ways a user would phrase the request.

### Add examples

Including input/output examples in the body dramatically improves consistency:

```markdown
## Examples

### Example: Small TypeScript PR

Input: `git diff main...feature/auth-refactor`

Expected output structure:
- Summary: "Refactors authentication middleware to use JWT validation..."
- 2-3 findings with severity, file, line, and suggested fix
- Verdict: APPROVE or REQUEST CHANGES
```

### Use dynamic context injection

For skills that need runtime information, use DCI to inject data at activation time:

```markdown
## Context

Current branch and recent commits:

!`git branch --show-current`

!`git log --oneline -5`
```

The `!` backtick syntax runs the command when the skill loads and injects the output into the prompt. This saves tool-call round trips and gives Claude immediate context.

### Reference supporting files

For complex skills, break instructions into multiple files:

```
skills/pr-review/
  SKILL.md            # Main instructions
  checklist.md        # Detailed quality checklist
  examples/
    typescript.md     # TypeScript-specific review examples
    python.md         # Python-specific review examples
```

Reference them from SKILL.md with relative links:

```markdown
For the full quality checklist, see [checklist](checklist.md).
For language-specific guidance, see [TypeScript examples](examples/typescript.md).
```

## Common patterns

### Subagent delegation

For expensive or long-running skills, run them in a forked context so they do not consume the main conversation's token budget:

```yaml
context: fork
agent: Explore
```

### Model override

If a skill needs stronger reasoning (e.g., complex security analysis), override the model:

```yaml
model: opus
```

For simple, high-volume tasks (e.g., formatting checks), use a faster model:

```yaml
model: haiku
```

### Scoped Bash access

Limit Bash access to only the commands the skill needs:

```yaml
allowed-tools: Read, Bash(npm:test, npm:run, git:diff, git:log)
```

This follows the principle of least privilege and prevents the skill from accidentally running destructive commands.

## Next steps

- [ccpi CLI Quick Reference](/docs/getting-started/cli-reference) -- validate and manage skills from the command line.
- [Browse existing skills](/skills) -- study the 2,834 skills in the marketplace for patterns and inspiration.
- [Explore plugins](/explore) -- see how production plugins structure their skills.
