Skip to main content
Glama
janacibron

agentskills-mcp

by janacibron

agentskills-modelcontextprotocol

An MCP (Model Context Protocol) server that exposes a catalog of "skills" to LLM agents. Each skill is a directory containing a SKILL.md (YAML frontmatter + Markdown body) plus optional references/, scripts/, and assets/ subdirectories. The server lets an LLM discover which skills exist, fetch their contents on demand, and read their supporting files.

Built on FastMCP 2.x. Designed for the Claude Desktop, VS Code, and custom MCP client ecosystem.


Architecture

MCP Client (LLM Agent)
    │  MCP Protocol (stdio / streamable-http)
    ▼
create_mcp_server()  ← Factory entry
    │  registers tools · mounts resources · wires SkillRegistry
    ▼
┌─────────────────────────────────────────────┐
│  Server Surface                              │
│  • 10 Tools   (LLM-invoked, on-demand)       │
│  • 3 Resources (URI-addressable context)     │
└─────────────────────────────────────────────┘
    │  delegates lookups
    ▼
SkillRegistry  ← validates frontmatter · LRU cache by (skill_id, mtime)
    │  resolves via abstract interface
    ▼
SkillProvider (interface)  ← list() · read() · stat() · list_subdir() · read_subfile()
    │
    ├─ LocalFileSystemSkillProvider  (reads ./skills)
    └─ GitSkillProvider              (future — pulls from remote git repo)
    │
    ▼
Filesystem (./skills/<skill_id>/)

See mcp-server-architecture.png for the full visual, or mcp-architecture.mmd for the editable Mermaid source (renders natively on GitHub).


Related MCP server: capalith

Project structure

.
├── mcp_server.py              # FastMCP wiring: create_mcp_server(), all 10 tools, 3 resources, CLI entry point
├── skill_provider.py          # SkillProvider ABC + LocalFileSystemSkillProvider + GitSkillProvider stub
├── discovery_tools_spec.md    # Spec for list_skills() / search_skills() + frontmatter schema
├── pyproject.toml             # Package metadata, deps, entry point, tooling config (ruff/mypy/pytest)
├── requirements.txt           # Runtime deps (pip install -r)
├── requirements-dev.txt       # Dev/test deps (pytest, ruff, mypy, ipython)
├── mcp-server-architecture.png  # Polished architecture diagram (Playwright + CSS)
├── mcp-architecture.mmd       # Same diagram as Mermaid source (for README embedding)
├── mcp-architecture.mermaid.png  # Mermaid rendering (tall/narrow — prefer the .png above)
└── README.md                  # This file

Installation

cd /path/to/agentskills-modelcontextprotocol
pip install -e .            # editable install — pick up code changes automatically
pip install -e '.[dev]'     # also installs pytest, ruff, mypy, ipython

This registers the agentskills-mcp command-line entry point (see Usage below).

Option B — Plain requirements.txt

pip install -r requirements.txt          # runtime deps only
pip install -r requirements-dev.txt       # add dev/test tools

Runtime dependencies

Package

Version constraint

Why

fastmcp

>=2.14,<3.0

MCP server framework

pydantic

>=2.10,<3.0

Response schemas (SkillSummary, ListSkillsResult, etc.)

pyyaml

>=6.0,<7.0

Frontmatter parsing in skill_provider.parse_frontmatter()

Python 3.10+ required (uses slots=True dataclasses and from __future__ import annotations).


Quick start

Via the agentskills-mcp CLI (after pip install .)

agentskills-mcp                                       # defaults: ./skills, stdio transport
agentskills-mcp --skills-root /opt/skills
agentskills-mcp --transport streamable-http --port 8080
agentskills-mcp --smoke-test                          # run in-process smoke test

Via the Python API

from mcp_server import create_mcp_server

mcp = create_mcp_server(skills_root="./skills")
mcp.run()  # stdio transport (default — for Claude Desktop, VS Code)

For HTTP transport:

mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)

Smoke tests

agentskills-mcp --smoke-test       # via CLI entry point
python mcp_server.py                # directly via __main__
python skill_provider.py            # provider-level test (validation, path traversal, etc.)

Skill directory convention

Each skill is a subdirectory of ./skills/ (or wherever skills_root points):

incident-response/
├── SKILL.md          ← YAML frontmatter + Markdown body
├── references/       ← served by get_skill_reference()
│   └── checklist.md
├── scripts/          ← served by get_skill_script()
│   └── run.py
└── assets/           ← served by get_skill_asset()
    └── template.json

Frontmatter schema

Required fields:

Field

Type

Purpose

name

str

Human-readable name (not unique; skill_id is the directory name)

version

str

Semver-ish; used for cache invalidation

description

str

One-liner shown in list_skills() output

Optional fields:

Field

Type

Default

Purpose

tags

list[str]

[]

Categorical labels; indexed by search_skills()

author

str

""

Attribution

license

str

""

SPDX identifier or custom

min_mcp_version

str

""

Semver constraint on the MCP server version

deprecated

bool

false

If true, hidden from list_skills() by default

replaces

str

""

skill_id this skill supersedes

Example SKILL.md:

---
name: Incident Response
version: 1.2.0
description: Runbooks for on-call incident handling
tags: [ops, sre, on-call]
author: SRE Team
license: Apache-2.0
---

