Blog

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.

The 21 skills rules in mcpscore 1.27.0, their severity and the section each one cites
What is checkedRulesSeverityBasis
skills/list and skills/get answer, and the resources capability is declaredskills_list_supportedskills_get_supportedskills_resources_capabilityMEDIUMListing skills, Getting a skill
skills/get refuses a skill the server does not serve with -32602skills_get_unknown_rejectedMEDIUMGetting a skill
With directoryRead, a skill root lists its children and unknown paths are refusedskills_directory_readMEDIUMReading directories
Entries are well formed, file lists are complete, the URI ends in the name, the name is validskills_entries_well_formedskills_resources_manifestskills_name_matches_uriskills_name_validMEDIUMAgent Skills name field
Files match their digest and size; SKILL.md frontmatter matches the listingskills_digest_integrityskills_frontmatter_matchesHIGHIntegrity and verification
Skill text has no hidden Unicode, credentials or prompt-injection phrasingskills_hidden_unicodeskills_no_embedded_secretsskills_prompt_injection_phrasingHIGHSecurity considerations
Relative links in SKILL.md point at listed filesskills_references_resolveLOWIntegrity and verification
Each skill stays within 512 files and 16 MiBskills_within_limitsLOWImplementation requirements
SKILL.md is text/markdown with the frontmatter name and descriptionskills_skill_file_metadataLOWSEP-2640 Resource Metadata
allowed-tools, when present, is a space-separated stringskills_allowed_tools_validLOWAgent Skills allowed-tools
Recommendations: each skill fits OpenAI import limits; SKILL.md is under 500 linesskills_openai_import_limitsskills_skill_file_lengthLOWOpenAI import, progressive disclosure
A stateless skills/list carries ttlMs and cacheScopereadiness_2026_skills_list_cache_hintsHIGHListing 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/214

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

Hugging Face skills whose SKILL.md is 500 lines or more
SkillSKILL.md lines
huggingface-llm-trainer738
transformers-js692
huggingface-paper-publisher624
huggingface-vision-trainer593

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 limits

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