Reference
The SkillCDN Format
- Status: Draft.
- Implemented by:
packages/core(parseSkillManifest,parseRepoManifest,classifyRepoFile,isServedPath,summarizeMarkdown). Fixtures live inapps/server/fixtures/.repositories/; the reference repository, written in the format and served live, isskillcdn/skills.
How SkillCDN reads a repository, and how a repository is written so that an agent uses it well. The layout is the Agent Skills layout: a repository that already works as a skills folder works here unchanged. That layout, together with the conventions that grow around it (what sits next to SKILL.md, what the repository root says, how plain documents are written), is the SkillCDN Format. This file is its specification; user documentation will be rendered from it.
repo/
SKILLCDN.md the repository manifest: name, description, which directories hold
documents, and the rules that hold for every skill
README.md optional introduction, summarized for discovery and read on demand
SKILL.md a single-skill repo, or
skills/
<name>/SKILL.md a multi-skill repo; front-matter carries name, description, ...
<name>/references/ documents the body points to
<name>/assets/ small data files the skill reads
<name>/scripts/ optional helpers; served as text, never run
<area>/ or one folder per area of work (marketing/, engineering/, ...)
SKILLCDN.md the area's manifest: who its skills are for, the rules they add
README.md the area's optional introduction
skills/<name>/SKILL.md the area's skills, laid out as above
docs/ the area's documents, discovered next to its manifest
docs/ plain documents: listed, searched and read without a skill;
other directories may be declared or individual Markdown files linked
.agents/skills/<name>/ hidden ancestors do not hide an explicitly declared SKILL.md
Reading rules
What the indexer does with a repository. These hold for every repository, whether or not it follows the rest of the format.
- A skill is a directory that holds a valid file named exactly
SKILL.md, at any depth, including under hidden ancestors. The exact file declares the skill; no repository-specific folder names or allowlists are needed. A file belongs to the nearest skill directory above it, so a nested skill owns its own files. A rootSKILL.mddeclares a root skill without suppressing nested skills. SKILL.mdis YAML front-matter followed by a Markdown body. Everything else in the skill directory is a supporting file the body may point to.- Documents are discovered in
docs, at the repository root or next to the nearest governing manifest; a manifest can declare different directories (the repository manifest). These are bulk discovery roots, not a denylist. Linked Markdown files outside them can also be read (references). - READMEs introduce visible folders. The repository root and folders already exposed through skills or documents can supply an optional README overview, even without
SKILLCDN.md. Discovery carries a short summary and canonical path; the full text is read only when needed. Overview eligibility alone does not add a searchable document or increase document counts (introductions). - A repository without any skill still works as a document endpoint through
browse_repo,search_repoandread_repo_file. - Hidden paths require a declaration. A valid
SKILL.mddeclares its skill even under hidden ancestors. Ordinary supporting descendants are eligible, but additional hidden children requireskillcdn.include, an explicit document-directory declaration or a Markdown link. A file link exposes that file, never its siblings. Unrelated hidden files remain excluded. - Explicit exclusions take precedence. A manifest's
excluderemoves the named files or subtrees from every discovery and reading route, including valid skills, README overviews, includes and references. Descendant declarations cannot reopen excluded paths (exclusions). - Folders retain their real paths.
browse_reposhows the same hierarchy whether or not manifests exist. ASKILLCDN.mdadds metadata and rules to its folder. A skill belongs directly to the nearest manifest folder; ancestor counts include descendants without duplicating their identities. - Repositories declare; they never ship code that we execute. Composition ("take this input, call these tools in this order") will be declared in Markdown or YAML. Scripts in a repo are files like any other: readable, never run.
allowed-toolsis passed through as text and grants nothing. - Indexed content: skill manifests, Markdown (
.md,.markdown,.mdx) and small JSON. Every other file is listed and can be read, but is not searched. A Markdown or JSON file over the per-file indexing limit is listed and readable like them, but not searched or summarized;checknames such files. A file over the read limit is not served at all: it is named with its size and where its bytes are at the host (what the extension serves). Binary files are listed, never searched. Limits are configuration with safe defaults; the index is partial, and says so, only when the tree, file-count or total-size limits leave entries out. - A plain Markdown document is listed under its front-matter
titleanddescriptionwhen it has them; otherwise its title is its first level-one heading, and its description is the first paragraph after that heading (or the first paragraph of the document when there is no heading), shortened to 200 characters. Links become their text; badges, images, lists, quotes, tables, code and HTML blocks (to the blank line that ends them, however their lines are indented) are skipped on the way. - A manifest that cannot be read is reported, never dropped in silence. Its path, code and reason appear in diagnostics in the REST API, the page of the address and the tools. Its boundary survives parsing, size and indexing failures: a failure must not restore broader default discovery. A commit whose tree listing may be missing a manifest, because the git host cut it short or it holds more manifests than the tree limit admits, is not published at all: it fails to index with
indexer.tree_truncated. Before pushing,checkreads a working tree with the same indexer and prints the findings (the check role). - Repos whose owner has verified them with the service are verified; until owners can, the operator of a deployment lists the repositories it vouches for. Responses from every other repository carry a provenance notice (tools).
Front-matter
| Field | Required | Rule |
|---|---|---|
name | yes | 1 to 64 characters on one line. |
description | yes | Nonempty text. Authors follow the Agent Skills limit of 1024 characters; the reader preserves longer descriptions with a warning within the front-matter size bound. What the skill does and when to use it; this is what search_repo ranks first. |
license | no | Short text. |
compatibility | no | 1 to 500 characters. |
allowed-tools | no | Text, passed through. |
metadata | no | Mapping from short text keys to short text values. |
skillcdn | no | A mapping with what SkillCDN adds to the layout, so that the top level stays the specification's: include and translations, below. |
skillcdn.include | no | A sequence of at most 20 paths of Markdown or JSON files the skill needs on every run, written as a link destination is: relative to the skill directory, or from the repository root with a leading /. load_skill and the skills extension return their text with the skill (tools). The skillcdn key stays in the source and is left out of the served document. A path inside the skill directory names any of its files, hidden ones included. A path outside it names a shared page: a document served under a document directory that a manifest at or above the skill declares (shared pages). Anything else is dropped and reported. |
skillcdn.translations | no | A mapping from a language tag (ko, pt-BR) to a mapping with title (1 to 200 characters on one line) and description (1 to 1024 characters), for people who read the page of the address in that language. At most 32 languages; an entry that translates nothing usable is dropped and reported. These are display text, not search aliases or agent context. Agents read the original name, description and body. |
- A manifest that breaks a rule for a required field, has no front-matter, or has front-matter that is not valid YAML is skipped and reported. It never fails the whole repo. The file stays readable, so that the author can see what was found; it is listed and searched only where a document would be.
- A
namethat ignores the naming convention (lowercase letters, digits and single hyphens) or differs from its directory name is served with a warning. The directory rule does not apply to a manifest at the mounted root. - An optional field that cannot be used is ignored with a warning. Unknown fields are ignored silently, so the convention can grow.
- Two skills may share a
name; the exact repository-rootSKILL.mdpath identifies a skill.
Writing the values
The front-matter is YAML, and a sentence written as a plain (unquoted) value can mean something else to YAML than to its author. The three mistakes that matter, and what the report says about them:
| Written | What YAML reads | The report |
|---|---|---|
description: Makes a video: fast and cheap. | A nested mapping: a colon followed by a space splits a plain value. The front-matter is invalid and the skill is skipped. | invalid_front_matter, with the hint that the value of description contains : . |
description: Greets people # by name | Greets people: a space followed by a hash starts a comment. The skill is served with half its description. | The warning commented_value. |
description: [x] Marks the task done. | A sequence, or an error: [, {, &, *, !, %, @ and ` mean something at the start of a value. | invalid_front_matter, with the hint that the value starts with that character. |
Quote such a value (description: "Makes a video: fast and cheap.") or write it as a block scalar, which takes any text:
description: >
Makes a video: fast and cheap. Use when the user wants a promo
short for their product.
The format
The reading rules say what is served. The format says how to make it useful: an agent browses folders or searches descriptions, loads an exact skill with load_skill, follows its continuation until complete, and reads optional references with read_repo_file; a host that implements the MCP skills extension receives the skills through it. A person opens the page of the address and browses the same structure.
Required
A repository is in the SkillCDN Format when all of these hold. They are what the explorer checks and what the reference repository's own check enforces.
- Every skill directory has a
SKILL.mdwhose front-matter passes the table above, whosenamefollows the naming convention and equals the directory name, and whose body starts with a level-one heading. - The
descriptionsays what the skill does and when to use it, in the words a user would say. It is whatsearch_reporanks first. Say when another skill is a better choice when their purposes overlap. - References make dependencies explicit. Use Markdown links for repository files needed by the workflow. Shared
.mdreferences outside the skill work through a repository-root connection. A sub-path connection cannot read references outside its mount; keep a skill self-contained when it must also work alone, and include a shared page every run needs, so that it travels with the skill (shared pages). - Everything an agent must read is Markdown or small JSON. Other files are listed and readable, never searched; rendered media and binaries are not part of a skill.
- Nothing has to run. A skill works from its
SKILL.mdand the documents it points to. Scripts may be shipped as help for agents that run locally, and are served as text. - Shared rules have a
SKILLCDN.mdat their scope. A root manifest states repository-wide rules; nested manifests add rules for their descendants. A manifest is optional when the repository needs no extra metadata, rules, document roots or exclusions. README introductions are available separately and do not become shared rules. - No secrets, no private hostnames, no real people's identity or likeness, in any file. The repository is served to whoever holds the address.
Recommended
The layout of a skill:
skills/<name>/
SKILL.md front-matter and the instructions; kept to a few hundred lines
references/ documents the body points to from the phase that needs them
assets/ small data files the skill reads: example JSON, schemas
scripts/ optional helpers for agents that run locally
The body of a SKILL.md, in this order, so that an agent that has read one knows where to look in the next: a title and a one-paragraph summary of what comes out from what; the requirements, that is the tools the skill calls and what to do when one is missing; the inputs, what the user must supply and the questions to ask for gaps; the workflow, as numbered phases that name what each consumes and produces and where the user is consulted; the hard rules an agent must never break; and the terms the skill uses with a fixed meaning.
A file that every run needs is declared in skillcdn.include, so that it arrives through the skill's context pages; a file that only some phase needs is linked from that phase and read when the phase comes. A page the skills of one tool family share is declared the same way, from a document set above the skill (shared pages).
Keep the skill body and always-required includes modest: about 16 KiB of UTF-8 text combined is a useful authoring target before inherited rules. This is guidance, not an admission limit. Larger context is paged; inherited rules and response metadata also take space. Link optional material from the phase that needs it so agents can read it on demand (loading a skill).
Write the canonical name, description, body and shared rules in English by default, so agents use the same original across UI languages. Other original languages are supported; declare the language in the nearest applicable manifest. SkillCDN preserves the author's original and never chooses an agent's skill language from the visitor's UI language.
Use skillcdn.translations (a title and description) and the manifest's translations for people reading the page in another language. Translations are display-only: the explorer marks translated skill summaries, and search uses original metadata and body text rather than translation fields. The original name remains visible beside a translated skill title. MCP discovery and skill context use the original fields and body; reading an exact source file can still show its translation declarations as written.
Connect the repository root by default, so one connection can reach shared references and different areas of work. Both <area>/skills/<name>/ and skills/<area>/<name>/ are supported without hiding the skills segment. Put a SKILLCDN.md where common descriptions and rules belong. Describe who should use that folder's skills and for which tasks. Users may express a preferred area in their client instructions or request; SkillCDN does not remember a selected role.
Plain documents (a document set is a directory of Markdown without a SKILL.md) carry a front-matter title and description, or start with a level-one heading followed by one summary paragraph, because that is what search shows.
A repository without a manifest
A client is told the repository, commit and mounted path, with a bounded folder overview (tools). README summaries provide orientation without loading the introduction; the git-host description is the fallback when no local description is available. Valid skills are discovered at any depth, documents in root docs are independently discoverable, and local Markdown references can add readable files. The folder tree needs no inferred aliases. A repository adds manifests only where metadata, common rules, other document roots or exclusions are useful.
README introductions
A README at the repository root or in a directory already exposed through skills, documents or their ancestors can be read with read_repo_file. A README alone does not expose an unrelated hidden folder or make every folder in a repository discoverable. The same size, text, symlink, mount and exclusion rules apply as for other files.
The basename is README without a language suffix, matched case-insensitively, and the extension is .md, .markdown or .mdx. Choose one overview per directory: exact README.md, then readme.md, then other .md spellings, .markdown, and .mdx, with lexical order breaking ties. The canonical path preserves the file's actual spelling.
browse_repo returns the current folder's optional overview with its canonical path, title and short description, and can identify a child folder's overviewPath. Titles and summaries use the normal Markdown summary parser. The manifest's explicit name and description take precedence when describing its folder; a README summary fills a missing introduction, followed by the git-host description for the repository root. Reading the overview remains optional: its body is never appended to connection instructions, inherited rules or a skill's required context.
The selected README is the original, without choosing a language variant from the UI language. Authors should write this original in English by default and link human-facing translations themselves. Reading an exact eligible translation path still returns that file as written.
Overview-only files stay out of independent search and document counts. A README independently eligible as a document or supporting file keeps that eligibility. Its local Markdown links follow the ordinary bounded reference rules below, allowing an agent to read a relevant linked guide on demand.
The repository manifest: SKILLCDN.md
A file named exactly SKILLCDN.md supplies metadata for its folder and a Markdown body with rules for descendant skills. Manifests may be nested; they do not make isolated projects (ADR-0022). They use the same safe front-matter parser and limits as SKILL.md.
---
name: SkillCDN skills
description: Skills for everyday work, one folder per area (marketing, product, engineering, and more as they are added), served live through SkillCDN. Use to run a skill that drives a tool for a task in one of these areas, or to see how a skill repository in the SkillCDN Format is written.
documents:
- docs
language: en
translations:
ko:
name: SkillCDN 스킬
description: 업무 분야별 폴더(마케팅, 기획, 개발, 그리고 앞으로 추가될 분야)로 정리된 일상 업무용 스킬 모음입니다. SkillCDN을 통해 실시간으로 제공됩니다. 각 분야의 도구를 다루는 스킬을 실행하거나, SkillCDN 포맷의 스킬 저장소가 어떻게 쓰이는지 볼 때 쓰세요.
license: MIT
metadata:
author: skillcdn
---
# Rules for every skill in this repository
**Ask only what cannot be derived.** A skill asks for the inputs it cannot get from the request, in one short message, and derives everything else from those inputs and sensible defaults.
**Spend only with consent.** Anything that costs the user money or credits is estimated first and started only after they agree.
...
| Field | Required | Rule |
|---|---|---|
name | no | 1 to 100 characters on one line. Default: the repository's name on the git host. |
description | yes | 1 to 1024 characters. Who should use the skills in this folder and for which tasks. Shown with the folder and in connection context when space permits. |
image | no | The picture that stands for the folder (ADR-0031): the path of a file of the repository, relative to the manifest (assets/banner.png), or an https URL. Any picture, of any size and shape; the explorer's card shows its middle at two to one, the page of the address shows it wider. A path that is not a file of the repository, or anything else, is reported and ignored. A folder without one inherits the nearest ancestor's. The pages load it from where it is, the host's raw copy of the file at the commit shown or the URL as written, and never store it. |
documents | no | At most 20 directories relative to the manifest (docs, docs/ or . for its own directory), no ..; invalid entries are dropped and reported. Their Markdown and JSON are discoverable, and their eligible text files can be read. Default: docs next to the manifest. An empty sequence disables bulk document discovery; it does not block explicit file references. An explicitly named hidden directory is allowed. |
exclude | no | At most 100 exact file or subtree paths relative to this manifest; . excludes its entire scope. Omitted or empty: no exclusions. A trailing / is allowed. No globs, negation, absolute paths or ... Ancestor exclusions accumulate and cannot be overridden. Any invalid entry fails the manifest closed rather than silently dropping the policy. |
language | no | The tag of the language the original content is written in (en, ko, pt-BR); English is the recommended authoring default. Told to the client on connect, so that a model searches in that language; on the page, text shown as the author wrote it is marked as being in it. It does not select a translation. A tag that is not one is ignored and reported. |
translations | no | A mapping from a language tag to a mapping with name (1 to 100 characters on one line) and description (1 to 1024 characters): display text for people reading the page in that language. At most 32 languages. These are not search aliases or agent context; agents read the original name, description and rules. |
license | no | Short text, as in SKILL.md. |
metadata | no | Mapping from short text keys to short text values, as in SKILL.md. |
The values are YAML, with the same pitfalls as in SKILL.md (writing the values).
The nearest manifest determines bulk document discovery for a file outside skills. Its documents declaration replaces the default at that scope; document directories are not accumulated from ancestors. A manifest itself is readable but not an independent search result.
Rules are different: load_skill and the skills extension return every ancestor manifest's body in root-to-nearest order, labeled by canonical source path, before the skill body. Only language and image automatically inherit among metadata fields, with a nearer declaration overriding them. Names, descriptions, translations, licenses and metadata remain attached to the manifest that declares them.
A failed manifest retains its boundary and is reported. Its descendant scope is closed to discovery and reads because its publication policy could not be established. Neither nested declarations, includes, README overviews nor links reopen it. The policy's diagnostic identifies the problem without exposing descendants. Manifests are considered even under hidden ancestors so exclusions can govern hidden skills. While indexing is incomplete, reads report indexing rather than guessing the policy.
A sub-path connection retains applicable ancestor rules through load_skill continuation, but ordinary read_repo_file cannot read outside the mount. All source paths remain repository-root paths.
Excluding files and subtrees
Place the policy at the narrowest relevant scope. For example, to keep test repositories out of a repository's served skills, add this SKILLCDN.md inside the directory that contains those fixtures:
---
description: Internal test repositories.
exclude:
- .
---
To omit only some descendants, name their paths instead, such as templates or references/draft.md. Exclusions match complete path segments, so templates does not exclude templates-public. They use repository path spelling and never escape the manifest directory. A sub-path connection inside an excluded subtree does not bypass an ancestor's policy.
A manifest cannot be excluded on its own: a policy stays with the files it governs, so excluding a SKILLCDN.md, from an ancestor or from itself, withholds its whole directory. Excluding the directory says the same thing on purpose; excluding only the manifest is reported as excluded_policy.
Exclusions govern the served set before parsing descendant skills and walking references. Intentionally excluded fixtures therefore produce neither skills nor irrelevant manifest diagnostics. This is publication control within SkillCDN, not confidentiality: files in a public git repository remain public on the git host.
Markdown references
The index resolves local Markdown links from valid skills, applicable rules, README overviews and served Markdown documents, recursively following targets ending in .md. Inline and reference-style links are supported. Relative links are resolved against their source file; a leading / means the repository root. Fragments are removed. External URLs, directory links and anchor-only links are not repository file references. A filename mentioned only in prose or code is not a declaration.
Every resolved target remains inside the repository and refers to the same commit. Normalized paths prevent traversal; symlinks and submodules are not followed. A visited set prevents cycles, and file, byte and traversal limits bound the work. Excluded paths and failed manifest scopes block expansion. A link to another skill or manifest preserves that file's identity and does not make its rules apply to the source skill.
Reference inspection reads at most 262,144 characters and records at most 200 local destinations per source file. Expansion follows at most eight edges beyond already served content, within the repository's file and byte budgets. Hitting these limits marks the index partial; per-file inspection limits also produce a diagnostic.
An otherwise undiscovered target becomes readable, including a hidden file explicitly named by a link. It does not become an independent searchable document, expose siblings or declare another folder's content. Canonical reference paths are returned with the skill for reading when needed.
The served set and reference graph are computed once per repository and commit. A sub-path mount clips the result to its path; it does not recompute graph reachability from local sources. References outside that mount cannot be read there. Repository-root connections can use shared reference directories without copying their contents into every skill; a page every run needs travels with the skill instead, as a shared include.
Shared pages
The skills of one tool family repeat what they know about the tool unless it lives in one place. Put it in a document set that the manifest above the skills declares (docs/<family>/ at the repository root, or an area's docs/ when one area holds every skill of the family), one file per topic, and let each skill declare in skillcdn.include the pages every run needs, with a root-relative path (/docs/<family>/models.md); the rest are linked from the phase that reads them (ADR-0044).
A page a skill includes this way is a shared page: a Markdown or JSON document served under a document directory that the manifest governing it declares (the root's docs where no manifest governs), where that manifest is at or above the skill, as the manifests whose rules the skill carries are. A file of another skill, a page of a sibling area, an excluded or hidden file and a file only a link reaches are not shared pages: such an include is dropped and reported, and it declares nothing, since an include never publishes a file outside the skill directory.
A shared page is served with the skill as a local include is: inlined in the assembled SKILL.md and in load_skill in declaration order with the skill's own includes, under its repository-root path, and counted in the skill's digest, so that a change to the page changes the digest of every skill that includes it, as a change to a manifest does. It arrives on every route the skill does, a sub-path mount included, as the ancestor rules do; read_repo_file still cannot read it there by its path. The page stays a document of the repository in its own right, listed, searched and read where it is, and it is not among the skill's resources in the skills extension. A copy of the skill taken from the git host has the page's path in its source and must fetch it; check prints the shared pages of each skill apart from its own files for that reason.
The 16 KiB authoring target covers the skill body and every include, shared pages among them: what every run needs is included, the rest is linked.
Licenses
What a deployment may pass on follows the license it finds (ADR-0026). For each skill the reader takes the first of these that says something:
- A license file in the skill's directory:
LICENSE,LICENSE.md,LICENSE-MIT,LICENCE,COPYING,UNLICENSEand the like, the plainest name when there are several. - The skill's
licensefield.MIT,Apache-2.0,CC-BY-4.0,GPL-3.0-or-laterand the usual ways of writing them are recognized;MIT OR Apache-2.0is as closed as its most closed part; a field that only points elsewhere (See LICENSE) is skipped. - The
licensefield of each manifest between the skill and the repository root, nearest first. - The repository's license file.
- The root manifest's
licensefield.
Files outside the skills follow the same list from step 3. A license is permissive when it allows copies to be passed on with its notice: the usual open-source licenses, public-domain dedications and Creative Commons Attribution, ShareAlike included; copyleft conditions bind what the reader does next, not the serving. It is restrictive when it reserves all rights, forbids redistribution or derivatives, allows non-commercial use only, or is a text the reader does not know, which is the safe mistake; a license file that cannot be read, or is over 64 KiB, counts as unrecognized. A text is read as the license it names itself, not as one it quotes: a source-available license that turns into a permissive one later (the Functional Source License, the Business Source License) and a permissive text with a condition added to it (the Commons Clause) are restrictive. A repository that says nothing has no license.
- A permissive skill is served in full, and results name the license and where it was found:
License: MIT (LICENSE). - A restrictive skill is described only:
browse_repoandsearch_repolist it and say so,load_skillreturns its name, description, license and a link to its source and nothing else,read_repo_filerefuses the files of its directory, and the skills extension does not list it. A verified repository is served in full on its default branch: the owner, once owners can verify, or the operator lifts the restriction by vouching for it (tools). - Files outside the skills under a restrictive license are listed and refused the same way.
- A skill without a license is served in full with its provenance notice, and a repository without one is never featured.
check prints the repository's license and each skill's, with (described only, unless the repository is verified) where that applies.
What the skills extension serves
A host that implements the MCP skills extension receives the skills of a mount as skills, with every file digested (tools). What it lists is decided when the commit is indexed (ADR-0024, ADR-0025, ADR-0033):
- A skill is listed when a host can hold it whole: its directory is named after it (or it is the root skill), every served file inside its directory within
READ_MAX_FILE_BYTESis stored, there are at most 512 of them, and they total at most 16 MiB. The files of nested skills count as the enclosing skill's. A root skill whose name is also a directory of the repository is not listed, because its URI would be ambiguous. - A file over
READ_MAX_FILE_BYTESis left out, and named. The skill is listed without it; its servedSKILL.mdnames the file with its size and where its bytes are at the host, so that a host that installed the skill knows what is missing and can fetch it. The skill carries a warning for the author,load_skilllists the file with its source, andread_repo_filerefuses it with the same link. The file does not count toward the bounds above. - Identical copies are listed once, at the visible or else the shortest path: a skill under a per-agent hidden directory that repeats a visible one, file for file, is neither listed nor searched.
- A skill under a hidden directory is listed and searched only when the repository has no visible skill. Its files leave search with it, so that a copy does not come back through its reference files; the copy and its files stay browsable and readable by path, and counts cover only what can be discovered.
- A skill that is not listed stays loadable and readable through the tools. Its warnings say why it is not listed, and
checkprintsextension: not listed (<reason>)for it:name_directory_mismatch,too_many_files,too_large,file_unavailable(a file within the limit, or a shared page the skill includes, could not be stored),duplicate,hiddenoruri_collision.
The SKILL.md a listed skill is served as is assembled from its sources, and its digest is computed over that document: the front matter of the source, re-serialized with every value quoted and without the skillcdn key; a line that names the files and the commit it was assembled from; one line per file left out for its size, with the size and where to fetch it; the bodies of the repository manifests from the repository root to the nearest, each under --- applicable rules: <path> ---; the skill's own body under --- instructions ---; and the files it includes, each under --- included file: <path> ---, the shared pages outside its directory among them (shared pages). Every other file of the skill is served as it is in the repository; a shared page is not among its resources. read_repo_file still returns the source SKILL.md as written. A change to a manifest above a skill, or to a shared page it includes, changes the skill's digest, and a host that verified the skill asks its user again.
Checking a repository before pushing
The skillcdn check command (@skillcdn/cli on npm) reads a directory as the indexer reads a commit, with the same code, the same parsers and the same limits, and prints what an agent would get, headed by the version of the reading rules it applied: the manifest, the repository's license, every skill with its files, warnings, its license and whether the skills extension lists it, the documents outside the skills, linked references, what is not served, index diagnostics, and the instructions a client is told on connect. It needs no database and no git host, executes nothing from the directory, and exits with 1 when index diagnostics are present, so it can gate a push. The server image has the same code as its check role.
npx @skillcdn/cli check # the current directory
pnpm dlx @skillcdn/cli check /path/to/repository
node packages/cli/dist/bin.js check ../skills # from a checkout of this repository, after pnpm build
docker run --rm -v "$PWD:/repo:ro" skillcdn check /repo # from the image
The working-tree reader does not follow symbolic links and excludes repository administration and dependency directories. Hidden content otherwise follows the same declarations as indexing. It reads a working directory rather than a commit, so untracked or ignored files can make its report differ from what was pushed. Only the INDEX_* limits are read from the environment (deploy).
Parsing requirements
Everything in a repo is untrusted input:
- YAML is parsed with the failsafe schema: strings, mappings and sequences only, so
1.0andnostay the text the author typed. Explicit tags, aliases, duplicate keys, non-string keys and a second document are errors. Front-matter is at most 16,384 characters and a manifest at most 262,144. - Front-matter fields are validated against the table above. Text that carries control or invisible characters is rejected, never cleaned up.
- Paths from manifests must stay within their declared repository scope. Canonical paths returned to clients are repository-root paths. Symlinks are not followed.
- Markdown links are parsed as data, normalized and checked before bounded local reference expansion. They never cause a network fetch to a linked URL or execute repository code.
Open questions
- The exact rules for verified once owners can verify a repository themselves; the operator's list is a stand-in.
- How
intakequestions are declared. - How skills that need user files describe an upload step, given that remote MCP servers cannot receive chat attachments.
- Whether the explorer reports the format's required points 3 to 6 as diagnostics, next to the manifest diagnostics it already reports.
- Search matches words as written. A repository in a language whose words carry particles or inflections (Korean, for one) is found by its exact words; whether
languageshould pick a text-search configuration, or a morphological or embedding search should take over, is open.