# Incident Response

Follow the runbook for your incident severity...

Skills with missing/invalid frontmatter are skipped at registration time (logged via the on_warning callback, not raised) — one bad skill won't break the catalog.


Tools (10)

Discovery (NEW in this refinement)

Tool

Description

list_skills(include_deprecated=False, tag=None)

Return the full catalog. Call this first when you don't know which skill applies.

search_skills(query, include_deprecated=False, limit=10)

Keyword search across skill_id, name, tags, description. Returns ranked matches with relevance scores.

Access

Tool

Description

get_skill_metadata(skill_id)

Frontmatter only — cheap.

get_skill_body(skill_id)

Full SKILL.md content (frontmatter + markdown). Cached by file mtime.

list_skill_references(skill_id)

List files in the skill's references/ directory.

get_skill_reference(skill_id, name)

Read a specific reference file.

list_skill_scripts(skill_id)

List files in scripts/.

get_skill_script(skill_id, name)

Read a specific script file.

list_skill_assets(skill_id)

List files in assets/.

get_skill_asset(skill_id, name)

Read a specific asset file.

Typical LLM flow

  1. list_skills() — see what's available

  2. If unclear which fits, search_skills(user_intent) — narrow down

  3. get_skill_body(chosen_skill_id) — load full instructions

  4. Follow the skill's instructions, calling get_skill_reference/script/asset as needed


Resources (3)

Resources are URI-addressable context that the client decides when to fetch (unlike tools, which the LLM invokes).

URI

MIME type

Description

skills://catalog/markdown

text/markdown

Human-readable catalog; one section per skill.

skills://catalog/xml

application/xml

Machine-readable catalog; same fields as list_skills().

skills://tools-usage-instructions

text/markdown

Condensed guidance for the LLM on when to call which tool.

Why both tools and resources? Different MCP clients behave differently. Claude Desktop exposes resources as attachable context; other clients may auto-prefetch. Tools work everywhere. Providing both ensures the discovery layer is reachable regardless of client behavior.


Search scoring algorithm

search_skills(query) uses multi-field scoring (no embeddings required for v1):

Field

Weight

Match strategy

skill_id

3.0

Exact = 3.0, prefix = 1.5, substring = 0.5

name

2.0

Exact = 2.0, substring = 0.5

tags

2.0

Exact tag match = 2.0 per tag

description

1.0

Word-level substring = 0.2 per word

Query is tokenized on whitespace; each token scored independently and summed. Case-insensitive. Results with score 0 are excluded. Ties broken alphabetically by skill_id.


Caching

SkillRegistry maintains an LRU cache (max 256 entries) keyed by (skill_id, mtime):

  • get_skill_body(skill_id) first calls provider.stat() to get current mtime

  • If the cache has an entry for (skill_id, mtime), it's returned without re-reading the file

  • If the file changed on disk (different mtime), the cache entry is invalidated and the file is re-read

This means repeated calls to get_skill_body are essentially free, and editing a SKILL.md on disk is reflected on the next call without server restart.


Security

Path traversal is rejected at the provider layer:

  • skill_id must not contain /, \, or be ./../empty/hidden

  • name (within references/ etc.) must be a single file segment

  • Invalid IDs return None (no file access attempted)

Example:

>>> get_skill_body("../../etc/passwd")
None

Extending

Adding a new skill

  1. Create ./skills/<skill_id>/SKILL.md with required frontmatter

  2. Optionally add references/, scripts/, assets/ subdirectories

  3. Restart the server (or call get_skill_body again — the cache invalidates on mtime change)

Adding a new provider

Implement the SkillProvider ABC (5 methods: list, stat, read, list_subdir, read_subfile), then pass it to create_mcp_server(provider=YourProvider()). The registry and all 10 tools work unchanged.

The GitSkillProvider stub in skill_provider.py shows the implementation plan for a future git-backed provider (shallow clones, atomic checkout swap, per-skill version pinning via git refs).


Roadmap

  • GitSkillProvider — pull skills from a remote git repo (shared team catalogs, version pinning)

  • search_skills_semantic() — embeddings-based semantic search (companion to the keyword tool)

  • Pagination for list_skills() once catalogs exceed ~500 skills

  • Structured logging integration (replace default print warning callback)

  • Integration tests for cache invalidation (modify SKILL.md between calls)

  • CLI tool for skill scaffolding (new-skill <name> creates the directory + frontmatter template)

Open questions are tracked in section 7 of discovery_tools_spec.md.


License

TBD — set in your own project's LICENSE file.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to search and retrieve over 89K skills on-demand at runtime, eliminating the need to manually install skills upfront.
    25 PyPI
    134
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables read-only access to a local-first catalog of portable agent skills, exposing tools to discover ranked matches, inspect stored artifacts, traverse declared relationships, and view configuration.
    5
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to discover, search, and load agent skills from many sources, including other agents' skill directories, Claude Code plugin marketplaces, and remote git repositories, with no copying or syncing required.
    0
    6
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to install compatible SKILL.md skills from Git repositories or local directories into a persistent registry, then discover them through search, progressive loading, and asset reads. It exposes only user-selected hot skills as individual activation tools, keeping execution and trust decisions under explicit operator control.
    MIT