Chapter 23: Agent Skills – Modular Expert Knowledge & "Skills over MCP"
In Chapter 8, we discussed the problem of tool overload ("Tool Fatigue"). When a model must hold hundreds of tool definitions in its context window simultaneously, token costs, latency, and hallucination rates soar. The most modern and elegant answer to this challenge is Agent Skills: structured knowledge packages that bundle standardized instructions (SKILL.md), optional scripts, and reference documents, loading them into the context strictly when needed according to the principle of Progressive Disclosure.
Since 2025/2026, a powerful two-tier standard ecosystem has emerged:
- The Open Skill File Format (agentskills.io): Originally open-sourced by Anthropic in late 2025, this directory-based format is now supported in a vendor-neutral fashion by Claude Code, Google Gemini CLI, OpenAI Codex, and modern agentic IDEs.
- The Official MCP Extension "Skills over MCP" (
io.modelcontextprotocol/skills): With the MCP protocol specification revision 2026-07-28 and SEP-2640 (finalized on September 13, 2026), the bridge directly into the Model Context Protocol was established. MCP servers can now natively declare skills, announce them over the wire viaskills/list, and deliver their files to local or remote clients through standardizedskill://resources.
1. What Is an Agent Skill?
At its core, a skill is a clearly defined directory encapsulating a single domain or complex task in a modular fashion:
pdf-processing/
├── SKILL.md # Required: YAML frontmatter + instructions
├── FORMS.md # Optional: deep dive at Level 3 (on demand)
├── REFERENCE.md # Optional: API or syntax reference
└── scripts/
└── fill_form.py # Optional executable helper scriptsThe two required fields in the frontmatter are name and description:
---
name: pdf-processing
description: Fills out fillable PDFs, extracts tables, and generates format-compliant reports.
Use for any task involving .pdf files (reading, filling, converting).
---
# PDF Processing
1. Verify the file structure with `qpdf --check`…
2. Forms: use the script `scripts/fill_form.py` …The Fundamental Difference: Tool vs. Skill
A common misconception is confusing tools with skills. They are not competitors; they complement each other:
| Characteristic | MCP Tool | Agent Skill |
|---|---|---|
| Role | Atomic function / API call (the verb) | Procedural knowledge & workflow (the recipe) |
| Wire Protocol | JSON-RPC Request/Response (tools/call) |
Metadata (skills/list) + Content (resources/read) |
| Context Cost | Full schema permanently in the prompt | Only ~100 tokens permanently, remainder on demand |
| Content | Parameter schema (JSON Schema) | Step-by-step instructions, best practices, scripts |
| Invocation | Model emits function call | Model reads instructions and orchestrates existing tools |
In short: Tools are the how-do-I-work (the action surface); skills are the what-do-I-know (the domain knowledge). Tools expand the model's range of actions; skills guide it to execute those actions flawlessly in the correct sequence.
2. Progressive Disclosure – The Core of the Pattern
The governing principle of Agent Skills is Progressive Disclosure. Rather than dumping all reference material into the context window upfront, context is revealed progressively in three levels:
- Level 1 – Metadata (Always Loaded):
At startup, the host only loads the skill's identity (
nameanddescription) into the system prompt. Token cost: Around 50–100 tokens per skill. 50 installed skills occupy fewer than 4,000 tokens. - Level 2 – Instructions / Workflow (Loaded on Trigger):
When the model identifies that a skill matches the user's intent, it fetches the main body of
SKILL.md. Token cost: Around 1,000–4,000 tokens—and strictly for the specific task at hand. - Level 3+ – Specific Assets & Scripts (On-Demand Access):
Supporting files such as
FORMS.mdor scripts underscripts/are only fetched or executed when a specific step requires them. The output of a script enters the context, not necessarily the script's raw code.
3. Two Distribution Channels: File System vs. "Skills over MCP"
How do skills reach the agent? The ecosystem provides two complementary paths:
┌──────────────────────┐
│ Agent / Client │
└──────────┬───────────┘
┌──────────────────────┴─────────┐
▼ ▼
Path A: local Path B: MCP protocol
~/.agents/skills/<name>/ io.modelcontextprotocol/skills
(SEP-2640)
① startup scan ① skills/list
② read_file(SKILL.md) ② resources/read(skill://…)
③ local execution ③ resources/read(skill://…)Path A: Local File System Skills (`.agents/skills/`)
For local coding agents (Claude Code, Gemini CLI, OpenAI Codex), skills live directly on disk:
- Global:
~/.agents/skills/<skill-name>/(or vendor-specific aliases like~/.claude/skills/) - Project-specific:
.agents/skills/<skill-name>/in the repository root.
The client scans these directories at startup, injects the metadata into the system prompt, and reads files on demand using native file tools (read_file, cat).
Path B: Protocol-Based via "Skills over MCP" (`io.modelcontextprotocol/skills` / SEP-2640)
When operating an MCP server—especially a remote server over Streamable HTTP—the remote client does not have access to the server's local file system. This is where SEP-2640 steps in:
Capability Declaration: During the initialization handshake, the server advertises its skill capability:
{ "capabilities": { "io.modelcontextprotocol/skills": {} } }Discovery via
skills/list: The client queries available skills provided by the server:// Request { "jsonrpc": "2.0", "id": 1, "method": "skills/list", "params": {} } // Response { "jsonrpc": "2.0", "id": 1, "result": { "skills": [ { "name": "pdf-processing", "description": "Fills out fillable PDFs, extracts tables, and generates reports...", "uri": "skill://pdf-processing", "files": [ { "path": "SKILL.md", "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, { "path": "scripts/fill_form.py", "digest": "sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069" } ] } ] } }Advantage: The client immediately receives Level 1 metadata (name + description) along with a file manifest containing cryptographic digests (SHA-256 hashes) for all files.
Content Delivery via MCP Resources (
skill://URI): Rather than introducing a redundant transfer mechanism, SEP-2640 leverages the standard Resources primitive (resources/read). When the LLM requests the content ofSKILL.md, the client retrieves it via the standardizedskill://URI scheme:// Request { "jsonrpc": "2.0", "id": 2, "method": "resources/read", "params": { "uri": "skill://pdf-processing/SKILL.md" } } // Response { "jsonrpc": "2.0", "id": 2, "result": { "contents": [ { "uri": "skill://pdf-processing/SKILL.md", "mimeType": "text/markdown", "text": "---\nname: pdf-processing\n...\n# PDF Processing Guide\n..." } ] } }
Thanks to the cryptographic digests provided in Step 2, the host can verify that the delivered files have not been tampered with and match the approved version.
4. Implementation Example in Go
Below is a concise example showing how an MCP server implemented with the official Go SDK registers the io.modelcontextprotocol/skills extension:
package main
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"strings"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type SkillFileManifest struct {
Path string `json:"path"`
Digest string `json:"digest"`
}
type SkillEntry struct {
Name string `json:"name"`
Description string `json:"description"`
URI string `json:"uri"`
Files []SkillFileManifest `json:"files"`
}
func RegisterSkillsExtension(server *mcp.Server, skillsDir string) {
// 1. Declare capability for skills
server.AddCapability("io.modelcontextprotocol/skills", map[string]any{})
// 2. Register handler for skills/list (Level 1 Discovery)
server.RegisterMethod("skills/list", func(ctx context.Context, req mcp.Request) (any, error) {
// Read SKILL.md frontmatter and compute SHA-256 hash
content, _ := os.ReadFile(skillsDir + "/pdf-processing/SKILL.md")
hash := sha256.Sum256(content)
return map[string]any{
"skills": []SkillEntry{
{
Name: "pdf-processing",
Description: "Fills out fillable PDFs, extracts tables, and generates reports...",
URI: "skill://pdf-processing",
Files: []SkillFileManifest{
{Path: "SKILL.md", Digest: "sha256:" + hex.EncodeToString(hash[:])},
},
},
},
}, nil
})
// 3. Register resource handler for the skill:// URI scheme (Level 2 & 3 Delivery)
server.RegisterResourceHandler("skill", func(ctx context.Context, uri string) (*mcp.ResourceContent, error) {
// Example URI: skill://pdf-processing/SKILL.md
relPath := strings.TrimPrefix(uri, "skill://")
filePath := fmt.Sprintf("%s/%s", skillsDir, relPath)
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("skill file not found: %w", err)
}
return &mcp.ResourceContent{
URI: uri,
MIMEType: "text/markdown",
Text: string(data),
}, nil
})
}5. Skills vs. MCP Tools: The Perfect Symbiosis
In production, a skill provides the procedural playbook for the tools exposed by the MCP server:
MCP Server "Document Suite"
├── Tools (Action Surface):
│ ├── convert_to_pdf(input_path)
│ ├── extract_tables(pdf_path)
│ └── sign_document(pdf_path, cert_id)
│
└── Skills (Orchestration & Domain Knowledge):
└── skill://contract-review/
├── SKILL.md 1. Call convert_to_pdf.
│ 2. Extract clauses.
│ 3. If approved, sign_document.
└── templates/compliance_checklist.mdWithout skills, developers must constantly overload system prompts explaining the valid combinations and sequences of tools. With Skills over MCP, the server ships the tools alongside the exact operational procedures required to use them safely.
6. Architectural Patterns for Skill Activation
How does an agent decide when to load a skill? In practice, four patterns are common:
A. Explicit Trigger (Slash Command)
The user types /security. The client immediately injects SKILL.md into the context without model hesitation. Zero token cost for discovery, but requires user awareness of available skills.
B. Router Chain (Classification Pre-Stage)
A lightweight model (e.g. Flash or Haiku) inspects the prompt and directs the primary model: "Activate skill security-audit". Significantly minimizes false activations at low inference cost.
C. Agentic Self-Activation (Default)
The primary model compares the user query with the Level 1 skill descriptions (skills/list or prompt metadata) and autonomously initiates fetching SKILL.md. This is the most flexible approach, but relies heavily on high-quality metadata.
D. Environment Trigger (Context Pre-Filter)
The client inspects workspace files prior to startup: finding go.mod enables the Go skill; finding package.json activates the Node skill. This prunes Level 1 token overhead in large codebases.
7. Metadata Quality: The Discovery Problem
The description is the cornerstone of effective skill discovery. Because the model inspects only this text during Level 1, it determines whether the skill is utilized:
- Poor: "Helps with Go programming."
$\rightarrow$ Far too generic; the model cannot distinguish it from other tools. - Good: "Expert for Go backend services using the Gin framework. Specialized in API design, JWT authentication, and SQL query tuning; adheres to chi-mw routing conventions."
$\rightarrow$ Specifies the domain, trigger keywords, and differentiation.
4 Golden Rules for Skill Descriptions:
- Be Concrete: Never use vague descriptions like "code helper".
- Include Trigger Keywords: Include 3–5 terms likely to appear in user prompts (file extensions, framework names, CLI commands).
- Differentiate Clearly: State the contrast with related skills ("For Go web APIs; for CLI utilities use go-cli").
- Stay Concise: Keep descriptions under ~250 characters to minimize Level 1 token usage.
8. Practical Example: A Complete Security Audit Skill
Here is a complete, production-ready skill:
security-audit/
├── SKILL.md
├── templates/report.md
└── scripts/owasp_scan.py`security-audit/SKILL.md`
---
name: security-audit
description: Audits source code for OWASP Top 10 vulnerabilities (SQLi, XSS, CSRF)
and produces a Markdown report. Use for code reviews or when the user
requests "security" or "security audit".
---
# Security Audit
## Step 0: Scope Clarification
1. Identify the target directory; use `glob_files` to narrow down candidates.
2. Load the report template from `templates/report.md`.
## Step 1-3: Security Checks
1. Search for unsafe SQL string concatenations:
`grep -rEn "(SELECT|INSERT).*\+"`
2. Run the local audit script:
`python3 scripts/owasp_scan.py --path .`
3. Compare findings against the whitelist in `.audit-whitelist`.
## Output Requirements
* Write findings to `reports/audit-<YYYY-MM-DD>.md`.
* Never print cleartext credentials or sensitive tokens into the conversation history.9. Common Errors & Pitfalls
| Error | Consequence | Solution |
|---|---|---|
Generic description |
Skill is never or mistakenly triggered | Add trigger keywords, domain scope, and differentiation |
SKILL.md too large (> 50 KB) |
High token cost at Level 2, model loses focus | Extract reference details to REFERENCE.md or templates |
Missing digests in skills/list |
Remote host rejects skill for security reasons | Provide SHA-256 digests for all declared files in the manifest |
Unresolvable skill:// URIs |
Resource read fails, terminating the workflow | Ensure the server's resource handler fully supports the skill scheme |
| Scripts with network dependencies | Fails in isolated sandboxes or containers | Ensure scripts are hermetic and run without undeclared network calls |
10. Checklist: Is Your Skill Production-Ready?
Before releasing a skill in a repository or via an MCP server, verify the following checklist:
- Frontmatter Validated:
nameand precisedescription(under 250 characters) are present? - Progressive Disclosure Applied:
SKILL.mdkept concise; deep details moved to supporting files? - MCP Compatibility: If served over MCP,
io.modelcontextprotocol/skillscapability declared andskill://resources accessible? - Integrity Guaranteed: SHA-256 digests provided for every file in the manifest?
- Security Audited: No embedded secrets, API keys, or unsafe
curl | bashpatterns in scripts? - Tested with Prompts: Verified with representative prompts that the model correctly triggers Level 1 and fetches Level 2?
Conclusion
Agent Skills solve the root cause of Tool Fatigue: they cleanly separate the action surface (MCP tools) from procedural knowledge (skills). Through Progressive Disclosure, the context window stays free of unnecessary overhead.
With the SEP-2640 specification ("Skills over MCP"), this pattern is now fully unified with the protocol: MCP servers can distribute complete, remote-ready expert workflows via skills/list and skill:// directly to AI agents.
← Chapter 22: Choosing the Right Programming Language | Table of Contents | Next Chapter: Glossary →
Copyright Michael Lechner – 2026-09-27 (Updated for MCP Revision 2026-07-28 & SEP-2640 Skills over MCP)