The MCP Handbook

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:

  1. 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.
  2. 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 via skills/list, and deliver their files to local or remote clients through standardized skill:// 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 scripts

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

  1. Level 1 – Metadata (Always Loaded): At startup, the host only loads the skill's identity (name and description) into the system prompt. Token cost: Around 50–100 tokens per skill. 50 installed skills occupy fewer than 4,000 tokens.
  2. 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.
  3. Level 3+ – Specific Assets & Scripts (On-Demand Access): Supporting files such as FORMS.md or scripts under scripts/ 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:

  1. Capability Declaration: During the initialization handshake, the server advertises its skill capability:

    {
      "capabilities": {
        "io.modelcontextprotocol/skills": {}
      }
    }
  2. 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.

  3. 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 of SKILL.md, the client retrieves it via the standardized skill:// 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.md

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

  1. Be Concrete: Never use vague descriptions like "code helper".
  2. Include Trigger Keywords: Include 3–5 terms likely to appear in user prompts (file extensions, framework names, CLI commands).
  3. Differentiate Clearly: State the contrast with related skills ("For Go web APIs; for CLI utilities use go-cli").
  4. 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: name and precise description (under 250 characters) are present?
  • Progressive Disclosure Applied: SKILL.md kept concise; deep details moved to supporting files?
  • MCP Compatibility: If served over MCP, io.modelcontextprotocol/skills capability declared and skill:// resources accessible?
  • Integrity Guaranteed: SHA-256 digests provided for every file in the manifest?
  • Security Audited: No embedded secrets, API keys, or unsafe curl | bash patterns 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)

Licence: CC BY-NC 4.0