Skip to main content
Glama
AutomateIP

skills-mcp-server

by AutomateIP

skills-mcp-server

An MCP server, built with FastMCP, that serves a directory of SKILL.md-based "skills" over the network — as both MCP resources and MCP tools.

How it works

  • Resources come from FastMCP's built-in fastmcp.server.providers.skills.SkillsDirectoryProvider. It scans a root directory, and treats every subdirectory containing a SKILL.md file as one skill, exposing:

    • skill://{name}/SKILL.md — the skill's main file

    • skill://{name}/_manifest — a synthetic JSON file listing (path/size/hash) for the skill

    • skill://{name}/{relative/path} — every other file in the skill folder (e.g. references/*.md)

    This server configures the provider with supporting_files="resources", so every supporting file is individually listed by list_resources() up front (not hidden behind a lazy URI template) — good for upfront discoverability by clients that just call list_resources() once.

  • Tools (list_skills, get_skill, get_skill_reference) are hand-written on top of the same skills directory, since the provider only exposes resources, not tools. They parse full YAML frontmatter (interface: blocks, nested lists, etc.) rather than relying on the provider's internal simplified line-based frontmatter parser, so nested metadata (e.g. risk_level, confirmation_points) comes through correctly.

    • list_skills() → name, description, argument hint, risk level, and path for every skill.

    • get_skill(name) → full parsed frontmatter, the markdown body, and a list of that skill's supporting reference files.

    • get_skill_reference(skill_name, path) → the text content and MIME type of one reference file within a skill (e.g. references/infoblox/procedure.md), for clients that want a specific reference file's content without walking list_resources(). path must be one of the paths in that skill's reference_files list; anything else (including path traversal attempts) is rejected.

  • Transport: streamable-HTTP (mcp.run(transport="http", ...)), since this is meant to run in a container and be reached by remote MCP clients rather than over stdio.

  • Skills directory is never baked into the image. The Dockerfile only ships the server code; the actual skills live on the host and are mounted as a read-only volume at container runtime (docker-compose.yml mounts them to /skills). Swap in a different skills directory by changing the compose volume mount or the SKILLS_DIR env var — no rebuild needed.

Related MCP server: skills-mcp-server

Configuration

All configuration is via environment variables (see .env.example):

Variable

Default

Meaning

SKILLS_DIRS

(unset)

Multiple named skill sources — see Multiple skills directories below. Takes precedence over SKILLS_DIR if both are set.

SKILLS_DIR

/skills

Legacy single-directory config. Root directory to scan for skill folders. Still fully supported for backward compatibility when SKILLS_DIRS is unset.

MCP_HOST

0.0.0.0

Host/interface the HTTP transport binds to

MCP_PORT

8010

Port the HTTP transport binds to

SKILLS_RELOAD

true

If true, re-scan the skills directory on every request. Default on since an external process (e.g. a cron job re-cloning a skills repo into the mounted volume) may mutate content behind the server's back. Set false for a static skills directory to skip re-scan overhead.

SKILLS_SUPPORTING_FILES

resources

resources (list every file upfront) or template (lazy URI template)

LOG_LEVEL

INFO

Standard Python logging level

A GET /health route is also registered for container/orchestrator liveness checks. It reports every configured skill source by name.

Multiple skills directories

This server can merge more than one skills collection behind a single MCP endpoint. Configure SKILLS_DIRS as a comma-separated list of name=path pairs, one per source:

SKILLS_DIRS=agent-skills=/skills/agent-skills,automation-skills=/skills/automation-skills

Each source is identified by an explicit, stable name — this is what drives collision resolution (below), not the order the sources are listed in.

Collision policy: agent-skills always wins. If the same skill directory name exists under more than one configured source, the source literally named agent-skills always wins, regardless of where it appears in SKILLS_DIRS. This is enforced by an explicit name check (source.name == "agent-skills") in discovery._resolve_skill_dirs, not by "whichever directory happens to be scanned first" — reordering SKILLS_DIRS in your .env file later will not silently flip precedence. If neither colliding source is named agent-skills, the first-configured source wins (list order only matters as a tiebreaker in that case).

