Skip to content

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:

Terminal window
docmeta validate .claude/skills/**/SKILL.md -s anthropic:claude-skill:2.1
docmeta validate skills-to-publish/**/SKILL.md -s agentskills:skill:1.0

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, name

A 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.

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 string

name 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.

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 0
user-invocable: TRUE # the boolean true

All three work in Claude Code, so the schema accepts all three types rather than boolean alone. A pattern constrains only the string form, a 01 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]

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:

Terminal window
docmeta validate skills/**/SKILL.md \
-s anthropic:claude-skill:2.1 \
-s agentskills:skill:1.0

The 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.

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.