Build once, deploy everywhere. Skills are the universal way to teach AI agents how to do your team’s work.
What problem do skills solve?#
AI agents are increasingly capable, but they lack the context they need to do your work reliably. They do not know your team’s conventions, your internal tools, your deployment process, or your testing strategy. Every time someone types a long prompt explaining how to “run our specific linting pipeline” or “generate a migration for our ORM setup”, that knowledge evaporates when the session ends.
Skills solve this by packaging procedural knowledge - the kind of step-by-step expertise that lives in a senior engineer’s head - into portable, version-controlled files that agents load on demand. Think of a skill as an onboarding guide for a new hire, except the new hire is an AI agent that can read and follow instructions instantly.
Here is the key insight: a skill is not a configuration file. It is not a prompt template. It is a procedure - a set of instructions that tells an agent how to accomplish a specific task the way your team does it.
The open standard: SKILL.md#
Agent Skills follow an open specification
built around one simple idea: a directory containing a SKILL.md file. That single format works across 20+ agent platforms, including Claude Code, GitHub Copilot in VS Code, Cursor, Gemini CLI, OpenAI Codex, Windsurf, Roo Code, Cline, Goose, and many others. You build a skill once, and it runs everywhere your engineers work.
The SKILL.md file has two parts:
- YAML frontmatter (metadata)
- Markdown body (instructions)
Here is the simplest possible skill:
my-skill/
SKILL.md
---
name: commit-message-generator
description: >
Use when generating commit messages or reviewing staged changes.
Handles conventional commits format, scope detection, and breaking change notation.
---
# Commit Message Generator
## Workflow
1. Run `git diff --cached`. If empty, ask the user to stage changes.
2. Pick a type: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`.
3. Pick a scope from the touched paths. `src/auth/**` -> `auth`.
4. Write the subject line: `<type>(<scope>): <description>`. Imperative mood, no period, <= 72 chars.
5. If the diff touches `src/api/public/**`, append a `BREAKING CHANGE:` footer that names the broken contract.
6. Reference issue numbers in the footer when applicable: `Refs: #123`.
## Examples
| Diff | Message |
| --------------------------------------------- | ------------------------------------------------ |
| New file `src/auth/login.ts` | `feat(auth): add login endpoint` |
| Removed required field from `src/api/public/` | `feat(api): remove deprecated email field`<br>`BREAKING CHANGE: email is no longer accepted in /users` |
| Fix in `src/db/migrations.ts` | `fix(db): handle null timestamps in migrations` |
That is a complete, functional skill. An agent can discover it, understand when to use it, load the full instructions, and follow the procedure. Notice the format: flat numbered steps, imperative form, no decorative blockquotes, no “## Format” section duplicating “## Rules” - dense for the model, not pretty for a screen.
How skills work: progressive disclosure#
This is the single most important concept to understand about skills, and the one most often misunderstood. Skills do not dump their entire content into the agent’s context window at startup. They use a three-phase loading model called Progressive Disclosure :
Phase 1 - Metadata (always loaded). When a session starts, the agent reads only the name and description from the frontmatter of every available skill. This costs roughly 100 tokens per skill - cheap enough to have dozens of skills installed without bloating context.
Phase 2 - Instructions (loaded on activation). When the agent decides a skill is relevant to the current task (based on the description matching the user’s request), it loads the full body of the SKILL.md file. The specification recommends keeping this under 5,000 tokens (roughly 500 lines).
Phase 3 - Resources (loaded on demand). If the skill references additional files (scripts, templates, reference documentation), those are only loaded when the agent needs them. A skill directory can include scripts/, references/, and assets/ subdirectories, but their contents stay untouched until explicitly accessed.
This architecture matters because context windows are a finite, expensive resource. Every token spent on irrelevant skill instructions is a token unavailable for understanding the user’s code. Progressive disclosure ensures skills scale: you can install fifty skills and the agent only pays the full cost for the ones it activates. Another problem - attention window pollution - is also mitigated. The agent’s attention is focused on the relevant instructions, not distracted by dozens of other skill bodies.
Here is a more complete directory structure for a skill with supporting files:
code-reviewer/
SKILL.md # Required: metadata + instructions
scripts/
lint-check.sh # Loaded only when the skill references it
references/
style-guide.md # Loaded only when needed for deep review
assets/
review-template.md # Template for review comments
The description field: your skill’s discovery trigger#
The description field in the frontmatter is far more important than it appears. For a skill the agent can invoke autonomously, it is not documentation, not a summary for humans - it is a trigger, the mechanism by which the agent decides whether to activate your skill.
That framing has one important exception, covered at the end of this section: skills explicitly marked as user-invoked-only never get auto-loaded, so their descriptions stop being triggers and revert to being human-readable labels. Until you reach that subsection, assume the skill is model-invocable - this is the default on every platform.
This is the number one mistake skill authors make, and understanding why it fails requires understanding the progressive disclosure model from the inside.
When a user asks an agent to “review the PR for the payments service”, the agent scans all available skill descriptions looking for a match. If your description already explains the workflow (“Analyzes git diff, identifies issues, generates review comments in conventional format”), the agent may conclude it already understands what to do and skip loading the full skill body entirely. The model is optimizing for token efficiency - if the description told it the procedure, why would it spend tokens reading the detailed instructions?
This is not a bug. It is rational behavior from the model’s perspective. But it means your carefully crafted workflow inside the skill body never gets read.
A real failure case. Consider a skill with this description:
description: "Code review between tasks - analyzes diff, checks for issues, generates review"
When activated, the agent performed a single cursory review instead of following the skill’s detailed flowchart that included separate passes for logic errors, style violations, security concerns, and test coverage. The description had already told the agent what to do, so it skipped the how.
The fix was changing the description to a trigger:
description: >
Use when reviewing code changes, pull requests, or diffs.
Handles multi-pass review with security, style, and test coverage checks.
Now the description tells the agent when to activate (the trigger conditions) and what capabilities are available (so it knows the skill is worth loading), without revealing the procedure itself. The agent must load the full skill body to learn the workflow.
Principles for writing effective descriptions:
The description is injected into the agent’s system prompt at session startup, so write it in third person. Phrases like “I can help you” or “You should use this” break the system prompt context. Instead, use declarative forms: “Handles…”, “Use when…”, “Generates…”.
Include the specific keywords users type. Not “Python testing” but “pytest”. Not “Word documents” but “.docx files”. Not “database queries” but “BigQuery SQL” or “PostgreSQL migrations”. The model matches on concrete terms, not abstractions.
Include synonyms and related terms to catch variant phrasings. If your skill handles PDF creation, mention “PDF”, “pdf”, “.pdf files”, “portable document format” - because different engineers phrase the same request differently.
Controlling over-triggering with negative triggers. Pushy descriptions solve under-triggering, but sometimes a description is too effective. Your data analysis skill keeps loading when users ask for basic spreadsheet formatting. Your code review skill activates during regular coding sessions.
The fix is adding negative triggers, explicit statements about what the skill should NOT handle:
description: >
Advanced statistical analysis for CSV and Parquet files.
Use for regression modeling, clustering, hypothesis testing,
and time-series forecasting. Do NOT use for basic data
exploration, formatting, or simple aggregation.
Negative triggers act as a filter. The agent reads “Do NOT use for basic data exploration” and excludes the skill from those requests. Without this, a broadly worded description pulls the skill into conversations where it adds overhead without value.
To verify your description works, ask the agent: “When would you use the [skill-name] skill?” Compare the response to your intended trigger conditions and adjust until they align.
Keep descriptions under 1,024 characters (the specification limit). Aim for 2-4 sentences. Every extra word costs tokens across every session, even when the skill is never activated.
When the description stops being a trigger#
The whole “Use when…” discipline only matters when the agent is the one deciding whether to load your skill. If you explicitly remove that decision from the agent, every piece of advice above becomes obsolete for that skill.
Claude Code (and, since December 2025, VS Code GitHub Copilot) supports disable-model-invocation: true in the frontmatter. The Claude Code skills docs
describe the effect plainly: “Description not in context, full skill loads when you invoke”. The skill is invokable only by the user typing /skill-name. The model never sees the description and never auto-loads the body.
For these skills, “Use when reviewing code changes…” is dead weight. The description becomes a label that surfaces in skill catalogs, the slash-command picker, and team documentation - it speaks to humans, not to the model. Write it accordingly:
---
name: release-checklist
description: Walks the user through the production release checklist for the payments service.
disable-model-invocation: true
---
Compare with the model-invocable version, which spends tokens on trigger phrases the model needs to match against:
---
name: release-checklist
description: >
Use when preparing a production release, cutting a release branch,
or running pre-deploy checks for the payments service.
Handles changelog generation, version bumps, and staging verification.
---
Both descriptions are correct - for different skills. Picking the right one means deciding up front who triggers the skill: the model, the user, or both. Anthropic’s docs make the distinction explicit in the skill content guide
: “Task content gives Claude step-by-step instructions… These are often actions you want to invoke directly with /skill-name rather than letting Claude decide when to run them. Add disable-model-invocation: true to prevent Claude from triggering it automatically.”
Two cautions before you reach for the field reflexively:
- It is not a portable spec field.
disable-model-invocationlives in Claude Code’s frontmatter table and VS Code Copilot’s extension; it is not in the agentskills.io spec . On a platform that ignores the field, your skill becomes model-invocable again, and a description like “Walks the user through…” will trigger badly. - It interacts with other vendor-specific fields. Codex enforces manual-only invocation through a separate
agents/openai.yamlwithpolicy.allow_implicit_invocation: false, not through SKILL.md frontmatter. If you target Codex too, you need to set both.
If the skill is genuinely Claude-Code-only (or Claude Code plus VS Code Copilot), use the field, drop the trigger language, and move on. The portability section below covers when that single-vendor target is the right call.
Frontmatter: beyond name and description#
The frontmatter supports several optional fields beyond name and description. Some are part of the agentskills.io spec
and run anywhere; others are vendor-specific extensions that real platforms have shipped because the spec is too narrow for what authors actually need. Decide your portability target before you decide which fields to use.
Pick a portability target before you write frontmatter#
There is a recurring error in skill-writing tutorials (this guide included, until this revision): treat cross-platform portability as a virtue and stick to the spec’s small surface area. That is correct advice for one specific situation - skills you intend to publish for arbitrary teams on arbitrary tools - and wrong advice for everything else.
Three honest portability targets, with the tradeoffs each makes:
Single vendor (e.g., Claude Code only). Choose this when the skill is for your team, your team uses Claude Code, and there is no realistic plan to migrate. You get the full Claude Code frontmatter table - disable-model-invocation, user-invocable, allowed-tools, model, agent, argument-hint, hooks - and you should use whatever genuinely fits the workflow. Anthropic’s own skill-creator skill
ships with only name + description, but other Anthropic-published skills lean on Claude Code extensions when the workflow demands it. Targeting one vendor is not a failure mode; it is a deliberate scope decision.
A small set of vendors you control (e.g., Claude Code + VS Code Copilot). Some fields work on multiple non-spec platforms. disable-model-invocation and user-invocable work on both Claude Code and VS Code GitHub Copilot (per the VS Code skills docs
). allowed-tools works on Claude Code with partial support on Gemini CLI but is missing on VS Code Copilot’s documented field list. When you target a small set, write frontmatter that satisfies the intersection, and document the assumption in the skill body.
Spec-level portability (anywhere agentskills.io runs). Choose this for skills you publish to public registries, share across organizations, or expect to outlive any single tool. Stick to name, description, license, compatibility, metadata. Anything else is best-effort and will be silently ignored by some platform.
The mistake to avoid is the implicit fourth option - “spec-level portability by default, even though I only use Claude Code”. That choice trades real ergonomics (manual invocation, tool restrictions, model selection) for a portability promise no one is collecting on. Write down which target you picked at the top of the skill body or in the project’s skill catalog, so future maintainers do not bend the skill back toward universalism for no reason.
Spec fields (work on every spec-conformant platform)#
name(required) - 1 to 64 characters, lowercase letters, digits, and hyphens only. Must match the parent directory name. If your directory isskills/code-reviewer/, the name must becode-reviewer. A mismatch silently prevents the skill from loading on several platforms.description(required) - 1 to 1,024 characters. The discovery trigger for model-invocable skills, covered in detail above. For skills set to manual-only invocation, becomes a human-readable catalog label.license(optional) - Keep it short: either the name of a standard license or a reference to a bundled LICENSE file.compatibility(optional) - Only include this if the skill has genuine environment requirements beyond basic agent capabilities. Most skills do not need this field.allowed-tools(optional, marked Experimental in the spec) - Restricts which tools the agent can use while the skill is active. A code review skill might specifyallowed-tools: [Read, Grep, Glob]to prevent the agent from accidentally modifying files during review. Full support on Claude Code, partial on Gemini CLI, ignored on VS Code Copilot. The spec text itself warns: “Support for this field may vary between agent implementations.”
Invocation-control fields (Claude Code, VS Code Copilot)#
These two fields together let you decide who can trigger a skill. They are documented in Claude Code’s skills reference and VS Code’s agent skills docs . They are not in the open spec.
disable-model-invocation(optional, defaultfalse) - Whentrue, the model never auto-loads the skill. The description is dropped from context. Invocation is manual only, via/skill-name. Use for procedures the user should consciously decide to start: production deploys, destructive cleanups, end-of-sprint checklists.user-invocable(optional, defaulttrue) - Whenfalse, the user cannot invoke the skill from the slash-command picker. Use for background-knowledge skills the model should pull in autonomously but users have no reason to trigger directly.
The four corners of this matrix:
disable-model-invocation |
user-invocable |
Who invokes | Description visibility |
|---|---|---|---|
false (default) |
true (default) |
Model and user | Loaded into system prompt |
true |
true (default) |
User only | Not loaded |
false (default) |
false |
Model only | Loaded into system prompt |
true |
false |
Nobody (invalid) | - |
OpenAI Codex enforces manual-only invocation through a separate agents/openai.yaml file with policy.allow_implicit_invocation: false; the field does not live in SKILL.md frontmatter on Codex. If you ship the same skill to Codex and Claude Code, you set both.
Other Claude Code extensions (Claude Code only)#
The full Claude Code frontmatter table includes model, effort, context, agent, hooks, paths, shell, argument-hint, arguments, and when_to_use alongside the invocation-control fields above. None of these are in the open spec or supported elsewhere. Use them when you have explicitly chosen single-vendor as your portability target. Consult the Claude Code reference
for current semantics; the surface evolves.
Convention fields (for tooling, ignored by agents)#
These fields are not part of any specification. Agents do not read them. They exist for human organization, skill catalogs, and external tooling.
tags(optional) - A list of keywords for categorization and discovery. Tags help when maintaining a skill catalog or building tooling that filters skills by domain. Example:tags: [security, compliance, audit].triggers(optional) - Explicit activation phrases for skill catalogs and documentation. Some community tools use this field to generate searchable indexes. Agents do not use this field for activation - they use only thedescriptionfield. If you want specific phrases to trigger your skill, put them in the description.metadata(optional) - A key-value map for additional properties. Useful for custom tooling, team ownership tracking, or organizational metadata. Example:metadata: { team: platform, priority: high }.
Frontmatter examples by portability target#
Spec-level portability, model-invocable skill:
---
name: security-audit
description: >
Use when performing security reviews, vulnerability assessments, or compliance checks.
Handles OWASP Top 10, dependency scanning, secrets detection, and access control review.
license: MIT
---
Claude Code single-vendor, manual-only release checklist:
---
name: release-checklist
description: Walks the user through the production release checklist for the payments service.
disable-model-invocation: true
allowed-tools:
- Read
- Bash
---
Claude Code + VS Code Copilot intersection, model-invocable analysis skill with read-only enforcement on the platforms that honour it:
---
name: architecture-review
description: >
Use when reviewing system architecture, evaluating design patterns,
or auditing module boundaries.
license: MIT
allowed-tools:
- Read
- Grep
- Glob
tags:
- architecture
- review
---
Designing for success#
Before writing a single line of SKILL.md, two design decisions shape everything that follows: what kind of skill you’re building, and how you’ll know it works.
Problem-first vs. tool-first design#
Skills emerge from two directions, and recognizing which one you’re starting from prevents wasted effort.
Problem-first: You start with a user’s goal. “I need to set up a project workspace.” The skill orchestrates whatever tools are needed to accomplish that outcome. The user describes what they want; the skill handles how.
Tool-first: You start with an available capability. “We have a Jira MCP server connected.” The skill teaches the agent your team’s conventions for using Jira effectively. The user has access; the skill provides expertise.
Most skills lean one direction. A project onboarding skill is problem-first: the user describes an outcome, and the skill coordinates multiple tools. A Jira workflow skill is tool-first: the user already has Jira access, and the skill adds procedural knowledge.
Knowing which framing fits your use case helps you choose the right structure. Problem-first skills tend toward sequential workflows with clear phases. Tool-first skills tend toward reference patterns with decision trees.
Three categories of skills#
In practice, skills fall into three broad categories. Understanding which category your skill belongs to focuses the design process.
Document and asset creation. Skills that produce consistent, high-quality output: documents, code, presentations, designs. These skills embed style guides, templates, and quality checklists. They primarily use the agent’s built-in capabilities (code execution, file creation) rather than external tools. Example: a frontend design skill that generates polished UI components following brand guidelines.
Workflow automation. Skills that guide multi-step processes requiring consistent methodology. These skills provide step-by-step workflows with validation gates, templates for common structures, and iterative refinement loops. Example: a sprint planning skill that analyzes team velocity, suggests task prioritization, and creates tickets.
MCP enhancement. Skills that add procedural knowledge on top of MCP tool access. Without a skill, users connect an MCP server and don’t know the optimal usage patterns. With a skill, proven approaches activate automatically. Example: a code review skill that coordinates Sentry error data with GitHub PR analysis to produce actionable fix recommendations.
These categories aren’t rigid. Some skills span two categories, and that’s fine. The value of the categories is helping you decide what kind of instructions your skill needs. Creation skills need templates and style guides. Automation skills need workflow steps and validation gates. Enhancement skills need domain expertise and error handling for common tool failures.
Defining success before you start#
Before writing the skill body, define what success looks like. Without criteria, you can’t tell whether iteration is improving the skill or changing it sideways.
Quantitative signals you can track:
- Trigger accuracy: Run 10-20 test queries that should trigger your skill. Track how many times it loads automatically vs. requires explicit invocation. Target: 80-90% correct activation.
- Workflow efficiency: Compare the same task with and without the skill enabled. Count tool calls, messages exchanged, and total tokens consumed.
- Error rate: Monitor tool call failures during test runs. A well-designed skill should handle common error cases without user intervention.
Qualitative signals to watch for:
- Self-sufficiency: Does the user need to redirect or clarify mid-task? During testing, note how often you intervene. A good skill reduces interventions to near zero.
- Consistency: Run the same request 3-5 times. Do the outputs have consistent structure and quality? Inconsistency reveals ambiguous instructions.
- First-try success: Can someone unfamiliar with the skill accomplish the task on the first attempt? This tests whether the description triggers correctly and the instructions are self-contained.
These are aspirational targets, not pass/fail thresholds. The goal is to have something to measure against, so each iteration moves in a clear direction.
Writing the skill body: procedures, not documentation#
The body of SKILL.md is where your instructions live. This is what the agent reads after deciding to activate the skill. The specification places no format restrictions on the body, but the way you write it dramatically affects how well agents follow your instructions.
The most common mistake is writing documentation instead of procedures. A skill is not a README. It is not a tutorial. It is a set of actionable steps that an agent should execute.
❌ Documentation style (weak):
# Database Migration Skill
This skill helps with database migrations. Our project uses Drizzle ORM
with SQLite. Migrations are important for keeping the schema in sync
across environments. The migrations directory contains all historical
migration files.
✅ Procedural style (strong):
# Database Migration
## Workflow
1. Read the current schema from `src/db/schema.ts`
2. Compare against the latest migration in `drizzle/migrations/`
3. Generate a new migration:
```bash
npx drizzle-kit generate
```
4. Review the generated SQL for destructive operations (DROP, ALTER column type)
5. If destructive operations are found, add a data migration step before the schema change
6. Run the migration against the local database:
```bash
npx drizzle-kit migrate
```
7. Verify by running the test suite: `npm test`
## Rules
- Never generate migrations that drop columns without explicit user confirmation
- Always create a backup script before destructive migrations
- Migration file names use the format: `NNNN_description.sql`
Notice the difference. The first version explains what things are. The second version tells the agent what to do, in what order, with specific commands and verification steps. Agents are execution engines - give them executable procedures.
Structure recommendations:
Start with a workflow section that outlines the main steps. Follow with rules or constraints that apply across all steps. Add specific sections for edge cases, error handling, or conditional logic. Reference external files for lengthy reference material rather than inlining everything.
Use conditional logic when appropriate. Agents handle branching well:
## Step 3: Determine Test Strategy
- If the change modifies a public API endpoint:
- Run the full integration test suite
- Update the API contract tests
- If the change is internal refactoring only:
- Run unit tests for affected modules
- Skip integration tests unless dependencies changed
- If the change touches database models:
- Run migration tests first
- Then run the full test suite
Include verification steps. Do not assume the agent will check its own work:
## Step 5: Validate Output
1. Run the linter: `npm run lint`
2. If linting fails, fix the issues before proceeding
3. Run type checking: `npx tsc --noEmit`
4. Run the relevant test file: `npm test -- --grep ""`
5. If any check fails, diagnose and fix before marking the task complete
Format SKILL.md for the model, not for humans#
Most skill-authoring guides - including earlier versions of this one - format SKILL.md like a README: hard-wrapped paragraphs, blockquotes for examples, ladders of indented bullets, decorative section dividers. That formatting makes a document readable on a screen. Skills are not read on screens.
When a skill activates, the body lands in the model’s context window. The reader is a language model. Anthropic’s skills overview describes this directly: “When a Skill is triggered, Claude uses bash to read SKILL.md from the filesystem, bringing its instructions into the context window.” GitHub Copilot, OpenAI Codex, and Gemini CLI describe their loading model the same way. No human is in the loop at activation time. Optimize for the actual reader.
This blog post is formatted for humans because humans read it. Do not propagate that formatting into the SKILL.md files this post teaches you to write. The two artifacts have opposite audiences.
The cost framing comes straight from Anthropic’s best-practices guide : “The context window is a public good. Your Skill shares the context window with everything else Claude needs to know… once Claude loads it, every token competes with conversation history and other context.” Decorative formatting is not free. It costs tokens that could have been your conversation history, and it dilutes the model’s attention across less informative surface area.
What to drop from SKILL.md#
- Hard-wrapped prose. A sentence wrapped at 80 characters costs more tokens than the same sentence on one line and does not help the model parse anything. Let lines be as long as your sentences need.
- Blockquotes and “Note:” boxes. The model treats
> Note: ...as ordinary prose with extra punctuation. Either the note matters - in which case promote it to a top-level instruction - or it does not, in which case delete it. - Three-deep bullet ladders. Indented sub-sub-bullets look organized in a Markdown viewer and dilute attention in a context window. Prefer flat numbered steps with terse phrasing, or a small table when you have genuinely parallel rows of data.
- Decorative dividers. Horizontal rules, emoji-prefixed headers, ASCII boxes. The model reads them as text and assigns them weight. Skip them.
- Explanations the model does not need. Anthropic’s best-practices guide gives a direct test: “Does Claude really need this explanation? Can I assume Claude knows this? Does this paragraph justify its token cost?” If the answer is “the model already knows what zod is”, delete the zod tutorial.
- Walls of MUST/ALWAYS/NEVER. Anthropic’s bundled skill-creator flags rigid all-caps imperatives as a yellow flag: “Try to explain to the model why things are important in lieu of heavy-handed musty MUSTs.”
What to keep#
- Imperative form. Same skill-creator: “Prefer using the imperative form in instructions.” “Run the linter” beats “You should run the linter.”
- Numbered steps with concrete commands. Numbers encode order; commands encode exact behavior. This is the format the model follows most reliably.
- Tables for parallel structured data. A two- or three-column table costs few tokens and communicates structure unambiguously. Use them for decision matrices, file/role mappings, error-code lookups.
- Code fences for everything mechanical. Inline backticks for paths, flags, identifiers. Fenced blocks for multi-line code or commands. Keep the language tag accurate; some tools depend on it.
- Examples as input/output pairs. Anthropic notes that examples in SKILL.md work the same way they do in prompting: “provide input/output pairs just like in regular prompting.” One concrete pair beats a paragraph describing the format.
A worked comparison#
Decorative version - the kind a human-readable guide naturally produces:
> **Note:** Before generating a migration, you should always verify
> that the schema file is up to date.
When generating a migration, please follow these steps carefully:
1. First, take a look at the current schema, which lives in
`src/db/schema.ts`. You may need to compare it against the
latest migration file in the `drizzle/migrations/` directory.
- Pay special attention to column type changes
- Also check for any renamed columns
2. Next, run the migration generator (this can take a moment):
```bash
npx drizzle-kit generate
```
Dense version - same instruction, written for the model:
1. Read `src/db/schema.ts`. Compare against the latest file in `drizzle/migrations/`. Flag column-type changes and renames.
2. Run `npx drizzle-kit generate`.
The dense version is roughly a third of the tokens, communicates the same procedure, and drops the visual scaffolding the model was paying for and ignoring. Compress every step that drifts into polite prose.
The honest exception#
Two cases soften the rule:
references/files load on demand only, when the body explicitly directs the agent to read them. They can afford length and discursive prose because the cost is paid only when the content is actually needed.- User-invoked-only skills (
disable-model-invocation: true) load less frequently overall, and their description never enters context at all - so the description specifically is freed from the trigger discipline. The body still ends up in context every time the user invokes the skill, so the dense-format rule still applies there.
The principle is not “be decorative when you can get away with it”. It is “decoration is paid for in tokens, charged on every load - reduce loads or reduce decoration.” Frequently-loaded SKILL.md bodies should be ruthlessly dense.
Where skills live: storage across platforms#
Skills can be stored at three levels, and the storage location determines their scope and sharing model.
Project skills are stored inside your repository and shared via version control. Every team member gets them automatically. This is the primary way teams share skills.
| Platform | Primary directory | Also supported |
|---|---|---|
| VS Code / Copilot | .github/skills/<skill-name>/ |
|
| Claude Code | .claude/skills/<skill-name>/ |
|
| Cursor | .cursor/skills/<skill-name>/ |
|
| Gemini CLI | .gemini/skills/<skill-name>/ |
.agents/skills/ (priority) |
| OpenAI Codex | .agents/skills/<skill-name>/ |
|
| Generic (cross-platform) | .agents/skills/<skill-name>/ |
Note:
.agents/is emerging as a cross-platform location. OpenAI Codex uses.agents/skills/as its primary directory (not.codex/). Gemini CLI supports both.gemini/skills/and.agents/skills/, with.agents/skills/taking precedence when both exist. Google Antigravity and OpenCode also use.agents/for cross-platform compatibility.
Personal skills are stored in your home directory and available across all your workspaces. Use these for personal workflows that are not team-specific.
| Platform | Primary directory | Also supported |
|---|---|---|
| VS Code / Copilot | User profile (managed via VS Code settings) | |
| Claude Code | ~/.claude/skills/<skill-name>/ |
|
| Cursor | ~/.cursor/skills/<skill-name>/ |
|
| Gemini CLI | ~/.gemini/skills/<skill-name>/ |
~/.agents/skills/ (priority) |
| OpenAI Codex | ~/.agents/skills/<skill-name>/ |
Extension/plugin skills are bundled with IDE extensions or agent plugins. These are distributed through package managers or marketplaces.
When skills share the same name across multiple storage locations, project-level skills take precedence over personal skills, which take precedence over extension skills. This lets teams override default behavior without deleting the original skill.
Skills vs. everything else#
If you have been working with AI agents, you have likely encountered custom instructions, prompt files, AGENTS.md, MCP servers, and Cursor Rules. Skills occupy a specific niche in this ecosystem, and understanding the boundaries prevents confusion.
- Skills vs. Custom Instructions (
.instructions.md,copilot-instructions.md, User Rules). Custom instructions are always-on rules that apply to every session. They define coding standards, naming conventions, and general preferences. Skills are on-demand capabilities that load only when relevant. Your instruction file says “always use TypeScript and follow our naming conventions”. Your skill says “here is the step-by-step procedure for creating a new API endpoint in our specific framework”. - Skills vs.
AGENTS.md.AGENTS.mdfiles provide persistent context about your project that the agent reads at session start. They describe architecture, conventions, and project-specific knowledge. Skills are procedural - they describe how to do something, not what something is. YourAGENTS.mdexplains your project structure and tech stack. Your skill teaches the agent how to execute a specific workflow within that structure. - Skills vs. Prompt Files / Commands. Prompt files (
.prompt.md) and Cursor Commands (.cursor/commands/) are user-invoked templates triggered by slash commands. You type/create-componentand the prompt runs. Skills are model-invoked - the agent decides when to activate them based on the task at hand. You can also invoke skills manually with/in some platforms, but their primary mode is autonomous activation. - Skills vs. MCP Servers. MCP (Model Context Protocol) connects agents to external services - databases, APIs, issue trackers. Skills are procedural knowledge about how to use those connections effectively. Your MCP server provides access to Jira. Your skill teaches the agent your team’s workflow for creating, updating, and transitioning Jira issues.
- Skills vs. Cursor Rules (
.cursor/rules/). Cursor Rules use a structured format with frontmatter controlling when they apply (always, intelligently, by file pattern, or manually). Skills follow the universalSKILL.mdformat and are portable across platforms. If you only use Cursor, either approach works. If your team uses multiple tools, skills provide portability. - Skills vs. Bundles. A bundle is a curated collection of skills grouped by role or project type. Bundles are not a separate artifact or spec feature - they are documentation that says “for this role, install these five skills”. The bundle concept helps onboarding: instead of presenting fifty skills to a new team member, you point them to the “Backend Developer” bundle. Skills are the atoms; bundles are molecules.
- Skills vs. Workflows. A workflow chains multiple skills into a sequence where output from one skill feeds into the next. Skills are standalone procedures. Workflows are documentation that orchestrates them into multi-step pipelines. Your
run-testsskill anddeploy-stagingskill are independent. Your “Ship to Production” workflow document sequences them: run tests → deploy staging → verify → deploy production.
In practice, a well-organized project uses several of these together. Instructions set the baseline, AGENTS.md provides architectural context, skills handle specific procedures, and MCP servers connect external services.
The “skills replace MCP” myth#
When Agent Skills became an open standard in December 2025 , a narrative emerged in some developer circles: MCP is now obsolete, Skills do everything MCP does, we can remove our MCP servers. This is a category error - a fundamental misunderstanding of what these technologies provide. The confusion is understandable, because both technologies “extend” what an agent can do. But they extend capabilities in completely different dimensions.
The essential distinction#
Think of it this way: MCP is a power cord. Skills are a user manual.
An MCP server gives the agent the ability to interact with an external system - your database, your issue tracker, your cloud infrastructure. Without that connection, the agent cannot read your production logs or create a Jira ticket, no matter how detailed its instructions are. You cannot describe your way into a database connection. The agent needs live access, and MCP provides that access through a standardized communication protocol .
A skill teaches the agent how to use that access effectively. Your MCP server connects to Jira. Your skill teaches the agent your team’s conventions for writing tickets: which fields matter, how to categorize issues, what information to include in the description, which project keys to use. The skill transforms raw tool access into disciplined, team-aligned behavior.
Neither one replaces the other. They operate at different layers of the stack entirely.
Where MCP cannot be replaced#
Certain capabilities require active, bidirectional connectivity that no amount of documentation can provide. MCP remains essential for these scenarios:
Live data access. When an agent needs to query your database, check the current state of a Kubernetes cluster, or read messages from a Slack channel, it needs a live connection. Skills can describe query patterns and proven approaches, but they cannot execute queries. The MCP PostgreSQL server reads data from real tables. A skill about “how to write SQL queries” produces text that looks like SQL - the agent still needs MCP to run it against a real database.
Real-time streaming. MCP supports Server-Sent Events and streaming protocols for continuous data flow. An agent monitoring production logs, processing real-time analytics, or watching a deployment pipeline needs streaming updates as they happen. Skills are static documents loaded once at activation. They cannot stream data.
Write operations with side effects. Creating a GitHub issue, sending a Slack message, deploying code to staging - these actions change state in external systems. MCP servers provide the authenticated connections that make these operations possible. A skill can describe your deployment workflow in perfect detail, but without an MCP server (or equivalent tool access), the agent cannot push the deploy button.
Authentication and authorization. MCP servers handle OAuth flows, API keys, service account credentials, and permission boundaries. They provide centralized credential management for team environments where multiple developers share access to the same services. Skills have no authentication layer - they are plain text files that assume tool access already exists.
Cross-agent communication. In multi-agent architectures, MCP enables capability discovery where agents negotiate which features they support and how to interact. This coordination requires active protocol participation that static files cannot provide.
Where skills work better#
Skills outperform MCP in scenarios that require teaching rather than connecting:
Procedural knowledge. Your team’s code review checklist, the steps for creating a database migration, the conventions for writing commit messages - these are procedures that agents must learn, not systems they must connect to. Skills excel at encoding this institutional knowledge in a format agents follow reliably.
Context efficiency. MCP tool descriptions consume significant context tokens. One study found that loading the Sentry MCP server alone consumed approximately 8,000 tokens for tool definitions - before any work began. Skills use progressive disclosure: the description costs roughly 100 tokens per skill, and the full body loads only when activated. For capability-rich environments, skills scale more efficiently.
Portability and iteration. Skills are markdown files. You can write them, commit them, review them in pull requests, and iterate on them without deploying any infrastructure. MCP servers require running services, handling connections, and maintaining availability. For solo developers or small teams, the skills plus CLI approach often proves more practical than standing up MCP servers.
Domain expertise transfer. When you want to teach an agent how your team handles incident response, the skill format provides a natural structure: workflow steps, conditional logic, verification checkpoints, references to additional documentation. MCP has no equivalent concept for transmitting procedural expertise - it provides tools, not training.
Where they work together#
The most effective agent deployments combine both technologies. Anthropic’s official guidance is explicit: “Use both together: MCP for connectivity, Skills for procedural knowledge”.
Consider a concrete example. Your company uses Google Drive for document storage. The Google Drive MCP server provides the connection - the agent can list files, read contents, create documents. But your team has conventions: project documents live in specific folder hierarchies, naming patterns encode project codes and document types, certain templates should be used for certain document categories.
A skill captures that expertise:
---
name: company-docs
description: >
Use when creating, finding, or organizing documents in Google Drive.
Handles folder navigation, naming conventions, and template selection.
---
# Company Document Procedures
## Finding documents
1. Start from the shared "Projects" folder
2. Navigate by project code (format: PRJ-NNNN)
3. Within each project folder, documents are organized by type:
- /specs - Technical specifications
- /designs - Design documents and mockups
- /reports - Status reports and retrospectives
## Creating new documents
1. Identify the document type and corresponding template
2. Copy the template from /Templates/{type}-template
3. Place the copy in the correct project subfolder
4. Rename following pattern: {PRJ-CODE}_{type}_{date}\_{title}
The MCP server provides access. The skill ensures that access is used according to your conventions. Neither alone is enough; together, they produce an agent that works with your documents the way your team works with them.
Another real-world example comes from Sentry’s code review skill , which explicitly complements their MCP server: “Automatically analyzes and fixes detected bugs in GitHub Pull Requests using Sentry’s error monitoring data via their MCP server”. The MCP server provides the live data feed from Sentry. The skill teaches the agent how to analyze that data and produce actionable fix recommendations.
When MCP becomes unnecessary#
Fair warning: not every MCP use case survives scrutiny. Some teams deploy MCP servers when simpler approaches would work.
CLI tools already handle it. If your agent can run git, npm, kubectl, or aws via bash, an MCP server wrapping those same CLIs adds complexity without benefit. The agent already has tool access through the shell. A skill teaching effective CLI usage often outperforms an MCP server that re-exposes the same functionality through a different protocol.
Static data that rarely changes. Reference documentation, style guides, API schemas - if the data updates infrequently, embedding it in a skill’s references/ directory may be simpler than maintaining an MCP server that serves the same content. MCP excels at live, changing data. For static reference material, the overhead is harder to justify.
Local development workflows. Solo developers working on local projects sometimes deploy MCP servers out of enthusiasm for the technology rather than necessity. If you are the only consumer and the tools are already available in your shell, skills provide the knowledge layer without the infrastructure burden.
The decision framework#
When evaluating whether a capability needs MCP, skills, or both, ask these questions:
Does this require live access to external state? If yes, MCP (or equivalent tool access). Skills cannot query databases, call APIs, or interact with running systems.
Does this require teaching a procedure or convention? If yes, skills. MCP provides tools, not training. Even with perfect tool access, agents benefit from procedural guidance.
Does this require authentication across a team? If yes, MCP provides centralized credential management. Skills have no auth layer.
Does this change frequently enough that embedding static content would become stale? If yes, MCP can serve fresh data on demand. Skills load what they contain at skill-creation time.
Can existing CLI tools accomplish this? If yes, consider whether a skill teaching effective CLI usage is simpler than deploying an MCP server.
Most production environments land on a hybrid: MCP servers for essential integrations (source control, issue tracking, cloud infrastructure), skills for team-specific procedures and conventions. The ratio varies by organization, but the pattern of complementary deployment appears consistently in mature setups.
The real alternative to MCP#
The SmartScope analysis captures an important insight: “Skills aren’t the replacement - bash and curl are”. When developers argue against MCP, they are often arguing for direct CLI access documented through skills, not for skills alone. The debate is about protocol complexity versus shell simplicity, with skills providing the procedural layer in both cases.
This framing clarifies what skills replace: not MCP servers, but the informal knowledge that developers carry in their heads about how to use tools effectively. Skills formalize that knowledge. MCP remains the connectivity layer that provides tool access in the first place.
Installing and managing skills#
Manual installation is straightforward: create the directory, add the SKILL.md file, and the agent discovers it automatically on the next session. This is how most teams manage their own skills - version-controlled alongside the codebase.
CLI installation using the skills CLI lets you install community skills from the skills.sh directory. The CLI runs directly via npx without global installation:
# Install a skill from the registry
npx skills add vercel-labs/agent-skills
# General pattern: owner/skill-name
npx skills add <owner>/<skill-name>
The CLI places skills in the correct directory for your current platform. Browse available skills at skills.sh - the leaderboard shows popular skills ranked by community usage. For complete CLI documentation, see the skills.sh docs .
Note: “Community” typically means unmoderated. Skills from public registries, GitHub repositories, and leaderboards are not reviewed for quality, accuracy, or safety. Much of what circulates online is AI-generated content of questionable value - vague instructions, incorrect patterns, or procedures that sound plausible but fail in practice. Before installing any external skill, read the
SKILL.mdfile yourself. Does the description follow trigger patterns or does it summarize the workflow? Are the instructions procedural or just documentation? Do the scripts actually exist and do what they claim? A few minutes of review prevents hours of debugging why your agent behaves strangely.
Managing skills in different platforms:
In VS Code, type /skills in chat or use the Chat: Configure Skills command. The diagnostics view (right-click in Chat > Diagnostics) shows all loaded skills and any errors.
In Claude Code, skills are auto-discovered from the storage directories. Use claude --debug to see loading errors during startup.
In Gemini CLI, skills require explicit user consent before activation - Gemini proposes activation and you approve. Interactive commands in chat:
/skills list- display all skills and their status/skills enable <name>and/skills disable <name>- control activation/skills link <path>- symlink a skill from a local directory/skills reload- refresh skill discovery
Terminal commands (gemini skills):
gemini skills list- enumerate discovered skillsgemini skills install <source>- install from Git repos, local paths, or .skill files; use--pathfor monoreposgemini skills uninstall <name>- remove a skillgemini skills enable/disable <name> --scope workspace|user- control activation scope
In Cursor, skills follow the same discovery pattern from .cursor/skills/. You can toggle Agent Skills support in Cursor Settings > Rules.
Validation before deployment catches common issues:
# Using the skills-ref reference library
npx skills-ref validate ./my-skill/SKILL.md
This checks frontmatter structure, field constraints (name length, character restrictions), and format compliance.
Maintaining a skill catalog
When a project accumulates more than ten skills, browsing the filesystem becomes tedious. Maintain a catalog file that lists all available skills with their categories and activation keywords.
A minimal catalog in docs/SKILL-CATALOG.md:
# Skill Catalog
| Skill | Category | Keywords (from description) |
| --------------------- | ----------- | ------------------------------------------- |
| `create-api-endpoint` | Development | "new endpoint", "create route", "add API" |
| `generate-migration` | Database | "migration", "schema change", "drizzle" |
| `run-tests` | Testing | "run tests", "test suite", "jest", "pytest" |
| `security-audit` | Security | "security review", "vulnerability", "OWASP" |
| `deploy-staging` | Release | "deploy staging", "stage deploy" |
The “Keywords” column documents phrases that activate each skill. These come from the skill’s description field (which agents read), not from a separate triggers field. The catalog is for humans browsing available skills.
Update the catalog when adding or removing skills. Some teams automate catalog generation by parsing SKILL.md frontmatter across all skill directories.
Patterns that work#
These patterns come from production skills across multiple teams and platforms.
Pattern: Multi-file skill with supporting resources. When your procedure needs scripts, templates, or reference material, split them into separate files. The SKILL.md body stays focused on the workflow, and resources load only when the agent reaches the step that references them.
api-endpoint-creator/
SKILL.md
scripts/
scaffold.sh
generate-tests.sh
references/
api-conventions.md
error-codes.md
assets/
endpoint-template.ts
test-template.ts
In the SKILL.md body, reference these files with relative paths:
## Step 2: Scaffold the Endpoint
Run the scaffolding script to generate the boilerplate:
See `scripts/scaffold.sh` for the implementation.
## Step 4: Apply API Conventions
Follow the conventions documented in `references/api-conventions.md` for:
- URL naming patterns
- Request/response envelope format
- Error response structure
Keep referenced files one level deep. Deeply nested structures are harder for agents to navigate.
Pattern: Restricted tool access for safety. When a skill should only read or analyze (not modify), use allowed-tools to enforce the constraint:
---
name: architecture-review
description: >
Use when reviewing system architecture, analyzing dependencies,
or evaluating design patterns in the codebase.
allowed-tools:
- Read
- Grep
- Glob
---
This prevents accidental modifications during analysis tasks.
Pattern: Multi-service coordination. When workflows span multiple services, a skill orchestrates the handoff between them. Without a skill, the user must direct each step manually, remembering which service handles what.
## Design-to-development handoff
### Phase 1: Design export (Figma MCP)
1. Export design assets from Figma
2. Generate design specifications
3. Create asset manifest
### Phase 2: Asset storage (Drive MCP)
1. Create project folder in Drive
2. Upload all assets
3. Generate shareable links
### Phase 3: Task creation (Linear MCP)
1. Create development tasks referencing the design specs
2. Attach asset links to each task
3. Assign to the engineering team
### Phase 4: Notification (Slack MCP)
1. Post handoff summary to #engineering
2. Include asset links and task references
Each phase passes data to the next: asset URLs from Figma flow into Drive folders, shared links from Drive attach to Linear tasks, and task references appear in Slack notifications. Clear phase separation matters here. Validate at each phase boundary before proceeding. If asset upload fails, don’t create tasks pointing to missing files.
Pattern: Cross-references without force-loading. Sometimes skills relate to each other. A deployment skill might reference a testing skill. Avoid using @ syntax to reference other skills - that force-loads the referenced file and burns tokens before they are needed. Instead, use plain text references with explicit markers:
## Prerequisites
Before deploying, ensure all tests pass. If tests fail,
follow the procedures in the `test-runner` skill (REQUIRED).
For performance-critical deployments, also consult the
`load-testing` skill (OPTIONAL) for pre-deployment benchmarks.
The REQUIRED/OPTIONAL markers tell the agent how to prioritize without forcing immediate loading.
Pattern: Naming conventions that communicate purpose. Use gerunds (verb-ing) for process skills and verb-first phrases for action skills:
code-reviewing- an ongoing process with multiple passesgenerate-migration- a discrete action that produces outputtest-debugging- a diagnostic processcreate-api-endpoint- a specific generation task
Skill bundles#
Bundles are a documentation convention, not a specification feature. No agent reads bundle files directly. You create bundles to help humans discover and organize skills.
When a project accumulates dozens of skills, discovery becomes a problem. New team members face a wall of options with no guidance on where to start. Bundles solve this by grouping skills into role-based collections.
A bundle is not a technical artifact - it is a curated recommendation list. You do not “install a bundle”. You install the skills, then document which combinations matter for which roles.
Example bundles by role:
| Role | Skills |
|---|---|
| Backend Developer | create-api-endpoint, generate-migration, run-tests, code-review |
| Security Engineer | security-audit, dependency-scan, secrets-detection, penetration-test |
| Agent Architect | create-skill, prompt-optimizer, agent-debugger, mcp-connector |
| Release Engineer | changelog-generator, version-bump, deploy-staging, deploy-production |
Documenting bundles:
Create a BUNDLES.md or add a section to your skill catalog:
## Backend Developer bundle
Start here if you work on API development and database operations.
| Skill | Purpose |
| --------------------- | ----------------------------------------------------- |
| `create-api-endpoint` | Scaffold new REST endpoints with validation and tests |
| `generate-migration` | Create and apply database schema changes |
| `run-tests` | Execute test suites with coverage reporting |
| `code-review` | Multi-pass review for logic, security, and style |
### Learning path
1. Start with `run-tests` - understand the test workflow first
2. Move to `create-api-endpoint` - the most common daily task
3. Add `generate-migration` when you work on schema changes
4. Use `code-review` when reviewing others' PRs
Bundles also support progression. A “Junior Backend” bundle might include three core skills. The “Senior Backend” bundle adds four more. This creates natural learning paths without overwhelming new engineers.
Composing skills into workflows#
Workflows are a documentation and prompting convention, not a specification feature. You create workflow documents that describe skill sequences. Agents follow them when you reference the document or describe a goal that matches the workflow.
Individual skills handle discrete tasks. Workflows chain skills into multi-step pipelines where output from one skill feeds into the next.
The difference matters: a skill is a standalone procedure. A workflow orchestrates multiple procedures toward a larger outcome.
Example: Ship a feature workflow
[Feature Request]
|
v
+------------------+ Tech specification +------------------+ Plan +------------------+
| **specify** | -----------------------> | **make-plan** | ---------> | **implement** |
| (Architect) | | (Planner) | | (Coder) |
+------------------+ +------------------+ +------------------+
|
Source code
|
v
+-------------------+ Approval +------------------+ Test +------------------+
| **deploy-staging**| <------------------ | **code-review** | <------------ | **run-tests** |
| (Release) | | (Reviewer) | | (Tester) |
+-------------------+ +------------------+ +------------------+
Each step produces artifacts the next step consumes. The specification feeds the planner. The plan guides implementation. Tests verify the implementation. Review validates quality. Deployment ships the result.
Documenting workflows:
Create a WORKFLOWS.md or docs/workflows/ directory:
## Ship to production workflow
Use this workflow when releasing verified features to production.
### Prerequisites
- All tests pass
- Code review approved
- Staging verification complete
### Steps
1. **run-tests** - Execute full test suite
- Input: Current branch
- Output: Test results (must pass to proceed)
- Skill: `run-tests`
2. **deploy-staging** - Deploy to staging environment
- Input: Passing test results
- Output: Staging URL
- Skill: `deploy-staging`
3. **verify-staging** - Manual or automated verification
- Input: Staging URL
- Output: Verification checklist
- Skill: `smoke-test` (optional)
4. **deploy-production** - Deploy to production
- Input: Verified staging
- Output: Production deployment
- Skill: `deploy-production`
### Abort conditions
- Stop at step 1 if tests fail
- Stop at step 3 if verification finds issues
- Roll back at step 4 if health checks fail
Workflow invocation:
Workflows are not automatically executed. The agent follows them when you reference the workflow document or describe the goal that matches a workflow. For example:
- “Follow the ship-to-production workflow”
- “I need to deploy this feature to production” (agent matches to workflow)
Some teams create a thin wrapper skill that references the workflow document:
---
name: ship-to-production
description: >
Use when deploying verified features to production.
Orchestrates testing, staging deployment, verification, and production release.
---
# Ship to Production
Follow the workflow documented in `docs/workflows/ship-to-production.md`.
Load and execute each step in sequence. Stop if any step fails.
This pattern keeps the workflow documentation human-readable while giving the agent a clear entry point.
Testing#
A skill isn’t finished when it’s written. It’s finished when it works reliably across different phrasings, contexts, and edge cases. The gap between “looks correct” and “works in practice” is where most skills fail quietly. This section covers proactive testing during development. If you’ve already deployed a skill and something is wrong, see Troubleshooting .
Start with one task#
The most effective skill authors share a common habit: they iterate on a single challenging task until the agent succeeds, then extract the winning approach into the skill. This is faster than building a comprehensive skill and testing broadly from the start.
The reasoning behind this approach is practical. When you test one task repeatedly, you get fast feedback on what works and what confuses the agent. You learn which instructions the agent follows reliably and which it treats as suggestions. You discover which phrasing produces consistent results. Once the skill handles one task well, expanding to cover related tasks becomes straightforward because you’ve established a working foundation.
The three tests every skill needs#
After the initial iteration, structured testing catches problems that ad-hoc use misses. Every skill benefits from three categories of tests.
Triggering tests: does the skill load when it should?
The description field determines activation, and it fails in two directions. Under-triggering means the skill doesn’t load for relevant requests. Over-triggering means it loads for unrelated requests. Both waste the user’s time and degrade trust in the skill.
Test with 10-20 queries across three groups:
Should trigger (obvious phrasing):
- “Help me set up a new project workspace”
- “Create a project in our system”
Should trigger (paraphrased):
- “Initialize a workspace for Q4 planning”
- “I need a new project environment”
Should NOT trigger:
- “What’s the weather forecast?”
- “Help me write a Python function”
- “Create a spreadsheet”
Track how often the skill activates correctly. If it triggers on fewer than 80-90% of relevant queries, the description needs more trigger phrases or synonyms. If it triggers on unrelated queries, add negative triggers or narrow the description scope.
Functional tests: does the skill produce correct outputs?
Once the skill triggers, verify that the agent follows the instructions and produces the expected results:
- Does each step execute in the correct order?
- Are tool calls (MCP or CLI) successful?
- Does the output match the expected format?
- Do edge cases produce reasonable behavior?
Run the same request 3-5 times and compare outputs for structural consistency. If the agent produces different workflows across runs, the instructions are ambiguous somewhere. Find the ambiguous step and tighten the language or add an example.
Performance comparison: is the skill worth the token cost?
This test is often skipped, but it answers an important question: does the skill produce better results than a well-crafted prompt?
Compare the same task with and without the skill enabled:
| Metric | Without skill | With skill |
|---|---|---|
| Back-and-forth messages | 15 | 2-3 |
| Failed tool calls | 3 | 0 |
| User corrections needed | 4 | 0-1 |
| Total tokens consumed | 12,000 | 6,000 |
If the skill doesn’t meaningfully improve at least one metric, it’s adding complexity without delivering value. Simplify the skill or reconsider whether a custom instruction would serve better.
Anti-patterns#
These are the most common mistakes observed in real-world skill repositories. Each one degrades agent performance, sometimes subtly and sometimes catastrophically.
Workflow summary in the description#
This is the most damaging mistake because it short-circuits progressive disclosure. When the description contains the procedure, the agent may never read the full skill body.
❌ Bad: this IS the workflow, the agent may skip the body:
description: "Analyzes git diff, identifies the change type,
generates a commit message in conventional format with scope detection"
✅ Good: this tells when to activate and what capabilities exist:
description: >
Use when generating commit messages or reviewing staged changes.
Handles conventional commits, scope detection, and breaking change notation.
Generic descriptions that match everything or nothing#
Vague descriptions cause two problems: false activations (the skill loads when it should not) and missed activations (the skill does not load when it should).
❌ Bad: too vague, could match almost any request:
description: "Helps with data processing and analysis tasks"
✅ Good: specific triggers with concrete terms:
description: >
Use when querying BigQuery, writing SQL for analytics pipelines,
or optimizing query performance. Handles partitioned tables,
materialized views, and cost estimation.
README-style documentation instead of procedures#
Skills that explain concepts instead of providing step-by-step procedures leave the agent without actionable guidance.
❌ Bad: explains what things are:
## About Our Testing Framework
We use Jest for unit testing and Playwright for E2E tests.
The test directory contains all test files organized by module.
Jest configuration is in jest.config.ts.
✅ Good: tells the agent what to do:
## Running Tests
1. Identify which modules are affected by the current changes
2. Run unit tests for affected modules:
```bash
npx jest --testPathPattern=""
```
3. If changes affect UI components, also run E2E tests:
```bash
npx playwright test --grep=""
```
4. If any tests fail, read the failure output and fix before proceeding
Monolithic skills that try to do everything#
A single skill that covers “all development workflows” will be enormous, expensive to load, and imprecise in its description matching. Split broad capabilities into focused skills.
Instead of one development-workflow skill (hundreds of lines), create separate focused skills: run-tests, create-migration, deploy-staging, code-review. Each one loads independently, keeping context costs low and activation precise.
First or second person in descriptions#
The description is injected into the system prompt, which is written in third person. Personal pronouns break the voice.
❌ Bad: breaks system prompt voice:
description: "I can help you create database migrations"
description: "You can use this to generate API documentation"
✅ Good: third person, declarative:
description: "Generates database migrations for Drizzle ORM with SQLite"
description: "Use when creating or updating API documentation for REST endpoints"
Critical instructions buried in the middle#
When the agent loads a skill but skips key steps, the problem is often structural rather than instructional. Agents process long documents with varying attention, and the middle of a 300-line SKILL.md is the most likely place for instructions to be missed.
Three common causes and their fixes:
Instructions too verbose. If the body reads like a technical manual, the agent may skim. Cut explanatory prose that doesn’t directly support a workflow step. Move reference material to references/ files.
Critical information buried. Put the most important constraints at the top of the body. Use ## Critical or ## Important headers for non-negotiable rules. If a constraint like “never drop columns without confirmation” appears halfway through a long file, elevate it to the top.
Ambiguous language. “Make sure to validate things properly” gives the agent no actionable guidance. “Run python scripts/validate.py --strict and fix all errors before proceeding” does.
For critical validations that the agent occasionally skips, move the check into a script. Code is deterministic; language interpretation isn’t. A 10-line validation script will outperform a paragraph of validation instructions every time.
External dependencies via git clone or network requests#
Skills should be self-contained. If the agent needs to clone a repository or download files before the skill can work, the skill is fragile and environment-dependent. Bundle what you need in the skill directory. Move large reference material to references/ where it loads on demand.
Command lists without context or verification#
A flat list of commands without conditional logic, error handling, or verification steps produces brittle workflows.
❌ Bad: no conditions, no verification, no error handling:
1. Run `npm run build`
2. Run `npm run test`
3. Run `npm run deploy`
✅ Good: conditions, verification, error handling:
1. Run the build: `npm run build`
- If the build fails, read the error output and fix before proceeding
2. Run the full test suite: `npm run test`
- If tests fail, fix the failures. Do not proceed to deployment with failing tests
3. Verify the build artifact exists: `ls dist/`
4. Run a smoke check: `node dist/index.js --health-check`
- If the health check fails, investigate the build output
5. Deploy to staging first: `npm run deploy -- --env staging`
6. Verify staging deployment: `curl https://staging.example.com/health`
7. Only after staging verification succeeds, deploy to production
Reflexive cross-platform portability#
Earlier versions of this guide (and most others) implicitly told authors to stick to spec-level frontmatter on every skill, treating vendor-specific fields as a last resort. That advice fits one scenario - skills published for arbitrary teams on arbitrary tools - and misfires on every other scenario.
If your team uses one platform and there is no realistic plan to migrate, refusing to use that platform’s frontmatter (disable-model-invocation, user-invocable, allowed-tools, model, agent, hooks) is the anti-pattern. You give up real ergonomic wins (manual invocation gates, tool restrictions, model selection) for a portability guarantee no one is collecting on.
Bad: a Claude Code-only release-checklist skill written with only spec fields, then a paragraph in the body asking the user “please remember to invoke this manually because we don’t have a way to enforce it.” The platform supports disable-model-invocation: true. Use it.
Good: write down the portability target at the top of the skill or in your skill catalog. “Claude Code only - uses disable-model-invocation” is a complete and legitimate scope statement. So is “Claude Code + VS Code Copilot intersection.” So is “spec-level portability.” Pick one consciously, stop apologising for it.
Trigger-style description on a user-invoked-only skill#
When disable-model-invocation: true is set, the description never enters the model’s context. “Use when…” trigger phrases become meaningless tokens that nobody reads.
Bad (description never reaches the model, but is written as if it must compete for activation):
description: >
Use when preparing a production release, cutting a release branch,
or running pre-deploy checks. Handles changelog generation, version
bumps, staging verification, and rollback prep.
disable-model-invocation: true
Good (description is a label for the human who runs /release-checklist):
description: Walks the user through the production release checklist for the payments service.
disable-model-invocation: true
The bad version is not catastrophic - the description is simply unread - but it signals that the author copied trigger-style phrasing without thinking about who actually reads the field. New skills written with the same template inherit the confusion.
Decorative formatting that targets human readers#
This guide is formatted for humans because it is read by humans. Hard-wrapped paragraphs, “Note:” blockquotes, three-deep bullet ladders, and emoji-prefixed headers are appropriate for the blog post you are reading right now. They are not appropriate for the SKILL.md files this post teaches you to write.
When skill authors copy this article’s prose conventions verbatim into their skills, they are making the model pay for visual scaffolding the model cannot use. The full treatment is in Format SKILL.md for the model, not for humans . The short version: drop hard wraps, drop blockquotes, drop nested bullet ladders, drop decorative dividers; keep numbered steps, imperative form, tables for parallel data, code fences for anything mechanical.
Troubleshooting#
When a skill doesn’t behave as expected, resist the urge to rewrite it from scratch. Most problems fall into a handful of categories, and each has a targeted fix. Start by identifying which symptom matches your situation.
Skill doesn’t activate#
Symptoms: The agent ignores the skill entirely. You have to mention the skill by name or manually enable it for the agent to load it.
Diagnose: Ask the agent: “When would you use the [skill-name] skill?” If the agent can’t describe the skill’s purpose accurately, the description isn’t registering.
Common causes:
- Description is too vague. “Helps with projects” matches nothing. Add specific trigger phrases that real users would type.
- Missing synonyms. Engineers phrase the same request differently. If the skill handles PDF creation, mention “PDF”, “.pdf files”, “document generation”, and “portable document format”.
- Keyword mismatch. The description uses abstract terms (“data processing”) while users type concrete ones (“parse CSV”, “BigQuery query”). Match the vocabulary your users use.
Fix: Rewrite the description with specific trigger phrases and test with 10-20 varied queries. See The description field for the full treatment.
Skill activates too broadly#
Symptoms: The skill loads for unrelated requests. Users disable it or work around it.
Diagnose: Track which unrelated queries trigger the skill. Look for overly broad keywords in the description.
Common causes:
- Description uses general terms that match many domains (“processes data”, “creates documents”).
- No negative triggers to exclude adjacent use cases.
Fix: Narrow the scope with specific qualifiers and add explicit exclusions: “Advanced statistical analysis for CSV files. Do NOT use for basic data exploration or simple formatting.” See Controlling over-triggering with negative triggers in the description section.
Skill loads but the agent skips steps#
Symptoms: The skill activates correctly, but the agent performs a shallow version of the workflow instead of following the detailed instructions.
Diagnose: Check whether the description summarizes the procedure. If the description already explains what to do, the agent may conclude it has enough information and skip the skill body entirely.
Common causes:
- Description leakage: the description contains the workflow (“Analyzes git diff, identifies the change type, generates a commit message”). The agent treats the description as sufficient and doesn’t read the full instructions.
- Critical steps buried in a long body. Agents process long documents with varying attention. Constraints in the middle of a 300-line file get missed.
- Instructions too verbose. When the body reads like a technical manual, the agent skims for the actionable parts.
Fix: Rewrite the description to communicate when to activate and what capabilities exist, not how the workflow proceeds. Elevate critical constraints to the top of the body. Cut explanatory prose that doesn’t directly support a workflow step. See the Workflow summary in the description and Critical instructions buried in the middle anti-patterns.
Inconsistent results across runs#
Symptoms: The same request produces different workflows or outputs in different sessions. Sometimes the skill works correctly, sometimes it doesn’t.
Diagnose: Run the same request 3-5 times and compare outputs side by side. Find the step where results diverge.
Common causes:
- Ambiguous language at a decision point. “Process the file appropriately” means different things in different contexts. The agent interprets the ambiguity differently each time.
- Multiple valid approaches without a clear default. “You can use either pdfplumber or PyMuPDF” forces the agent to choose, and it chooses differently across sessions.
- Missing examples. Without input/output pairs showing the expected behavior, the agent falls back on its general training, which varies.
Fix: Tighten the language at the inconsistent step. Replace “process the file” with “run python scripts/process.py input.pdf”. Declare one default approach per task. Add multishot examples that demonstrate the expected output format.
For critical steps where consistency is non-negotiable, move the logic to a script. Code execution is deterministic; language interpretation isn’t. A python scripts/validate.py call produces the same result every time. A paragraph of validation instructions can drift in interpretation across sessions.
Performance degradation with many skills#
Symptoms: Agent responses feel slower. Quality degrades even for tasks unrelated to any specific skill. Context seems crowded.
Common causes:
- Too many skills enabled simultaneously. Each skill’s description costs ~100 tokens at startup. With 50+ skills, that’s 5,000+ tokens of description metadata before the conversation starts.
- Large skill bodies loading unnecessarily. A skill that triggers broadly loads its full body often, consuming context that other tasks need.
- All content inlined instead of using progressive disclosure. A 2,000-line SKILL.md with everything in the body defeats the three-level loading model.
Fix: Audit which skills are actively used. Disable skills that activate fewer than once per week. Move reference material from the body to references/ files. Keep frequently-activated skill bodies under 200 words. See Token efficiency
for size guidelines.
A complete skill example#
Here is a production-quality skill that demonstrates the patterns discussed above. It handles creating new API endpoints in a Next.js application with specific team conventions.
create-api-endpoint/
SKILL.md
references/
api-conventions.md
error-codes.md
assets/
route-template.ts
test-template.ts
SKILL.md:
---
name: create-api-endpoint
description: >
Use when creating new API routes, REST endpoints, or server-side handlers
in Next.js App Router. Handles route creation, validation, error handling,
TypeScript types, and test scaffolding.
---
# Create API Endpoint
## Workflow
1. Confirm the path and HTTP methods with the user. Reject the request if the path collides with an existing route under `src/app/api/`.
2. Map the URL to a file: `/api/users` -> `src/app/api/users/route.ts`; `/api/users/[id]` -> `src/app/api/users/[id]/route.ts`.
3. Define types in `src/types/api/<resource>.ts`: request body (POST/PUT/PATCH), response body, query params (GET filters).
4. Copy `assets/route-template.ts` into the route file. Every handler validates with zod, returns `{ data, error, meta }`, sets status codes per `references/error-codes.md`, and wraps logic in try/catch with structured logging.
5. Copy `assets/test-template.ts`. Cover happy path per method, validation failure (malformed body), auth failure (missing/invalid token), not-found (when the path has parameters).
6. Verify: `npm run lint`, `npx tsc --noEmit`, `npm test -- --testPathPattern="api/"`. Fix all failures before reporting done.
## Status code lookup
| Status | Use for |
| ------ | ------------------------------------- |
| 400 | Validation failure (include field errors) |
| 401 | Missing authentication |
| 403 | Insufficient permissions |
| 404 | Missing resource |
| 500 | Unexpected error (log fully, return generic message) |
See `references/api-conventions.md` for envelope details and `references/error-codes.md` for the full taxonomy.
Token efficiency: why size matters#
Every token in your skill costs real money and real performance. The description is loaded for every session, even when the skill is never activated. The body is loaded on activation. Referenced files are loaded on access.
Practical guidelines based on the specification and real-world usage:
- Description: Under 1,024 characters. Aim for 2-4 sentences.
- Frequently-loaded skills: Keep the SKILL.md body under 200 words (~300 tokens). These are skills that activate in most sessions (like code style or commit message skills).
- Standard skills: Keep the body under 500 lines (~2,000-3,000 tokens). This covers most workflow skills.
- Complex skills: If the body exceeds 500 lines, split reference material into
references/files that load on demand.
The progressive disclosure model only works if you respect the size boundaries. A 2,000-line SKILL.md with everything inlined defeats the purpose - the agent pays the full token cost every time the skill activates, even if it only needs one section.
Getting started: your first skill in 10 minutes#
-
Pick a task you explain repeatedly. What procedure do you describe in Slack messages, PR comments, or onboarding docs more than once a month? That is your first skill.
-
Create the directory:
mkdir -p .github/skills/my-first-skill -
Write the SKILL.md:
--- name: my-first-skill description: > Use when [specific trigger condition]. Handles [specific capabilities]. --- # My First Skill ## Workflow 1. [First step with specific command or action] 2. [Second step] 3. [Verification step] ## Rules - [Important constraint] - [Edge case to handle] -
Test it. Start a new chat session and give the agent a task that matches your description. Watch whether the skill activates and whether the agent follows your procedure. If it does not activate, refine your description keywords. If it activates but skips steps, check whether your description accidentally summarizes the workflow.
-
Commit and share. Push the skill directory to version control. Your entire team gets it automatically.
Quick reference: specification constraints#
| Field | Portability | Requirement |
|---|---|---|
name |
Spec (all platforms) | 1-64 chars, lowercase, digits, hyphens only; must match directory name |
description |
Spec (all platforms) | 1-1,024 chars; loaded at startup unless disable-model-invocation: true |
license |
Spec (all platforms) | Optional; standard license name or LICENSE file reference |
compatibility |
Spec (all platforms) | Optional; environment requirements |
allowed-tools |
Spec (Experimental) | Optional; restricts agent tool access. Full on Claude Code, partial on Gemini, ignored on VS Code Copilot |
disable-model-invocation |
Claude Code, VS Code Copilot | Optional; default false. When true, skill is manual-only (/skill-name); description never loads into context |
user-invocable |
Claude Code, VS Code Copilot | Optional; default true. When false, only the model can invoke; user cannot pick from / menu |
model, agent, hooks, argument-hint, effort, context, paths, shell, arguments |
Claude Code only | Optional; consult Claude Code skills reference for current semantics |
tags |
Convention (tooling only) | Optional; for catalogs/tooling only; agents ignore |
triggers |
Convention (tooling only) | Optional; for catalogs/tooling only; agents ignore |
metadata |
Convention / Spec (tooling only) | Optional; key-value map for tooling; agents ignore |
SKILL.md body |
Spec (all platforms) | No format restrictions; recommended under 500 lines / 5,000 tokens |
| File references | Spec (all platforms) | Relative paths from skill root; keep one level deep |
| Directory structure | Spec (all platforms) | SKILL.md (required), scripts/, references/, assets/ (optional) |
Codex enforces manual-only invocation through a separate agents/openai.yaml file (policy.allow_implicit_invocation: false), not through SKILL.md frontmatter; if you target Codex alongside Claude Code, you set both.
Platform-specific notes#
VS Code / GitHub Copilot: Skills are generally available as of January 2026. Use /skills or Chat: Configure Skills command to manage. The diagnostics view shows loaded skills and errors. Extension authors can contribute skills via the chatSkills contribution point in package.json.
Claude Code: Skills auto-discover from .claude/skills/ (project) and ~/.claude/skills/ (personal). Supports the full vendor frontmatter surface beyond the open spec: disable-model-invocation, user-invocable, allowed-tools, plus model, agent, hooks, paths, argument-hint, and others - see the Claude Code skills reference
for the current table. Use claude --debug for troubleshooting. Skills are also available in Claude.ai for paid plans.
Cursor: Skills load from .cursor/skills/ at the project level. Enable or disable Agent Skills in Cursor Settings > Rules > Import Settings. Install community skills with npx skills add or create/copy them by hand.
Gemini CLI: Skills require explicit activation with user consent. The /skills command manages enable/disable state. Built-in skill-creator skill helps generate new skills. Once activated, skills remain active for the full session.
OpenAI Codex: Skills load from .agents/skills/ (project, scanning up to repo root) and ~/.agents/skills/ (personal). Codex uses .agents/ not .codex/ for skill storage. Invoke skills explicitly with $skill-name syntax. Built-in $skill-creator and $skill-installer skills help with authoring and installation. Configure enabled/disabled state in ~/.codex/config.toml. When duplicate skill names exist across locations, both appear in selectors (not merged). Supports symlinked skill folders.
Vendor-specific feature support matrix#
| Feature | Claude Code | VS Code Copilot | Cursor | Gemini CLI | Codex CLI |
|---|---|---|---|---|---|
allowed-tools |
Full | Not documented | N/A (uses .mdc rules) |
Partial | Not in SKILL.md |
disable-model-invocation |
Full | Full | N/A | No | Via agents/openai.yaml instead |
user-invocable |
Full | Full | N/A | No | No direct equivalent |
Other Claude Code fields (model, agent, hooks, paths) |
Full | No | N/A | No | No |
| Progressive disclosure | Full | Full | N/A | Full | Full |
| Skill validation tooling | Full | Full | N/A | Full | Partial |
“Partial” means the feature is recognized but behavior may differ from the specification. “N/A” for Cursor reflects that Cursor uses .mdc rules with different frontmatter (description, globs, alwaysApply) rather than the SKILL.md format.
References#
- Agent Skills specification
- GitHub Copilot Agent Skills
- Cursor Skills documentation
- Claude Code Skills documentation
- Claude Agent Skills (platform)
- Anthropic reference skills
- OpenAI documentation on skills
- OpenAI reference skills
- VS Code skills documentation
- Gemini CLI skills
- Skills CLI for installation
- Community skills

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.