SKILL.md frontmatter reference: every field, and which ones upload
Updated
The problem
You are writing a SKILL.md and want to know which frontmatter fields exist, which are required, and what values they take. Or you uploaded a skill to claude.ai or the Claude API and got:
Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name
The short answer
There are two sets of fields. The Claude API and claude.ai accept six keys — name, description, license, compatibility, metadata and allowed-tools — and reject an upload with any other key. Claude Code reads those six plus about a dozen more, and silently ignores any key it does not recognize. A skill that uses Claude Code-only keys works in Claude Code and fails to upload anywhere else.
Why it happens
The fields below are from the Claude Code skills docs and the Agent Skills overview. I have not run every field in every product myself.
Where the frontmatter must be
The frontmatter is read only if the opening --- is the very first line of the file. Anything before it — a blank line, a title — and the whole file is treated as content, with no name or description.
If the YAML does not parse, Claude Code still loads the skill, but with no fields set. You can still run it as /skill-name, but Claude cannot match it from its description. The usual cause is an unquoted colon inside the description. Run claude --debug to see the parse error.
The six portable fields
These work everywhere: Claude Code, claude.ai uploads, the Skills API and package_skill.py.
name— required outside Claude Code. At most 64 characters; lowercase letters, numbers and hyphens only; no XML tags; must not contain "anthropic" or "claude". In Claude Code it defaults to the directory name and becomes the slash command.description— required outside Claude Code. Non-empty, at most 1,024 characters, no XML tags. Say what the skill does and when to use it.license— accepted. Claude Code does not act on it.compatibility— at most 500 characters. Claude Code does not act on it.metadata— a map of extra keys. In Claude Code, a value that is not a map is dropped.allowed-tools— tools Claude can use without asking permission during the turn that invokes the skill.
Fields only Claude Code reads
when_to_use— extra trigger phrases or example requests, appended to the description in the skill listing.argument-hint— hint shown during autocomplete for the arguments, such as[issue-number].arguments— named positional arguments, substituted as$namein the skill content.disable-model-invocation—truetakes the description out of Claude's context, so Claude never runs the skill on its own; you run it with/skill-name. Defaultfalse.user-invocable—falsehides the skill from the/menu, so only Claude can invoke it. Defaulttrue.disallowed-tools— tools removed from Claude's pool while the skill is active.model— the model to use for the rest of the turn that invokes the skill.effort—low,medium,high,xhighormax.context—forkruns the skill in a forked subagent context.agent— which subagent type to use whencontext: forkis set.background— only applies withcontext: fork. A forked skill runs in the background by default;falsewaits for its result in the same turn (v2.1.218+).hooks— hooks registered when the skill is invoked; they keep running for the rest of the session.paths— glob patterns. When set, Claude loads the skill automatically only when working with matching files.shell— the shell for the skill's inline commands:bash(default) orpowershell.
Booleans also accept yes/no, on/off and 1/0 (v2.1.218+). In Claude Code every field is optional, and if description is missing it uses the first non-empty line of the body.
Limits that are not fields
- Claude Code cuts
descriptionpluswhen_to_useat 1,536 characters in the skill listing. Text past that point is never seen when Claude decides whether to use the skill. - The docs recommend writing the description in the third person ("Extracts tables from PDFs…", not "I can help…" or "You can use…") and keeping the body of
SKILL.mdunder 500 lines.
Why an unknown key does not error in Claude Code
Field names must match exactly, and Claude Code ignores a field it does not recognize without reporting anything. A typo such as disable-model-invokation therefore does nothing, silently. The upload path for claude.ai and the API is strict instead: any key outside the six is a hard error.
Check it with the tool
Paste your SKILL.md into Kanon (beta), the free SKILL.md linter on this site, and choose the target you will ship to. It runs in your browser and sends nothing anywhere.
- API & claude.ai target: Kanon reports
unknown-keysas an error for any key outside the six — the same rejection as the upload. If every extra key is a Claude Code key, it tells you to switch the target instead. - Claude Code target: Kanon warns
unknown-keyfor each key Claude Code would ignore, and names the real field when the key differs from one only in case,-or_(such aswhen-to-useforwhen_to_use). It does not catch other misspellings, so check the key names in the list above. Rules that only the API enforces, such as reserved words inname, become portability warnings. - Either target:
fm-first-lineandyaml-parsecatch frontmatter that would be read as no fields at all, andeffort-enum,context-enum,shell-enum,boolandfork-onlycheck the values in the table above.
What to do next
- Decide where the skill will run. If it must also upload to claude.ai or the API, use only the six portable fields.
- Put
---on line 1 and quote any description that contains a colon. - Write the description in the third person, with what the skill does and a "Use when…" sentence, inside 1,024 characters.
- If you need a Claude Code-only field, keep that skill in
.claude/skills/<name>/SKILL.mdor~/.claude/skills/<name>/SKILL.mdrather than uploading it. - Paste the file into Kanon with the matching target and fix every error before you ship it.