Skills Extension - ChristianPresley/zig-sdk GitHub Wiki

Skills Extension

The Skills extension lets a server publish skills together with its tools, resources and prompts1. A skill is a directory of files with a SKILL.md at its root2. The server serves each file as a resource, and the client reads the files with resources/read. The extension adds the methods skills/list, skills/get and resources/directory/read. The extension is optional. The server enables it with the skills option.

Applies to: MCP 2026-07-28, extension io.modelcontextprotocol/skills (SEP-26403), zig-sdk 0.2.0.

Enable the extension

var server = try mcp.Server.init(gpa, io, .{
    .info = .{ .name = "docs", .version = "0.1.0" },
    .skills = .{},
});
try server.addSkill(.{
    .name = "pdf-processing",
    .skill_md = @embedFile("skills/pdf-processing/SKILL.md"),
    .files = &.{
        .{ .path = "templates/invoice.md", .content = @embedFile("skills/pdf-processing/templates/invoice.md") },
    },
});

The server then advertises the extension under capabilities.extensions in the server/discover result4. The option directory_read is true by default, and the settings object is then {"directoryRead": true}. With .skills = .{ .directory_read = false }, the settings object is empty and the server does not answer resources/directory/read. The server also declares the resources capability, because the extension needs it.

The extension defines no client declaration. Thus the server does not gate the methods on the client capabilities. A client sends the methods only after it saw the declaration of the server. Without the skills option, each method of the extension gives error -32601, and addSkill gives error.ExtensionNotEnabled.

Register a skill

addSkill takes the files of one skill in memory. The server copies all bytes.

Field Meaning
scheme The URI scheme. The default is skill.
prefix The organizational prefix of the skill path, for example acme/billing. The default is empty.
name The name of the skill directory.
skill_md The bytes of SKILL.md.
files The other files. Each file has a relative path, the content and an optional mime_type. Without a MIME type, the server selects one from the file extension.

The skill URI is scheme://prefix/name/SKILL.md, or scheme://name/SKILL.md with an empty prefix. A file has the URI scheme://prefix/name/path. The first segment of the URI is the authority, so it cannot contain : or @.

Steps of the registration

  1. addSkill checks name against the name rules of the Agent Skills specification (error.InvalidSkillName).
  2. It checks the scheme and the prefix (error.InvalidSkillPath).
  3. It refuses a second skill with the same URI (error.DuplicateSkill).
  4. It parses the frontmatter of SKILL.md into a JSON object.
  5. It checks that name and description are strings, and that description has 1 to 1024 bytes.
  6. It checks that the frontmatter name is equal to name (error.NameMismatch).
  7. It checks the number of files (error.TooManySkillFiles).
  8. It checks each file path (error.InvalidFilePath). A file cannot have the path SKILL.md.
  9. It checks the sum of the file sizes (error.SkillTooLarge).
  10. It computes the SHA-256 digest and the size of each file.
  11. It checks the nested skills (error.IncompleteSkill).
  12. It registers each new file as a resource.

The frontmatter errors are error.MissingFrontmatter, error.UnsupportedFrontmatter and error.InvalidFrontmatter. When a check fails, the server registers no file of the skill. The last step can fail with error.DuplicateName when the application registered a resource at the URI of a skill file. The files that the server registered before that error stay as resources.

Nested skills

A skill can contain other skills in subdirectories. The file list of the outer skill must also list the files of the nested skill. Register the nested skill with its own addSkill call and a prefix that is the full path of the outer skill, for example .prefix = "tool", .name = "inner". The two skills share the files, and the server registers each shared file one time.

The check works in both orders of registration. A file of the nested skill that the outer skill does not list, or lists with a different digest, gives error.IncompleteSkill. The same URI with different bytes gives error.ConflictingSkillFile.

Dynamic skills

addDynamicSkill registers a skill whose content the application generates. It takes scheme, prefix, name and skill_md, and no files. Its entry has "resources": "dynamic", and the server takes the frontmatter of the entry from skill_md.

The server registers no resource for a dynamic skill, also not SKILL.md. The application serves SKILL.md and the other files with its own resources or resource templates. The server applies no limits and no nesting checks to a dynamic skill. resources/directory/read does not show the files of a dynamic skill.

The frontmatter parser

The frontmatter is YAML. The SDK has its own parser for a safe subset of YAML. The server and the client use the same parser. Thus both sides get the same JSON object from the same text.