Every time a collision is resolved, the server logs a WARNING naming the skill, every source that defines it, the winning source, and the shadowed source(s) — so shadowing is never silent. This applies identically to both the tool layer (list_skills/get_skill/get_skill_reference) and the resource layer (skill://... URIs): a shadowed source's own files are never served under the winning skill's name, even if you ask for one of its supporting files by path directly.

SKILLS_DIR (singular) keeps working unchanged if you only need one source — it's treated as one source named "default". If both SKILLS_DIRS and SKILLS_DIR are set, SKILLS_DIRS wins and SKILLS_DIR is ignored.

Local development quickstart

Requires Python 3.11+ and uv.

uv venv .venv
source .venv/bin/activate
uv pip install -e .

# Point at any skills directory you like — this repo bundles a minimal
# example under ./example-skills:
export SKILLS_DIR=./example-skills
skills-mcp-server
# -> Starting MCP server 'Skills MCP Server' with transport 'http' on http://0.0.0.0:8010/mcp

(pip install -e . in a plain venv works too, if you'd rather not use uv.)

To run locally against a real, richer skills set instead of the bundled example (e.g. for manual testing), just point SKILLS_DIR elsewhere — nothing else needs to change:

export SKILLS_DIR=/path/to/agent-skills/skills
skills-mcp-server

This only affects your local shell session — it does not touch docker-compose.yml, the project's .env.example default, or the test suite (which always uses its own fixtures under tests/fixtures/, independent of both example-skills/ and whatever SKILLS_DIR you export).

Quick smoke test with the FastMCP client

import asyncio
from fastmcp import Client

async def main():
    async with Client("http://127.0.0.1:8010/mcp") as client:
        tools = await client.list_tools()
        print([t.name for t in tools])

        resources = await client.list_resources()
        print([str(r.uri) for r in resources])

        result = await client.call_tool("list_skills", {})
        print(result.data)

asyncio.run(main())

Or with curl against the health route:

curl http://127.0.0.1:8010/health
# {"status": "ok", "skill_sources": {"default": "./example-skills"}}

Docker quickstart

docker compose up --build

This builds the image (server code only — no skills baked in), mounts the bundled ./example-skills directory from the repo to /skills inside the container read-only, and serves on http://localhost:8010/mcp. No configuration is required for this to work on a fresh clone.

Pointing at a different skills directory

Copy docker-compose.override.yml.example to docker-compose.override.yml (untracked — see .gitignore) and edit the path:

services:
  skills-mcp-server:
    volumes:
      - /path/to/your/skills:/skills:ro

Compose automatically merges docker-compose.override.yml over docker-compose.yml, so your own skills directory is used without editing the tracked file.

Or, if running the container directly instead of via compose:

docker run --rm -p 8010:8010 \
  -v /path/to/your/skills:/skills:ro \
  -e SKILLS_DIR=/skills \
  skills-mcp-server:local

Connecting an MCP client

Any MCP client that supports streamable-HTTP transport can connect directly to http://<host>:8010/mcp. Example generic client config:

{
  "mcpServers": {
    "skills": {
      "url": "http://localhost:8010/mcp",
      "transport": "http"
    }
  }
}

Project layout

skills-mcp-server/
├── pyproject.toml
├── Dockerfile
├── docker-compose.yml
├── docker-compose.override.yml.example  # template for pointing at your own skills dir
├── .env.example
├── .gitignore
├── LICENSE
├── README.md
├── example-skills/            # bundled fixture so the server works out of the box
│   └── hello-world/
│       ├── SKILL.md
│       └── references/
│           └── greeting-styles.md
├── tests/                     # pytest suite — uses its own fixtures/, never example-skills/
│   ├── conftest.py
│   ├── fixtures/skills/
│   ├── fixtures/multi/        # two-source fixtures for collision-policy tests
│   ├── test_config.py
│   ├── test_discovery.py
│   ├── test_server.py
│   └── test_tools.py
└── src/skills_mcp_server/
    ├── __init__.py
    ├── config.py      # env-driven Settings; SKILLS_DIRS / SKILLS_DIR parsing, SkillSource
    ├── discovery.py    # full YAML frontmatter parsing, multi-source merge + collision policy
    ├── tools.py        # list_skills / get_skill / get_skill_reference MCP tool definitions
    └── server.py       # FastMCP app wiring, multi-provider root ordering, health route, entrypoint

Running tests

uv pip install -e ".[dev]" --python .venv/bin/python
.venv/bin/python -m pytest

License

Apache License 2.0 — see LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Converts AI Skills (following Claude Skills format) into MCP server resources, enabling LLM applications to discover, access, and utilize self-contained skill directories through the Model Context Protocol. Provides tools to list available skills, retrieve skill details and content, and read supporting files with security protections.
    3
    28
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    A minimal MCP server that resolves skill names to filesystem paths + metadata, enabling on-demand skill retrieval without loading full content into context.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    MCP server that serves agent skills (SKILL.md bundles) to any MCP client, exposing them via tools and resources for tool-oriented and resource-aware clients.
    3
    13 npm
    ISC