Gateway
Gatewaybeta8 min

Agent Skills over MCP

Serve Agent Skills (folders with a SKILL.md) to every client of the gateway through the MCP Skills extension — skills/list, skills/get, skill:// resources and directory reads — from config alone, under the same access rules and audit as every other resource.

An Agent Skill is a folder of instructions for an agent: a SKILL.md with a short YAML header (name, description) and a body that says how to do one kind of task, plus any references, templates or scripts it points to. Hosts show the model each skill's name and description, and load the full text only when a task needs it.

The Skills extension (SEP-2640, io.modelcontextprotocol/skills) lets an MCP server ship skills next to the tools they describe. MCPG implements the server side: point mcp.skills at a folder and every client of the gateway can find, verify and read those skills. No plugin and no code.

Serve a folder of skills

yaml
mcp:
  skills:
    sources:
      - path: skills          # relative to this config file
        prefix: macos         # optional: served under skill://macos/…
    governance:
      minimum_trust: header_asserted
  instructions: |
    Load skill://macos/computer-use/SKILL.md before you use these tools.
text
skills/
├── computer-use/
│   ├── SKILL.md
│   └── references/
│       └── tool-map.md
└── screen-coordinates/
    └── SKILL.md

Each folder that holds a SKILL.md is one skill. The folder name must equal the name in its frontmatter. With the config above the gateway serves:

ResourceURI
Skillskill://macos/computer-use/SKILL.md
Supporting fileskill://macos/computer-use/references/tool-map.md
Skill folderskill://macos/computer-use (a directory resource)

SKILL.md

markdown
---
name: computer-use
description: Operate the user's Mac through this server's tools. Load this first.
license: Apache-2.0
metadata:
  version: "1.0"
---

# Computer use on macOS

Work in a loop: observe, act, observe again. …
See `references/tool-map.md` for every tool.

name is 1 to 64 lowercase letters, digits and single hyphens. description is 1 to 1024 characters. Every other frontmatter field is passed to clients unchanged.

What clients get

When mcp.skills has a source, the gateway declares the extension in initialize (2025-11-25) and server/discover (2026-07-28):

json
{ "capabilities": {
    "resources": { "listChanged": true },
    "extensions": { "io.modelcontextprotocol/skills": { "directoryRead": true } } } }
MethodAnswer
skills/listEvery skill the caller may see: its SKILL.md URI, its frontmatter verbatim, and every file with a sha256: digest and its size. Paginated with cursor / nextCursor.
skills/getOne entry by its SKILL.md URI. An unknown skill, or one the caller may not see, is -32602.
resources/readA skill file's bytes: text for UTF-8 files, blob (base64) for others. cacheToken is the file's digest.
resources/directory/readThe direct children of a skill folder or of a prefix folder: files with their mimeType and size, folders as inode/directory.
resources/listAlso lists each SKILL.md, for clients that do not speak the extension.

On the 2026-07-28 wire, results carry resultType: "complete"; skills/list also carries ttlMs and cacheScope: "private".

A client that knows only resources still works: it sees each SKILL.md in resources/list and reads it — and the files it names — with resources/read. Point the model at the skills from mcp.instructions.

Integrity

The gateway reads every source once, when it starts and when it reloads its config, and serves the files from memory. The bytes resources/read returns are therefore always the bytes the listed digest describes. To publish a change, edit the files and reload (mcpg reloads on SIGHUP, or through the admin API). A host that holds an older entry sees a digest mismatch, calls skills/get and gets the new one.

The limits of the extension are enforced when the source loads: a skill holds at most 512 files and 16 MiB. Loading also refuses:

  • a SKILL.md without frontmatter, a name that breaks the naming rules or differs from its folder name, or no description;
  • a broken symbolic link, or a folder link that loops back (other links are followed, so a Kubernetes ConfigMap or Secret volume works as a source);
  • a SKILL.md at the root of a source (skills live in its subfolders);
  • two sources that serve the same URI.

Names that start with . (.git, .DS_Store) are not served. A file that is in no skill folder (a README.md next to the skill folders) is not served either.

A failure stops the gateway from starting, or keeps the old config on a reload, and names the file and the reason.

Access and audit

Skill files are resources. skills/list, skills/get and resources/directory/read show a caller only the URIs that its resources/read would allow, and every read runs the same pre-dispatch gate and policy chain as any other resource.

mcp.skills.governance is the block every binding takes (minimum_trust, allow_if, required_scopes) and applies to every skill file and folder. Unset minimum_trust takes governance.policy.tool_access.default_minimum_trust, which refuses anonymous callers — so a local stdio server whose client is anonymous sets minimum_trust: unauthenticated. A governance.policy.tool_access rule filed under one URI tightens that URI further; CEL sees the URI as tool_name.

Every read writes an mcpg.resource.read.success (or .denied) audit record, and every skills/list writes mcpg.skill.list.

Digests prove that a file is the one the listing named; they do not prove the content is safe. Serve only skills you reviewed, and remember that a host treats MCP-served skills as untrusted input.

Reference

KeyDefaultMeaning
mcp.skills.sources[].path—A folder of skills. A relative path starts at the last config file's folder. A config with no file (base64:, data:, a URL, a control-plane push) must give an absolute path; the gateway refuses a relative one at boot. The log line skills source loaded names the absolute folder.
mcp.skills.sources[].prefixunsetServed under skill://<prefix>/…. Segments of letters, digits, -, ., _, ~.
mcp.skills.directory_readtrueServe resources/directory/read and declare directoryRead: true.
mcp.skills.list_as_resourcestrueAlso list each SKILL.md in resources/list.
mcp.skills.governanceunsetminimum_trust, allow_if, required_scopes for every skill file and folder.

The examples/25-macos-computer-use config serves six computer-use skills this way.