Build high-quality Agent Skills for any agent - opinionated best practices distilled from the Agent Skills spec, official Anthropic guidance, and production experience. Covers SKILL.md structure, frontmatter, description writing, single-file vs references/ layout, progressive disclosure, testing, patterns, troubleshooting, and distribution across all surfaces (Claude.ai, Claude Code, API, Agent SDK). Use when creating a skill, reviewing skill quality, debugging why a skill won't trigger, structuring skill directories, or writing skill descriptions.
---
name: skills-best-practices
description: Build high-quality Agent Skills for any agent - opinionated best practices distilled from the Agent Skills spec, official Anthropic guidance, and production experience. Covers SKILL.md structure, frontmatter, description writing, single-file vs references/ layout, progressive disclosure, testing, patterns, troubleshooting, and distribution across all surfaces (Claude.ai, Claude Code, API, Agent SDK). Use when creating a skill, reviewing skill quality, debugging why a skill won't trigger, structuring skill directories, or writing skill descriptions.
metadata:
version: "0.8.1"
categories: "agents, knowledge"
topics: "agent-skills, skill-authoring, prompt-design, spec, best-practices"
openclaw:
homepage: https://github.com/tenequm/skills/tree/main/skills/skills-best-practices
emoji: "📐"
---
# Skills Best Practices
Opinionated guide to building Agent Skills for any agent - distilled from the [Agent Skills open standard](https://agentskills.io), Anthropic's official guidance, and production experience, with deviations from the official line marked where they occur. Skills are folders (often just a single file) containing instructions, scripts, and resources that teach an agent how to handle specific tasks.
## Quick Start
A minimal skill is a directory with a `SKILL.md` file:
```
my-skill/
├── SKILL.md # Required - instructions with YAML frontmatter
├── references/ # Optional - detailed docs loaded on demand
├── scripts/ # Optional - executable code
└── assets/ # Optional - templates, fonts, icons
```
Minimal `SKILL.md`:
```yaml
---
name: my-skill-name
description: What it does. Use when [specific triggers].
---
# My Skill Name
[Instructions here]
```
Only `name` and `description` are required in frontmatter.
## Core Design Principles
### Single File vs. references/ (Most Important)
**Default to a single SKILL.md.** One file can be pasted to a person, gisted, embedded in a CLI binary, and printed by a `<tool> skill` subcommand - a directory cannot. Split into `references/` only when **both** hold:
1. **Conditional loading**: a meaningful chunk of content is needed by only a subset of invocations (e.g. a tracked-changes doc most DOCX tasks never touch). If every invocation reads everything anyway, splitting adds Read round-trips and costs shareability while saving nothing.
2. **Size pressure**: the body exceeds the recommended budget below.
**Distribution is a veto.** If the skill must travel as one file - shipped inside a CLI, printed by a command, shared by paste - stay single-file regardless of size and condense instead. Condensing means cutting redundancy, filler, and over-explanation while preserving every load-bearing instruction; losing substance to hit a line count is the failure mode, not the fix. See the single-file CLI-embedded pattern under [Patterns](#patterns).
**Size guidance** (opinionated thresholds drawn from experience, not enforced spec limits) - measure with `wc -c SKILL.md`. Chars track token cost closely (~4 chars per token); line counts are not a metric - identical content varies 2x in lines by formatting style:
| Tier | Chars | Beyond it |
|------|-------|-----------|
| Recommended | 25k | Condense carefully; split only if the conditional-loading test passes |
| Hard ceiling | 50k | Must condense or split |
> Official Anthropic guidance says to split at 500 lines. That advice assumes registry-installed skills with rarely-needed subtopics, and measures size in a unit that formatting distorts - this skill deliberately deviates on both.
When a skill does split, information loads in three levels:
| Level | When Loaded | Token Cost | Content |
|-------|------------|------------|---------|
| **1: Metadata** | Always (startup) | ~100 tokens | `name` + `description` from frontmatter |
| **2: Instructions** | When skill triggers | <5k tokens (recommended) | SKILL.md body |
| **3: Resources** | As needed | Effectively unlimited | Bundled files, scripts |
Reference detail files from SKILL.md so they load only when the task requires them:
```markdown
## Advanced features
- **Form filling**: See [FORMS.md](FORMS.md)
- **API reference**: See [reference.md](reference.md)
```
### Composability
Skills work alongside other skills. Don't assume yours is the only one loaded.
### Portability
Skills work across Claude.ai, Claude Code, API, and Agent SDK without modification (if dependencies are available).
## Writing the Description (Critical)
The description is the **single most important field** - it determines when your skill activates. Claude uses it to decide relevance from potentially 100+ available skills.
### Rules
- Write in **third person** ("Processes files..." - first or second person breaks discovery)
- Include **WHAT** it does + **WHEN** to use it
- Max 1024 characters, no XML angle brackets
- Be slightly "pushy" - Claude tends to **undertrigger** rather than overtrigger
- Include specific trigger phrases users would naturally say, plus file types where relevant
- Write natural prose, not keyword dumps - matching is semantic, so a long "Triggers on X, Y, Z..." list adds little over a clear sentence
- If the skill depends on an MCP server, name it ("...via MCP. Requires Linear MCP server connected.")
### Good vs Bad
```yaml
# GOOD - specific, actionable, includes triggers
description: Extract text and tables from PDF files, fill forms, merge
documents. Use when working with PDF files or when the user mentions
PDFs, forms, or document extraction.
# BAD - too vague
description: Helps with documents.
# BAD - missing triggers
description: Creates sophisticated multi-page documentation systems.
```
### Negative Triggers
When a skill overtriggers, add boundaries directly in the description:
```yaml
description: Advanced data analysis for CSV files. Use for statistical
modeling, regression, clustering. Do NOT use for simple data
exploration (use data-viz skill instead).
```
### Manually-Invoked Skills
A skill with `disable-model-invocation: true` never auto-triggers - its description shows only in the `/` menu, so trigger phrases do nothing for it. Write a plain one-line summary and skip the trigger-tuning.
## Frontmatter Reference
### Required Fields
| Field | Rules |
|-------|-------|
| `name` | Kebab-case, max 64 chars, lowercase + numbers + hyphens only. No "claude" or "anthropic" |
| `description` | Non-empty, max 1024 chars, no XML tags. WHAT + WHEN |
The agentskills.io standard and the Claude API require both fields. Claude Code is more lenient: `name` falls back to the directory name, and `description` falls back to the first markdown paragraph. Write both anyway for portability.
The spec also defines optional `license`, `compatibility`, and `metadata` fields. `compatibility` is capped at 500 characters and states environment requirements (intended product, system packages, network access).
### Optional Fields (Claude Code)
| Field | Purpose |
|-------|---------|
| `argument-hint` | Autocomplete hint, e.g. `[issue-number]` |
| `when_to_use` | Extra trigger context, appended to `description` in the skill listing |
| `arguments` | Named positional arguments for `$name` substitution (space-separated string or list) |
| `disable-model-invocation` | `true` = only user can invoke (for deploy, commit) |
| `user-invocable` | `false` = hidden from `/` menu (background knowledge) |
| `allowed-tools` | Pre-approves tools (no permission prompt) for the current turn; space-separated, e.g. `Read Grep Glob`. In the spec allowlist but tagged **(Experimental)** |
| `disallowed-tools` | Removes tools from Claude's pool while the skill is active; clears on your next message |
| `model` | Override model for this skill; accepts `inherit`. Lasts the current turn only |
| `effort` | Override effort level: `low`, `medium`, `high`, `xhigh`, `max` |
| `context` | `fork` = run in isolated subagent |
| `agent` | Subagent type when `context: fork` (e.g. `Explore`, `Plan`) |
| `background` | `false` opts a forked skill out of background execution (v2.1.218+) |
| `shell` | `bash` (default) or `powershell` |
| `hooks` | Hooks scoped to this skill's lifecycle |
| `paths` | Glob patterns limiting when skill activates |
> **Publishing caveat:** every field above except `allowed-tools` is Claude Code-specific. They work in Claude Code at runtime, but the **official `agentskills validate` spec validator rejects them** - it allows only `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools`, with no relax flag. If your repo or CI runs that validator (most ClawHub-publishing repos do), a skill using these fields fails validation unless you strip them from the copy you validate/publish. The ClawHub registry itself tends to tolerate extra top-level fields on publish, but the reference validator in your pipeline will not. See [Validate Against the Spec](#validate-against-the-spec).
### Naming Conventions
The `name` (and its folder) must: be 1-64 chars; use only lowercase letters, numbers, and hyphens; not start or end with a hyphen; not contain consecutive hyphens (`--`); and match the parent directory name. Anthropic surfaces also reject the reserved words `claude` and `anthropic`.
Prefer **gerund form** for clarity:
- `processing-pdfs`, `analyzing-spreadsheets`, `managing-databases`
- Also acceptable: `pdf-processing`, `process-pdfs`
- Avoid: `helper`, `utils`, `tools`, `documents`
## Claude Code Specifics
Official docs cover most Claude Code skill behavior: the [skills docs](https://code.claude.com/docs/en/skills) (invocation control, argument substitution, discovery and priority, tool permissions, `skillOverrides`, context budget), the [commands reference](https://code.claude.com/docs/en/commands#all-commands) for the current bundled-skills roster (it churns every few releases - never hardcode it), and the [settings reference](https://code.claude.com/docs/en/settings#available-settings). Below is only what those docs miss or what bites in practice.
### Dynamic-Injection Footgun
Claude Code preprocesses SKILL.md at load: an exclamation mark immediately touching a backticked command executes that command before Claude sees the content ([dynamic context injection](https://code.claude.com/docs/en/skills#inject-dynamic-context)). The preprocessor is **not markdown-aware**:
- A literal example executes at load even inside a code fence or inline code span, and a failing placeholder command errors the whole skill at load
- The inline form fires only at line start or after whitespace; a prefix defuses it (`KEY=` before the `!` leaves it literal)
- A fence opened with `!` right after the backticks is the multi-line form and is equally live
- `references/` files are read with the Read tool and never preprocessed - the only safe home for live examples. In a SKILL.md, break the `!`-to-backtick adjacency instead (wrap the `!` in its own code span, as this section does)
- `"disableSkillShellExecution": true` in settings disables execution for user/project/plugin skills
### Undocumented Behavior
- `display-name`, `default-enabled`, and `fallback` frontmatter keys exist but are absent from the official frontmatter table
- Frontmatter keys parse case-insensitively - kebab-case, snake_case, and camelCase resolve to the same field; boolean fields also accept `yes`/`no`/`on`/`off`/`1`/`0` (v2.1.218+)
### Behavior That Bites
- `context: fork` skills run **in the background by default** since v2.1.218 (`background: false` opts out); backgrounded forks get a narrower tool set and their edits bypass checkpoints, so `/rewind` cannot undo them. `Explore`/`Plan` forks skip CLAUDE.md and git status; since v2.1.198 `Explore` inherits the session model
- Skills stack: `/skill-a /skill-b args` in one message loads up to six skills (v2.1.199+)
- `permissions.additionalDirectories` does **not** load skills from those directories - only the `--add-dir` flag and `/add-dir` command do
- The `allowed-tools` grant lasts the current turn - it clears when the user sends their next message, not when the skill "finishes"
- The `/command` name comes from the skill's **directory**; frontmatter `name` is only a display label (plugin skills excepted). Nested skills are invocable by qualified name, e.g. `/apps/web:deploy`
- Invoked skill content stays in context all session and is not re-read - write standing instructions, not one-time steps. After auto-compaction, each skill's most recent invocation is re-attached with its first 5,000 tokens from a shared 25,000-token budget filled most-recent-first; re-invoke to restore full content
- Skill descriptions load at startup within a listing budget of **1% of the context window**; least-used descriptions drop first (names always kept), each entry capped at 1,536 chars. Diagnose with `/doctor`; tune via `skillListingBudgetFraction`, `skillListingMaxDescChars`, or `SLASH_COMMAND_TOOL_CHAR_BUDGET`
## Structuring Instructions
### Be Concise
Claude is smart. Only add context it doesn't already have:
```markdown
# GOOD (~50 tokens)
## Extract PDF text
Use pdfplumber for text extraction:
```python
import pdfplumber
with pdfplumber.open("file.pdf") as pdf:
text = pdf.pages[0].extract_text()
```
# BAD (~150 tokens)
## Extract PDF text
PDF files are a common file format containing text and images.
To extract text, you need a library. There are many available...
```
### Avoid Too Many Options
Don't present multiple approaches unless necessary. Give one default with an escape hatch:
```markdown
# BAD: "Use pypdf, or pdfplumber, or PyMuPDF, or pdf2image..."
# GOOD: "Use pdfplumber for text extraction. For scanned PDFs needing
# OCR, use pdf2image with pytesseract instead."
```
### Set Degrees of Freedom
- **High freedom** (text guidelines): Multiple approaches valid, context-dependent
- **Medium freedom** (pseudocode/templates): Preferred pattern exists, some variation OK
- **Low freedom** (exact scripts): Operations are fragile, consistency critical
### Recommended SKILL.md Structure
```markdown
# Skill Name
## Quick start
[Minimal working example]
## Workflow Decision Tree
[Route to the right approach based on task type]
## Detailed Instructions
[Step-by-step for each workflow]
## Examples
[Concrete input/output pairs]
## Troubleshooting
[Common errors and fixes]
```
### Reference Files
Keep references **one level deep** from SKILL.md. "Depth" means the reference *chain* (a file linking to a file linking to a file), not filesystem nesting - a `references/` subdirectory is fine. In a chain, Claude may preview files with partial reads (`head`) and miss content.
```markdown
# BAD: Too deep
SKILL.md -> advanced.md -> details.md -> actual info
# GOOD: One level
SKILL.md -> advanced.md (contains the info directly)
SKILL.md -> reference.md (contains the info directly)
```
For reference files >100 lines, include a **table of contents** at the top. Watch file *size* too: a single reference of many hundreds of lines defeats progressive disclosure even at one level deep, because Claude loads the whole file for any subtopic. Split large references by subtopic so each task pulls only what it needs.
## Patterns
### Sequential Workflow
```markdown
## Step 1: Analyze input
Run: `python scripts/analyze.py input.pdf`
## Step 2: Validate
Run: `python scripts/validate.py fields.json`
Fix any errors before continuing.
## Step 3: Execute
Run: `python scripts/process.py input.pdf fields.json output.pdf`
```
### Conditional Workflow (Decision Tree)
```markdown
## Workflow Decision Tree
**Creating new content?** -> Follow "Creation workflow"
**Editing existing content?** -> Follow "Editing workflow"
**Reviewing content?** -> Follow "Review workflow"
```
### Feedback Loop
```markdown
1. Make edits
2. Validate: `python scripts/validate.py`
3. If validation fails -> fix issues -> go to step 2
4. Only proceed when validation passes
```
### Checklist Pattern (for complex tasks)
```markdown
Copy this checklist and track progress:
- [ ] Step 1: Analyze input
- [ ] Step 2: Create plan
- [ ] Step 3: Validate plan
- [ ] Step 4: Execute
- [ ] Step 5: Verify output
```
### Single-File Skill Embedded in a CLI
For skills documenting a CLI tool: keep SKILL.md as one file next to the CLI source, compile it into the binary (`go:embed`, Rust `include_str!`, or equivalent), and add a `<tool> skill` subcommand that prints it. The printed guide always matches the installed version, and one command fetches the whole doc - playwright-cli, browser-use (`browser-use skill show`), and agent-browser (`agent-browser skills get core`) all converge on this shape. Never split such a skill into references/; condense carefully instead.
### Working with MCP and Subagents
MCP provides tool access; skills provide the workflow knowledge for using those tools well. Reference MCP tools by qualified name (`BigQuery:bigquery_schema`, `GitHub:create_issue`). Skills are portable expertise; subagents are isolated execution - in Claude Code, `context: fork` frontmatter runs a skill inside a subagent.
### Developing Skills with Claude (A/B Loop)
Build skills with two Claude instances: **Claude A** helps design and refine (it knows the format and what agents need); **Claude B** is a fresh instance with the skill loaded, tested on real tasks. Notice what context you repeatedly supply during normal work, have A capture it as a skill, test with B, bring B's specific failures back to A ("it forgot to filter test accounts"), and repeat. Iterate on observed behavior, not assumptions. For output-style skills, input/output example pairs communicate the desired style better than any description.
## Scripts
When your skill includes executable code:
- **Solve, don't punt**: Handle errors explicitly instead of letting them fail
- **Justify constants**: No magic numbers - document why each value was chosen
- **Prefer execution over loading**: Scripts run without entering context; only output consumes tokens
- **Clarify intent**: "Run `analyze.py`" (execute) vs "See `analyze.py`" (read as reference)
- **List dependencies** in SKILL.md and verify availability
## Testing
### Build Evaluations First
Create evaluations **before** writing extensive instructions - this proves the skill solves a real problem. Run Claude on representative tasks *without* the skill and document the failures; build ~3 scenarios that test those gaps; measure a baseline; then write the minimum instructions needed to pass. Iterate against the baseline.
### Triggering Tests
```
Should trigger:
- "Help me set up a new project in [Service]"
- "I need to create a project" (paraphrased)
Should NOT trigger:
- "What's the weather?" (unrelated)
- "Write Python code" (too generic)
```
### Functional Tests
Test normal operations, edge cases, and out-of-scope requests. Run the same request 3-5 times to check consistency.
### Debug Triggering
Ask Claude: "When would you use the [skill-name] skill?" - it quotes the description back. Adjust based on what's missing.
### Validate Against the Spec
Run the official Agent Skills validator before publishing:
```bash
uvx --from skills-ref agentskills validate path/to/skill
```
Exit 0 means valid. It checks `SKILL.md` format and enforces the spec's strict frontmatter allowlist (`name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools`). Most registries (e.g. ClawHub) and CI gates run this, so validating locally catches failures early. If you rely on Claude Code-only frontmatter (see the publishing caveat under [Frontmatter Reference](#frontmatter-reference)), strip those fields from the copy you validate.
## Pre-Publish Checklist
Calibrate to scope: for a project-local or single-user skill, skip the triggering-accuracy and distribution-hygiene items.
- [ ] Folder and `name` kebab-case and matching; file is exactly `SKILL.md`
- [ ] Description: third person, WHAT + WHEN, specific triggers, under 1024 chars, no angle brackets
- [ ] Single file unless conditionally-loaded content justifies references/; within size budget (`wc -c`)
- [ ] Critical instructions at the top; working examples, not pseudocode; consistent terminology
- [ ] If split: references linked from SKILL.md, one level deep, TOC for files over 100 lines
- [ ] Scripts: explicit error handling, no unexplained constants, dependencies listed, execute-vs-read intent clear
- [ ] Triggering tested: fires on direct and paraphrased requests, silent on unrelated and similar-but-distinct ones
- [ ] Functional: normal and edge cases pass, output consistent across 3-5 runs, tested on more than one model
- [ ] No time-sensitive info, Windows-style paths, or deprecated APIs
- [ ] Spec validator exits 0 (command above)
- [ ] After upload: monitor under/over-triggering in real conversations, iterate the description, bump version on every change
## Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Skill never loads | Description too vague | Add specific triggers and key terms |
| Skill loads for wrong tasks | Description too broad | Add negative triggers, be more specific |
| Instructions not followed | Too verbose or buried | Put critical instructions at top, use headers |
| Slow/degraded responses | SKILL.md too large | Condense first; split to references/ only if content is conditionally loaded (see Single File vs. references/) |
| "Could not find SKILL.md" | Wrong filename | Must be exactly `SKILL.md` (case-sensitive) |
| "Invalid skill name" | Spaces or capitals | Use kebab-case: `my-skill-name` |
| Whole skill silently skipped at load | Description exceeds 1024 chars | Trim it - the loader rejects the file, not just the description |
| Frontmatter fails to parse | `Triggers:` (colon-space) or straight `"quotes"` inside an unquoted `description` value | Quote the whole value or remove the colon/quotes |
| A doc example runs a shell command | A `!` directly touching a backticked command executes on load, even inside a code fence | Move the example to `references/` or break the `!`-backtick adjacency (see [Dynamic-Injection Footgun](#dynamic-injection-footgun)) |
## Distribution
| Surface | How to Deploy |
|---------|--------------|
| Claude.ai | Settings > Features > Upload zip |
| Claude Code (personal) | `~/.claude/skills/<name>/SKILL.md` |
| Claude Code (project) | `.claude/skills/<name>/SKILL.md` |
| Claude Code (plugin) | `<plugin>/skills/<name>/SKILL.md` |
| API | Upload via the Skill Management API, use via the Messages API |
| Enterprise | Managed settings (org-wide) |
Skills don't sync across surfaces - deploy separately to each.
### Using Skills with the API
Custom skills are uploaded through the Skill Management API; `anthropic`-type skills are pre-built by Anthropic. Both are used identically - pass them in the Messages API `container` parameter, each as `{type, skill_id, version}` where `type` is `anthropic` or `custom`. Up to 8 Skills per request, 30 MB max upload (all files combined), and all files must share a common root directory. Requires the code execution tool and the beta headers `code-execution-2025-08-25` and `skills-2025-10-02` (plus `files-api-2025-04-14` for file upload/download).
**Network access differs by surface.** The API code execution environment has **no network access and no runtime package installation** - bundle dependencies or use pre-installed packages. On claude.ai, by contrast, Skills **can** install packages from npm and PyPI and pull from GitHub.
Also: a `pause_turn` stop reason signals a long-running Skill operation; reuse containers across turns via `container.id`; generated files come back via the Files API; changing the Skills list breaks prompt caching; Skills are not ZDR-eligible.
## Security
- Only use skills from **trusted sources**
- No XML angle brackets in frontmatter (injection risk)
- Audit all bundled scripts and resources before using third-party skills
- Be cautious of skills that fetch from external URLs
- Documenting the dynamic-injection syntax is itself a hazard - the loader executes examples at load, even inside code fences. See the [Dynamic-Injection Footgun](#dynamic-injection-footgun) before writing any
## Additional References
- [ClawHub publishing](references/clawhub-publishing.md) - source-mined moderation quirks: reason codes and fixes, LLM-review survival tactics, constraints absent from ClawHub's docs
## Official Resources
- [Agent Skills Spec](https://agentskills.io/specification)
- [Claude Code Skills Docs](https://code.claude.com/docs/en/skills)
- [API Skills Guide](https://platform.claude.com/docs/en/build-with-claude/skills-guide)
- [Best Practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices)
- [Anthropic Skills Repo](https://github.com/anthropics/skills)
- [Engineering Blog: Agent Skills](https://claude.com/blog/equipping-agents-for-the-real-world-with-agent-skills)
- [Complete Guide PDF](https://resources.anthropic.com/hubfs/The-Complete-Guide-to-Building-Skill-for-Claude.pdf)
don't have the plugin yet? install it then click "run inline in claude" again.
restructured original reference material into implexa's 6-component format with explicit intent, inputs, numbered procedure steps with decision points embedded, output contract defining success criteria, and outcome signals for user confirmation. added edge cases for token limits, yaml parsing, rate limits, auth, and network access. preserved all original guidance and author's perspective while improving actionability.
comprehensive reference for building Agent Skills that follow Anthropic's official guidelines. Skills are folders containing instructions, scripts, and resources that teach Claude how to handle specific tasks. They follow the Agent Skills open standard.
this skill teaches you to build, structure, test, and distribute Agent Skills that activate reliably and work across Claude.ai, Claude Code, API, and Agent SDK without modification. use it when you're creating a new skill from scratch, reviewing an existing skill for quality, debugging why a skill won't trigger, deciding how to organize skill files, or writing descriptions that make Claude recognize when to activate your skill. the skill covers progressive disclosure (token efficiency), composability with other skills, concrete patterns, testing strategies, and the exact validation rules that publishing platforms enforce.
no external connections, APIs, or auth required. this is a reference skill.
input: your skill concept (problem statement, target users, key tasks).
start here to confirm you're solving a real problem. ask yourself:
output: a one-sentence skill purpose (e.g., "extract and validate data from PDF forms").
if you can't write a narrow purpose, the skill is too broad. go back and narrow the scope.
input: skill purpose from step 1.
create a SKILL.md file with this minimal frontmatter:
---
name: my-skill-name
description: What it does. Use when [specific triggers].
---
rules for name:
claude, anthropic)processing-pdfs, analyzing-spreadsheets)rules for description:
<, >)output: valid frontmatter block with name and description fields.
decision point: if you're publishing to ClawHub or running agentskills validate, use only name, description, license, compatibility, and metadata in frontmatter. if you're deploying to Claude Code only, you can add Claude Code-specific fields like argument-hint, disable-model-invocation, user-invocable, allowed-tools, context, model, effort, hooks, or paths, but strip those fields before validation if your CI gate runs the spec validator.
edge case: if your description exceeds 1024 characters, the entire skill file may be silently rejected at load time. trim aggressively.
input: frontmatter from step 2.
structure the SKILL.md body with these sections in order (use headers):
keep the entire SKILL.md under 500 lines. if instructions are getting long, move detailed docs to separate files in a references/ subdirectory and link to them inline (e.g., "for advanced usage, see FORMS.md").
output: SKILL.md body with at least quick start and detailed instructions.
key writing rules:
python, bash, json)decision point: if your instructions are primarily conceptual (e.g., "how to think about this problem"), put them in the SKILL.md body. if they're repetitive checklists or reference material (e.g., "50 field mappings"), move them to references/ to avoid token bloat.
input: core instructions from step 3, any complex reference material.
optional folders (create only if you have content for them):
references/: detailed docs, guides, lookup tables, checklists. keep file depth to one level (a reference file links to inline content, not to another reference file)scripts/: executable code (python, bash, node) that Claude runs. clarify intent: "run script.py" (execute) vs "see script.py" (read as reference)assets/: templates, fonts, icons, sample dataoutput: organized skill directory with SKILL.md and optional supporting folders.
edge case: reference files >100 lines should have a table of contents at the top. watch overall file size: a single 500-line reference defeats progressive disclosure even at one level deep. split large references by subtopic so tasks pull only what they need.
input: core instructions from step 3.
create ~3 test scenarios that measure whether your skill solves the stated problem:
Test 1: Basic usage
Request: "[typical user request matching your description]"
Expected: [specific, measurable output]
Pass criteria: [what success looks like]
Test 2: Edge case
Request: "[boundary or error condition]"
Expected: [how skill should handle it]
Pass criteria: [expected handling]
Test 3: Out-of-scope rejection
Request: "[task your skill doesn't handle]"
Expected: Claude declines or routes elsewhere
Pass criteria: [appropriate refusal or delegation]
also document triggering tests:
output: test scenarios in a markdown file or inline in SKILL.md as ## Testing section.
run each test 3-5 times against the skill to check consistency. to debug why a skill isn't triggering, ask Claude: "when would you use the [skill-name] skill?" it will quote your description back, revealing what's missing or unclear.
input: final SKILL.md with frontmatter and body.
run the official validator locally before publishing:
uvx --from skills-ref agentskills validate path/to/skill
exit code 0 means valid. the validator checks:
SKILL.md file exists and is valid YAML/Markdownname and description are present and non-emptyname follows kebab-case rulesdescription is max 1024 charactersname, description, license, compatibility, metadata, allowed-tools)output: validator confirms "SKILL.md is valid" or lists specific errors.
decision point: if you're using Claude Code-specific frontmatter fields (like context: fork or argument-hint), those will fail spec validation. if your repo runs agentskills validate in CI, strip those fields from the copy you validate, or use a local validate without pushing. the ClawHub registry itself tolerates extra fields on publish, but the reference spec validator in most CI pipelines does not.
edge case: if your description has a colon followed by a space (e.g., "Use when: task") or straight quotes inside an unquoted value, YAML parsing fails silently and the entire skill may be rejected. always quote the description value or avoid colons in the value itself.
input: validated skill directory from step 6.
deployment differs by platform:
~/.claude/skills/<name>/SKILL.md.claude/skills/<name>/SKILL.md in your project root<plugin>/skills/<name>/SKILL.md inside your plugin repocontainer parameter (up to 8 skills per request, 30 MB max combined, requires code execution tool and beta headers code-execution-2025-08-25, skills-2025-10-02, and optionally files-api-2025-04-14)output: skill deployed and accessible on the target surface.
decision point: if you're using the API, note that the code execution environment has no network access and no runtime package installation. bundle all dependencies or use pre-installed packages only. on claude.ai and Claude Code, skills can install npm and PyPI packages and pull from GitHub. if you use external URLs in your skill (e.g., to fetch data), they work on claude.ai and Claude Code but not the API.
should i put something in SKILL.md or in a reference file?
use SKILL.md for core instructions and quick start. move to references/ if: it's >100 lines, it's optional detail, or it's a lookup table. keep SKILL.md under 500 lines to maintain token efficiency.
how do i know if my description is too vague? ask Claude: "when would you use the [skill-name] skill?" if it quotes back a generic response, rewrite the description to include specific triggers. if it cites unrelated tasks, narrow the scope. test with both triggering and non-triggering requests.
my skill does multiple related things. should i create one skill or many? create one skill if the tasks share a single narrative trigger ("when working with PDFs"). create multiple if they're accessed independently (e.g., a "create project" skill and a "delete project" skill are separate concerns). if one skill solves the problem, prefer one.
should my skill use Claude Code-specific frontmatter fields?
use them if deploying to Claude Code only. strip them before validation if your CI runs agentskills validate. the reference validator rejects unknown fields unless you modify your CI to pass a flag. if you're publishing to ClawHub or a registry, check their policy: most allow extra fields, but some don't.
what if my skill needs external authentication (API keys, OAuth)?
document required env vars or credentials in the compatibility field or in a setup section of SKILL.md. note that API-deployed skills cannot make network requests, so auth only works on claude.ai and Claude Code. if your skill requires network access, state it clearly in the description so Claude knows not to use it in a restricted environment.
how do i handle rate limits or network timeouts? in your scripts, add explicit retry logic with exponential backoff. document retry behavior in SKILL.md. if the API fails, provide a human-readable error message and suggest next steps (e.g., "try again in 60 seconds" or "check your API key"). never silently fail.
my skill triggers too often or too rarely. what do i change?
edit the description field: add specific trigger phrases if undertriggering, or add negative triggers ("not for general Python questions") if overtriggering. test each change with 3-5 sample requests. the description is the only field Claude uses to decide relevance.
should i bundle dependencies or assume they're pre-installed? if deploying to the API, bundle or assume pre-installed only (no install at runtime). if deploying to claude.ai or Claude Code, you can install packages at runtime. document dependencies clearly in SKILL.md.
what counts as a security risk?
audit all bundled scripts before publishing. avoid skills that fetch from untrusted external URLs. watch for XML angle brackets in frontmatter (injection risk). if documenting dynamic shell commands (with a ! prefix), keep the example in a references/ file so Claude reads it with the Read tool and doesn't auto-execute it on load. never use em-dashes or other special characters that confuse YAML parsing.
success looks like:
valid SKILL.md file with:
name and description fields)references/)optional supporting files:
references/: flat directory (one level deep) with detailed docs, checklists, or lookup tablesscripts/: executable code files with clear intent commentsassets/: templates, icons, or sample datavalidated by agentskills validate (if publishing to a registry):
test results:
deployment confirmation:
you'll know the skill worked if:
agentskills validate exits 0 and the skill is accepted by publishing pipelines