Accepted Rejected
Block maps and block sequences Anchors, aliases, tags, directives and the merge key <<
Plain scalars, also on more lines with a deeper indent Flow collections with content
Single-quoted and double-quoted scalars on one line Quoted keys, complex keys and duplicate keys
Literal (|) and folded (>) block scalars, with - or + Indentation indicators on block scalars, such as |2
Comments, [] and {} Tabs in the indentation, and maps in sequence items
Integers with 0x or 0o Plain scalars that YAML 1.1 reads differently from YAML 1.2

Plain scalars resolve with the core schema of YAML 1.2. The parser rejects the words y, n, yes, no, on and off in each case, and =. It also rejects numbers with a leading zero, base-60 numbers, .inf, .nan, dates and times. YAML 1.1 parsers read them as other types. Quote such values in the frontmatter. The parser also rejects a nesting deeper than 16 levels.

Methods

Method Result
skills/list The entries of all skills, with pagination. Each entry is complete and never splits across pages. The result has ttlMs and cacheScope from the server option cache.lists.
skills/get The entry of the skill with the SKILL.md URI params.uri. The result has ttlMs and cacheScope from the server option cache.reads. An unknown URI gives error -32602.
resources/directory/read The direct children of a directory, sorted by URI, with pagination.

With the default cache options, the results have "ttlMs": 0 and "cacheScope": "private".

An entry has three fields:

  • uri: the URI of SKILL.md.
  • frontmatter: the frontmatter as a JSON object. The object keeps every field of the author.
  • resources: the complete file list with uri, digest (sha256: and 64 hexadecimal digits) and size, or the string "dynamic".

In a directory result, a file has uri, name, mimeType and size. A subdirectory has uri, name and the MIME type inode/directory. The result has no ttlMs and no cacheScope.

A directory URI has no slash at the end. The skill root, each subdirectory and each level of the prefix are directories. A URI that is not a directory gives error -32602. Without directory_read, the method gives error -32601.

Files that are valid UTF-8 go out as text. Other files go out as a base64 blob. The digest covers the raw bytes in both cases.

The client

The client has these methods:

Method Effect
listSkills(arena, cursor, options) Sends skills/list.
getSkill(arena, uri, options) Sends skills/get.
readDirectory(arena, uri, cursor, options) Sends resources/directory/read. Send it only when mcp.protocol.skills.serverSupportsDirectoryRead is true for the capabilities of the server. The client does not check this.
readSkillFile(arena, entry, uri, options) Reads one file under a held entry, verifies it and returns the raw bytes.

The extension gives the host a set of rules for the content of a skill1. readSkillFile applies the rules of the extension that concern one read:

  1. It refuses an entry that mcp.protocol.skills.validateEntry rejects.
  2. It refuses a URI that the file list of the entry does not list (error.UnlistedFile). It does this before it sends a request.
  3. It compares the size of the content with the entry (error.SizeMismatch).
  4. It compares the SHA-256 digest of the content with the entry (error.DigestMismatch).
  5. For SKILL.md, it compares the frontmatter field by field with the entry (error.FrontmatterMismatch).

After a failure, do not use the content. Get the entry again with getSkill and ask the user again for approval. For a dynamic skill, the client can only check that the URI is inside the skill (error.OutsideSkill) and that the frontmatter agrees.

A directory read is a live observation. It does not extend the file list of a held entry.

Host rules outside the SDK

The extension gives the host more rules. The application must apply them:

  • Show the origin server of each skill to the model and to the user.
  • Record skills, approvals and cache entries under the server identity and the skill URI together.
  • Bind an approval to the file list of the entry. A changed file list cancels the approval.
  • Do not read files before the skill needs them.
  • Do not obey allowed-tools without the explicit approval of the user.
  • Treat skill content as data from a source that you do not trust.

Limits

Limit Default Effect
limits.skills.max_files 512 The maximum files in one skill, SKILL.md included. Above it, addSkill gives error.TooManySkillFiles.
limits.skills.max_bytes 16 MiB The maximum sum of the file sizes of one skill. Above it, addSkill gives error.SkillTooLarge.

The defaults are the limits of the extension. A host must accept skills up to these limits. On the client, readSkillFile uses the larger of the client limits and the limits of the extension, so a smaller client value has no effect. The client reports both overflows as error.SkillTooLarge. The server applies the limits as the application sets them.

Conformance

The official conformance suite has no scenarios for the extension. The unit tests of the SDK use the example of the specification and check its sizes and digests.

References

  1. MCP-EXT-SKILLS: Skills extension specification.
  2. AGENT-SKILLS-SPEC: Agent Skills specification.
  3. MCP-SEP-2640: SEP-2640, the Skills extension.
  4. MCP-EXT-OVERVIEW: Extensions of the protocol.
⚠️ **GitHub.com Fallback** ⚠️