Agent Skills: How to Teach AI Agents Your Workflows with SKILL.md
Agent Skills package your expertise into reusable folders that AI agents load on demand. Learn what skills are, where they live, how invocation and progressive disclosure work, and build a complete SEO audit skill step by step.
Every new session, your AI agent forgets everything: your conventions, your checklists, your hard-won tribal knowledge. You can paste the same instructions again and again — or you can package them once as an Agent Skill and let the agent load them exactly when they are needed. Skills are the difference between prompting an assistant and training one.
This guide covers what skills are, what they are good for, where they are stored, how invocation really works under the hood, how to organize them so they actually get used — and a complete, step-by-step example: an SEO audit skill you can copy today.
What Are Agent Skills?
An Agent Skill is a folder containing a SKILL.md file — YAML frontmatter plus markdown instructions — and, optionally, supporting files like scripts and reference documents. That's the entire format:
my-skill/
├── SKILL.md # required: metadata + instructions
├── references/ # optional: deep-dive documentation
└── scripts/ # optional: executable code
A minimal SKILL.md looks like this:
---
name: release-notes
description: Generates release notes from git history. Use when asked to write release notes, a changelog, or summarize changes between versions.
---
# Release Notes
1. Run `git log --oneline <last-tag>..HEAD` to collect changes.
2. Group commits into Features, Fixes, and Internal.
3. Write user-facing descriptions — never paste raw commit messages.
Two things make this simple format powerful:
- It's an open standard. Skills follow the [Agent Skills](https://agentskills.io) specification, developed by Anthropic and adopted by dozens of platforms — Claude Code, the Claude API, Cursor, GitHub Copilot, JetBrains, and more. A skill you write once is portable across agents.
- It's progressive. The agent doesn't read the whole skill up front. It sees only the name and description until the skill is actually relevant — more on that below, because it's the key design idea.
Skills vs. CLAUDE.md vs. Subagents vs. MCP
Skills sit in an ecosystem of agent-customization mechanisms, and they are often confused with their neighbors:
| Mechanism | What it is | When it loads | Best for |
|---|---|---|---|
| CLAUDE.md | Project memory file | Always, every session | Short, universal conventions |
| Skill | Folder of instructions + files | On demand, when relevant | Workflows and domain expertise |
| Subagent | Separate agent with own context | When delegated a task | Independent, parallel work |
| MCP server | External process exposing tools | Tools available per session | Live data and system access |
The rule of thumb: CLAUDE.md is for things that apply to every conversation, skills are for expertise that applies to some conversations, subagents are for work you want out of the current conversation, and MCP is for capabilities the model cannot get from the filesystem at all.
One recent simplification worth knowing: in Claude Code, custom slash commands have been merged into skills. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy — but the skill format is the recommended one, because it supports bundled files, frontmatter options, and automatic invocation.
What Can You Use Skills For?
Four categories cover most real-world skills:
- Repeatable workflows. Release notes, PR descriptions, content briefs, weekly reports — anything you have explained to your agent more than twice.
- Domain expertise. Your brand voice guide, your on-page SEO checklist, your legal review criteria. Knowledge that lives in a senior colleague's head, written down once.
- Fragile operations. Database migrations, deployment sequences, data transformations. Here the skill ships an exact *script*, because "roughly right" is not good enough.
- Team conventions. Commit formats, definition of done, review standards. Committed to the repo, so every teammate's agent behaves consistently.
There is also a fifth, invisible category: Anthropic ships pre-built skills — pdf, xlsx, docx, pptx — that power document creation on claude.ai and the Claude API. When Claude produces a formatted spreadsheet for you, a skill is doing the work.
Where Skills Are Stored
Skills are just directories, and where you put the directory decides who gets it:
| Level | Path | Scope |
|---|---|---|
| Personal | ~/.claude/skills/<name>/ | You, in every project |
| Project | .claude/skills/<name>/ | Everyone who clones the repo |
| Nested (monorepo) | apps/web/.claude/skills/<name>/ | Only when working in that subdirectory |
| Plugin | <plugin>/skills/<name>/ | Wherever the plugin is installed, namespaced as plugin:skill |
| Enterprise | Managed settings | Deployed org-wide by admins |
The most underused option is the project level. A skill committed to .claude/skills/ is versioned, code-reviewed, and distributed like any other code — your team's collective agent knowledge, in git. On other surfaces the mechanics differ slightly (the Claude API accepts skill uploads via a /v1/skills endpoint, claude.ai via a zip upload in settings), but the format is identical everywhere.
How Skills Are Invoked
There are two invocation paths, and understanding both is what separates skills that get used from skills that gather dust.
Model-invoked: the description decides
At session start, the agent loads a listing of every installed skill — but only the name and description of each. When your request matches a description, the agent loads that skill's full instructions and follows them.
Read that again, because it should sound familiar: a skill description is a search snippet, and the model is the search engine. Whether your skill gets "ranked" for a task depends entirely on whether its description matches the query. Writing skill descriptions is answer engine optimization with an audience of one — and the same rules apply: name what the skill does, name the triggers, be specific. This is the single highest-leverage line in the whole file.
User-invoked: slash commands and arguments
Every skill is also a slash command. Type /seo-audit https://example.com and the skill loads explicitly, with the URL available inside the skill body as $ARGUMENTS. Frontmatter supports an argument-hint field so autocomplete shows users what to pass. You can even control the two paths independently: disable-model-invocation: true makes a skill manual-only (useful for destructive operations like deployments), while user-invocable: false hides it from the slash menu but lets the model use it.
Progressive disclosure: why skills scale
Here is the design idea that makes the whole system work. A skill's content loads in four stages, each costing context only when actually needed:
Level 1: name + description → always in context (~dozens of tokens)
Level 2: SKILL.md body → loaded when invoked (up to a few thousand)
Level 3: references/*.md → read only if needed
Level 4: scripts/* → executed, never read
Level 4 deserves emphasis: a bundled script runs via the shell, so only its output enters the context window — a 500-line script costs the same as its 20-line result. This is why a skill can bundle megabytes of expertise while the agent pays a few dozen tokens until the moment it matters. You could never do this with CLAUDE.md, which bills its full length against every single conversation.
Dynamic context injection
One more trick: skill bodies can embed ` !command expressions. Before the model reads the skill, the command runs and its output replaces the expression. A code-review skill can start with !git diff HEAD ` and the model receives the actual current diff — fresh data, zero extra round-trips.
How to Organize Skills Effectively
The official best practices boil down to six rules:
- Write the description like a snippet. Third person, what + when, explicit trigger phrases: "Runs a technical on-page SEO audit for a given URL. Use when asked to audit a page, check meta tags, or diagnose search performance." Vague descriptions ("Helps with SEO stuff") simply never trigger.
- Keep SKILL.md under ~500 lines. It's a briefing, not a book. Move depth into `references/` files — kept *one level* deep, since agents may only partially read nested chains. Give long reference files a table of contents.
- Assume the model is smart. Don't explain what a canonical tag is. Challenge every paragraph: "Does the model really need this sentence?" Over-explaining wastes context and dilutes the instructions that matter.
- Match freedom to fragility. Multiple valid approaches? Write prose guidance. One correct sequence? Provide a template. Zero tolerance for variation? Ship a script and instruct the agent to run it, not reimplement it.
- Name skills like you mean it. `analyzing-spreadsheets` beats `helper`. Avoid `utils`, `tools`, and other names that describe nothing.
- Test like code. Write three test scenarios *before* writing the skill, then verify a fresh session passes them. Anthropic's official `skill-creator` plugin automates this loop — isolated test runs, grading, and A/B comparison of skill versions. Skills are prompts with version control; treat them with the same rigor as any other artifact in your repo.
Step by Step: Building an SEO Audit Skill
Theory done — let's build something real. The goal: type /seo-audit <url> and get a structured on-page audit against a fixed checklist, with the raw data gathered by a script instead of guessed by the model. Three files:
.claude/skills/seo-audit/
├── SKILL.md
├── references/
│ └── on-page-checklist.md
└── scripts/
└── fetch-page.mjs
Step 1: The SKILL.md
---
name: seo-audit
description: Runs a technical on-page SEO audit for a given URL. Use when asked to audit a page, check on-page SEO or meta tags, review headings or structured data, or diagnose why a page underperforms in search engines or AI answers.
argument-hint: [url]
allowed-tools: Bash(node ${CLAUDE_SKILL_DIR}/scripts/fetch-page.mjs:*)
---
# On-Page SEO Audit
Audit the page at $ARGUMENTS.
## Workflow
1. Run `node ${CLAUDE_SKILL_DIR}/scripts/fetch-page.mjs <url>` and
parse the JSON output.
2. Evaluate every signal against `references/on-page-checklist.md`.
3. If the script reports an error, report the fetch problem and stop —
do not guess missing values.
## Report format
1. A pass/fail table covering each checklist item
2. The three highest-impact fixes, each with a one-line rationale
3. A verdict: is this page ready for search engines AND AI answer engines?
Keep the report under 400 words. Never invent signals the script
did not return.
Notice the details: the description contains the trigger phrases users actually say. $ARGUMENTS receives the URL from the slash command. ${CLAUDE_SKILL_DIR} resolves to the skill's own folder, so the path works whether the skill is installed personally, in a project, or via a plugin — and the allowed-tools line pre-approves running the bundled script, so the user isn't interrupted by a permission prompt (this field is Claude Code-specific). The "do not guess" instruction is load-bearing: it converts fetch failures into honest errors instead of hallucinated audits.
Step 2: The checklist reference
references/on-page-checklist.md holds the domain expertise — as deep as you like, since it only loads when the skill runs:
# On-Page SEO Checklist
## Critical
- Title: 30–60 characters, primary topic near the front
- Meta description: 120–160 characters, clear value proposition
- Exactly one H1, matching the search intent
- Canonical URL present and self-referencing
- Robots meta must not block indexing unintentionally
## Important
- Structured data (JSON-LD) matching the page type
- Open Graph tags for social sharing
- Descriptive alt text on all images
- hreflang pairs on multilingual sites
- At least 300 words of body content
Step 3: The script
scripts/fetch-page.mjs gathers the facts. Deterministic code does the measuring; the model does the judging:
#!/usr/bin/env node
// Usage: node fetch-page.mjs <url> — prints SEO signals as JSON
const url = process.argv[2];
if (!url) {
console.log(JSON.stringify({ error: 'No URL provided' }));
process.exit(0);
}
try {
const res = await fetch(url, {
redirect: 'follow',
headers: { 'User-Agent': 'seo-audit-skill/1.0' },
});
const html = await res.text();
const pick = (re) => (html.match(re) || [])[1]?.trim() ?? null;
const count = (re) => (html.match(re) || []).length;
const text = html
.replace(/<script[\s\S]*?<\/script>/gi, ' ')
.replace(/<style[\s\S]*?<\/style>/gi, ' ')
.replace(/<[^>]+>/g, ' ');
console.log(JSON.stringify({
url: res.url,
status: res.status,
title: pick(/<title[^>]*>([\s\S]*?)<\/title>/i),
titleLength: pick(/<title[^>]*>([\s\S]*?)<\/title>/i)?.length ?? 0,
metaDescription: pick(/<meta[^>]+name=["']description["'][^>]+content=["']([^"']*)["']/i),
canonical: pick(/<link[^>]+rel=["']canonical["'][^>]+href=["']([^"']*)["']/i),
robotsMeta: pick(/<meta[^>]+name=["']robots["'][^>]+content=["']([^"']*)["']/i),
h1Count: count(/<h1[\s>]/gi),
imagesWithoutAlt: count(/<img(?![^>]*\balt=)[^>]*>/gi),
hasJsonLd: /application\/ld\+json/i.test(html),
hasOpenGraph: /property=["']og:/i.test(html),
hasHreflang: /hreflang=/i.test(html),
wordCount: text.split(/\s+/).filter(Boolean).length,
}, null, 2));
} catch (err) {
console.log(JSON.stringify({ error: `Could not fetch ${url}: ${err.message}` }));
}
(A production tool would use a real HTML parser instead of regex — for a bundled, zero-dependency demo script, this is the right trade-off.) Note that the script handles its own errors and always emits valid JSON. A best practice straight from the official docs: solve problems in the script, don't fail cryptically and make the model figure it out.
Step 4: Run it
> /seo-audit https://example.com/blog/my-post
The agent runs the script, reads the checklist, and produces something like:
| Check | Result |
|---|---|
| Title (30–60 chars) | ✅ 54 characters |
| Meta description | ⚠️ Missing |
| Single H1 | ✅ |
| Canonical | ✅ Self-referencing |
| JSON-LD | ❌ None found |
| Image alt text | ⚠️ 3 images missing alt |
Top fixes: add a 120–160 character meta description; add Article structured data; complete the missing alt attributes.
And because the skill's description names the right triggers, you don't even need the slash command — ask "why might this page underperform in AI answers?" mid-conversation, and the agent pulls the skill in on its own.
Step 5: Ship it to your team
Commit .claude/skills/seo-audit/ to your repo. Done — every teammate now has /seo-audit, and improving the checklist is a pull request like any other. That's the quiet superpower of skills: expertise stops being personal and starts being infrastructure.
What Most Guides Miss
Four details that rarely make it into introductions but matter in practice:
- The token economics are explicit. Skill descriptions share a listing budget of roughly 1% of the context window, and when a long session compacts, invoked skills are re-attached within a fixed token budget — most recently used first. Install fifty verbose skills and the oldest ones silently fall out. Curation beats collection. For the full picture of how agent context is billed — and how to cut the bill — see our deep dive on token economics.
- Skills are executable content — treat them like dependencies. Dynamic injection means a skill can run shell commands the moment it loads. That's the feature; it's also the risk. Review third-party skills before installing, exactly as you would an npm package, and note that organizations can disable skill shell execution via managed settings.
- Portability is real. Because the format is an open standard, the SEO audit skill above works in other agents adopting Agent Skills — write once, run in Claude Code, Cursor, or Copilot.
- Skills compose with everything else. A skill can reference MCP tools, spawn subagents for isolation, and restrict its available tools via frontmatter. They are not an alternative to the rest of the agent stack — they are its instruction layer.
Conclusion
Skills turn prompting into infrastructure. Instead of re-explaining your standards every session, you write them down once — in a format that is versioned, reviewable, portable, and loaded only when it earns its context cost.
Your getting-started checklist:
- [ ] Pick one workflow you have explained to your agent at least three times
- [ ] Create
.claude/skills/<name>/SKILL.mdwith a specific, trigger-rich description - [ ] Move deep details into
references/, deterministic logic intoscripts/ - [ ] Test with a fresh session: does it trigger when it should — and stay quiet when it shouldn't?
- [ ] Commit it to your repo and let your team inherit it
Start with one skill. The first time your agent handles a task perfectly because past you wrote the instructions, you'll build the second one the same day.
Related Articles
- Tool Use & Function Calling: How LLMs Interact with External Systems - How models invoke external functions
- MCP for Websites: Making Your Site Agent-Ready - The protocol side of agent capabilities
- Agentic Workflow Patterns - Designing reliable multi-step agent processes
- Prompt Engineering for Content Teams - Writing instructions models actually follow
Frequently Asked Questions
A SKILL.md file is a markdown file with YAML frontmatter that defines an Agent Skill: a name, a description that tells the agent when to use it, and instructions the agent follows once the skill is invoked. It lives in a folder that can also bundle scripts and reference documents.
A skill is filesystem-based instructions and code the agent loads into its context on demand. An MCP server is a running external process that gives the agent new tools and data connections. Skills teach behavior; MCP grants capabilities — and a skill can instruct the agent to use MCP tools.
No. Agent Skills are an open standard (agentskills.io) developed by Anthropic and adopted by dozens of platforms, including Cursor, GitHub Copilot, and JetBrains IDEs. The same skill folder is portable across compatible agents.
Only each skill's name and description are preloaded — the body loads on demand — so dozens of skills cost little. Descriptions do share a limited listing budget, so prefer a curated set of well-described skills over an exhaustive collection.
Yes. Skills can bundle scripts that the agent executes via the shell; only the script's output enters the context window. This makes scripts the most token-efficient way to package deterministic logic like data fetching or validation.