Agent Skills schemas
A skill is a directory with a SKILL.md in it: YAML front matter, then the
instructions an agent follows. The front matter is the part that has a contract,
and there are two of them.
| Id | Describes | Requires | Unknown keys |
|---|---|---|---|
agentskills:skill:1.0 |
the Agent Skills open standard | name, description |
rejected |
anthropic:claude-skill:2.1 |
what Claude Code 2.1 accepts | nothing | tolerated |
They are a pair, not alternatives. The second is a superset of the first, and the gap between them is a real failure you can hit:
docmeta validate .claude/skills/**/SKILL.md -s anthropic:claude-skill:2.1docmeta validate skills-to-publish/**/SKILL.md -s agentskills:skill:1.0Why one of them is strict
Section titled “Why one of them is strict”agentskills:skill:1.0 is the only built-in that sets
additionalProperties: false. Every other schema docmeta ships tolerates keys it
does not know, because the tools they model do.
The Agent Skills distribution path does not. Packaging with package_skill.py,
uploading a skill to claude.ai, and the Skills API all accept exactly six fields
and refuse the rest outright:
Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are:allowed-tools, compatibility, description, license, metadata, nameA permissive schema would report that file as clean. So the strictness is not an editorial opinion. It is the same rule the upload enforces, moved earlier.
agentskills:skill:1.0
Section titled “agentskills:skill:1.0”The whole standard. Six fields, two required.
| Field | Type | Notes |
|---|---|---|
name |
string | Required. 1–64 characters, a-z, 0-9 and - only, no leading, trailing or doubled hyphen. |
description |
string | Required. 1–1024 characters. What the skill does and when to use it. |
license |
string | A license name, or a pointer to a bundled license file. |
compatibility |
string | Up to 500 characters of environment requirements. Most skills need none. |
metadata |
object | A map of string keys to string values. |
allowed-tools |
string | Space-separated pre-approved tools. Experimental in the standard. |
Two rules deserve a note.
metadata takes strings, and only strings. The standard’s own example
quotes the value, as in version: "1.0". Unquoted, YAML reads that as a float,
and the map is defined as string to string:
metadata: author: example-org version: 1.0 # fails: must be stringname also has to match the directory. The standard requires the name
field to equal the name of the folder that holds the SKILL.md file. That is a
fact about the path rather than about the front matter, so no metadata check can
see it. docmeta validates the character rules and the length, and the directory
match is on you.
anthropic:claude-skill:2.1
Section titled “anthropic:claude-skill:2.1”Everything above, plus the fourteen fields Claude Code adds. It requires
nothing, because Claude Code marks every field optional. A SKILL.md with no
front matter at all still loads, and takes its description from the first
paragraph of the body.
| Field | Type | Notes |
|---|---|---|
name |
string | Display label in skill listings; the command still comes from the directory. In a plugin skill it sets the last command segment. |
description |
string | How Claude decides to load the skill. Falls back to the first body paragraph. |
when_to_use |
string | Trigger phrases appended to description in the listing. |
argument-hint |
string | Autocomplete hint, such as [issue-number]. |
arguments |
string | list | Named positional arguments for $name substitution. |
disable-model-invocation |
boolean | true leaves /name as the only way in. |
user-invocable |
boolean | false hides it from the / menu, leaving Claude as the only caller. |
allowed-tools |
string | list | Tools pre-approved for the invoking turn. |
disallowed-tools |
string | list | Tools removed from the pool while the skill is active. |
model |
string | Same values as /model, plus inherit. |
effort |
enum | low, medium, high, xhigh, max. |
context |
fork |
Runs the skill in its own subagent context. |
agent |
string | Which subagent type, with context: fork. |
background |
boolean | With context: fork, false waits for the result in the invoking turn. |
hooks |
object | Hooks registered for the rest of the session. |
paths |
string | list | Globs scoping automatic activation. |
shell |
enum | bash or powershell, for inline command injection. |
metadata |
object | Free-form, unlike the standard’s string map. |
license |
string | Accepted, not acted on. |
compatibility |
string | Accepted, not acted on. Up to 500 characters. |
Three fields are enumerated, and one deliberately is not
Section titled “Three fields are enumerated, and one deliberately is not”effort, context and shell have closed, published value sets, so a typo in
one is an error:
✗ .claude/skills/audit/SKILL.md /effort must be equal to one of the allowed values (line 4) [anthropic:claude-skill:2.1]model is not enumerated. Its accepted values move with the product, and an
organisation’s availableModels allowlist narrows them further. A list written
here would reject skills that work. This is the same rule the vocabulary
schemas follow for ms.topic: a set
assembled from examples fails correct documents.
name is not constrained either, and that one is the division of labour between
the two schemas. name: Fancy Review is a legal Claude Code display label and an
illegal Agent Skills name. Validating against the spec schema is how you find
out.
Booleans have six spellings, and YAML only understands two
Section titled “Booleans have six spellings, and YAML only understands two”Claude Code accepts yes, no, on, off, 1 and 0 in any letter case, as
well as true and false. A YAML 1.2 parser, which is what reads your front
matter, resolves only true and false to booleans. Everything else arrives as
a string or an integer:
disable-model-invocation: yes # the string "yes"background: 0 # the integer 0user-invocable: TRUE # the boolean trueAll three work in Claude Code, so the schema accepts all three types rather than
boolean alone. A pattern constrains only the string form, a 0–1 range
constrains only the integer one, and booleans are left as they are. A word that is
not one of the spellings still fails:
✗ .claude/skills/audit/SKILL.md /disable-model-invocation must match pattern "^(?:[Tt][Rr][Uu][Ee]|…)$" (line 4) [anthropic:claude-skill:2.1]Validate both at once
Section titled “Validate both at once”Nothing stops you stacking them, and for a skill that has to work locally and publish, that is the right run. Each failure is attributed to the schema that raised it:
docmeta validate skills/**/SKILL.md \ -s anthropic:claude-skill:2.1 \ -s agentskills:skill:1.0The two agree on every field they share, so a spec-conformant SKILL.md passes
both. What the pair reports is the Claude Code-only keys, which is the list of
things to remove before you package.
The other file in .claude/
Section titled “The other file in .claude/”A subagent definition is a different contract, not a third skill schema. It
lives under .claude/agents/, and it requires name and description. It
spells its tool grants tools and disallowedTools rather than allowed-tools
and disallowed-tools. Validating one against a SKILL.md schema passes it
while every key it set does nothing. See Claude Code subagent
schema.