Back to Blog
    TechnicalAEOStrategy

    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.

    Julia Maehler··6 min read

    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:

    1. 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.
    2. 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:

    MechanismWhat it isWhen it loadsBest for
    CLAUDE.mdProject memory fileAlways, every sessionShort, universal conventions
    SkillFolder of instructions + filesOn demand, when relevantWorkflows and domain expertise
    SubagentSeparate agent with own contextWhen delegated a taskIndependent, parallel work
    MCP serverExternal process exposing toolsTools available per sessionLive 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:

    1. Repeatable workflows. Release notes, PR descriptions, content briefs, weekly reports — anything you have explained to your agent more than twice.
    2. 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.
    3. Fragile operations. Database migrations, deployment sequences, data transformations. Here the skill ships an exact *script*, because "roughly right" is not good enough.
    4. 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 skillspdf, 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:

    LevelPathScope
    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
    EnterpriseManaged settingsDeployed 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:

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. Name skills like you mean it. `analyzing-spreadsheets` beats `helper`. Avoid `utils`, `tools`, and other names that describe nothing.
    6. 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:

    CheckResult
    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.md with a specific, trigger-rich description
    • [ ] Move deep details into references/, deterministic logic into scripts/
    • [ ] 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.

    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.