Skills over MCP: 21 checks, run on Hugging Face's 25 skills
· 8 min read
Tools give an agent operations. A skill tells it how to combine them: a SKILL.md with instructions, plus the references and scripts it points to. Since the Skills extension (SEP-2640) became final in September, an MCP server can serve skills next to its tools. mcpscore 1.27.0 checks them with 21 rules, and we ran them on the 25 skills Hugging Face serves.
What a skill looks like on the wire
A server that serves skills declares io.modelcontextprotocol/skills in its capabilities. Each file of a skill is an MCP resource, under a skill:// URI. skills/list returns one entry per skill: the URI of its SKILL.md, the frontmatter of that file as JSON, and the complete list of the skill’s files with a SHA-256 digest and a size for each. This is a real entry from Hugging Face:
# One entry of Hugging Face's skills/list result
{
"uri": "skill://hf-mem/SKILL.md",
"frontmatter": {
"name": "hf-mem",
"description": "Hugging Face CLI to estimate the required memory to load Safetensors or GGUF model weights for inference from the Hugging Face Hub"
},
"resources": [
{
"uri": "skill://hf-mem/SKILL.md",
"digest": "sha256:ee99f9d97e084aa61a80eaec4e6de2265341e71256d918d48fb87a2ac01ce99f",
"size": 3325
}
]
}skills/get returns the same entry for one skill by URI, and a server that sets directoryRead: true also answers resources/directory/read. The skill format itself, with its naming rules and frontmatter fields, comes from the Agent Skills specification.
The extension is optional, so the rules are too. A server that does not declare it skips all 21 rules as not applicable, and its score does not change. A server that declares it has opted in to its requirements, and the rules count in its score like any others.
The 21 checks
The severity follows the wording of the spec. A requirement the extension states with MUST is MEDIUM, or HIGH when breaking it makes a host refuse the skill or puts hidden instructions in front of the model. A SHOULD or a recommendation is LOW. HIGH is worth three points, MEDIUM two and LOW one.
| What is checked | Rules | Severity | Basis |
|---|---|---|---|
| skills/list and skills/get answer, and the resources capability is declared | skills_list_supportedskills_get_supportedskills_resources_capability | MEDIUM | Listing skills, Getting a skill |
| skills/get refuses a skill the server does not serve with -32602 | skills_get_unknown_rejected | MEDIUM | Getting a skill |
| With directoryRead, a skill root lists its children and unknown paths are refused | skills_directory_read | MEDIUM | Reading directories |
| Entries are well formed, file lists are complete, the URI ends in the name, the name is valid | skills_entries_well_formedskills_resources_manifestskills_name_matches_uriskills_name_valid | MEDIUM | Agent Skills name field |
| Files match their digest and size; SKILL.md frontmatter matches the listing | skills_digest_integrityskills_frontmatter_matches | HIGH | Integrity and verification |
| Skill text has no hidden Unicode, credentials or prompt-injection phrasing | skills_hidden_unicodeskills_no_embedded_secretsskills_prompt_injection_phrasing | HIGH | Security considerations |
| Relative links in SKILL.md point at listed files | skills_references_resolve | LOW | Integrity and verification |
| Each skill stays within 512 files and 16 MiB | skills_within_limits | LOW | Implementation requirements |
| SKILL.md is text/markdown with the frontmatter name and description | skills_skill_file_metadata | LOW | SEP-2640 Resource Metadata |
| allowed-tools, when present, is a space-separated string | skills_allowed_tools_valid | LOW | Agent Skills allowed-tools |
| Recommendations: each skill fits OpenAI import limits; SKILL.md is under 500 lines | skills_openai_import_limitsskills_skill_file_length | LOW | OpenAI import, progressive disclosure |
| A stateless skills/list carries ttlMs and cacheScope | readiness_2026_skills_list_cache_hints | HIGH | Listing skills |
The last row belongs to the readiness checks for MCP 2026-07-28: on the new stateless lifecycle, every list result carries caching hints, and a skills listing is no exception. The rules reference has every rule with its citation.
Why a digest mismatch is HIGH
The integrity requirements are written for hosts: a host MUST check each file it reads against the size and digest in the listing, and MUST compare the frontmatter of the SKILL.md it fetched with the frontmatter in the listing, field by field. Content that fails either check MUST NOT be used. A server that serves a stale file, or edits SKILL.md without updating its listing, ships a skill that no conforming host will load.
mcpscore reads every listed SKILL.md and one supporting file per skill, within fixed caps, and checks them the way a host would. The spec is clear that a matching digest proves consistency, not trust: the same server supplies both. That is what the rule checks, and all it claims.
Skill text is a prompt
A skill is text that goes straight into a model’s context, and the extension’s security section tells hosts to treat it as untrusted input. The three checks we run on tool descriptions for the OWASP MCP Top 10 run on skills too, as separate HIGH rules in Security & Auth: hidden Unicode, embedded credentials, and prompt-injection phrasing. They read the frontmatter and every line of each SKILL.md.
Skills are full of examples, and examples of prompts look like prompts. One of Hugging Face’s skills shows a reranker’s prompt template, chat tokens included, inside a JSON code block. A chat-template token inside code counts as an example and is not flagged; every other kind of finding counts wherever it appears.
Hugging Face's 25 skills
Hugging Face’s MCP server serves 25 skills, from a single 3 KB SKILL.md to a 31-file training guide, and sets directoryRead: true. It passes 18 of the 19 skills rules that apply to it, and the readiness check for its stateless listing. The 41 files mcpscore read match their digests and sizes, all 25 frontmatters match their listings, and all 66 relative links in the SKILL.md files point at files the skills list.
uvx mcpscore==1.27.0 'https://huggingface.co/mcp'
...
✅ skills/list answered with 25 skill(s).
✅ resources/directory/read lists a skill's root and refuses unknown and non-directory URIs.
✅ skills/get returned the entry of every listed skill asked for (25).
✅ skills/get refused a skill the server does not serve with -32602.
...
✅ 41 skill file(s) match their listed digest and size.
✅ 25 served SKILL.md file(s) carry the frontmatter their listing shows.
✅ All 66 relative link(s) in SKILL.md files point at listed skill files.
✅ Every skill is within 512 resources and 16 MiB.
✅ Every skill fits OpenAI's import limits.
✅ SKILL.md resources are text/markdown and carry the frontmatter's name and description.
❌ Number of SKILL.md files of 500 lines or more: 4. This is an optional quality recommendation.
⏭️ Skipping rule 'skills_allowed_tools_valid': not-applicable
...
✅ skills/list on the stateless lifecycle carries valid ttlMs and cacheScope.
...
Audit finished. Final score: 194/214The one finding is a recommendation. The Agent Skills specification advises keeping SKILL.md under 500 lines and moving detail into referenced files, which a host reads only when the model needs them. Four are over:
| Skill | SKILL.md lines |
|---|---|
huggingface-llm-trainer | 738 |
transformers-js | 692 |
huggingface-paper-publisher | 624 |
huggingface-vision-trainer | 593 |
The allowed-tools rule skips, because none of the 25 skills uses the field. The skills rules add 41 points to Hugging Face’s maximum, and it scores 40 of them, for 194/214 overall.
What mcpscore reports but does not score
Some properties of a skill are decisions for the host, and a score should not make them for it. The extension says hosts MUST ignore allowed-tools on a skill that arrives over MCP until the user approves that grant, and MUST NOT run code from a skill without per-skill approval (SEP-2640 §Security Implications). A server may still ship both, so mcpscore lists them in a skills section of the JSON report and on one line of CLI output:
Skills (not scored): 25 listed · 0 with allowed-tools · 9 with bundled scripts · OpenAI imports at most 5 skills and 8 MiB per scan, within per-skill limitsNine of Hugging Face’s skills bundle Python scripts. The same line shows the catalog against OpenAI’s import, which takes at most five skills and 8 MiB of archives per scan. A catalog of 25 does not fit in one import, and that is a fact to know, not a defect. The per-skill limits are a LOW recommendation instead, because a skill over them cannot be imported at all.
Servers on the 2026-07-28 lifecycle
A server that speaks only the stateless 2026-07-28 lifecycle has no initialize handshake to declare capabilities in. mcpscore asks server/discover, and when the server declares the extension, sends skills/list, skills/get and resources/read as stateless requests, each declaring the extension in its _meta. The same 20 skills rules apply. Hugging Face speaks both lifecycles, so mcpscore also asks it for one stateless listing to check the caching hints: ttlMs and a cacheScope of public, which it passes.
Where this fits next to MCP Inspector
MCP Inspector 2.6.0 and later verify skills from the command line: --method skills/list --verify checks conformance, digests and frontmatter per skill, with exit codes for CI. If conformance is all you need, it is the official tool and it is free.
mcpscore checks skills in the same audit as your tools, transport, auth and security, so one score, one badge and one CI step cover the whole server. It adds what conformance does not: the safety checks on skill text, the quality recommendations, and the facts a host will ask about. Our comparison of MCP testing tools goes through the rest.
Check your skills
The rules run on every audit in mcpscore 1.27.0, on the CLI and on mcpscore.dev, for servers over HTTP and stdio:
uvx mcpscore==1.27.0 'https://your-server.example/mcp'Add --json for the skills section. The methodology says what mcpscore reads and the caps it reads within, and the release notes list what changed.