skills-mcp
An MCP server that lets AI agents browse and fetch skills from remote registries — GitHub repositories or direct HTTP URLs — without installing them locally first.
README
skills-mcp
An MCP server that lets AI agents browse and fetch skills from remote registries — GitHub repositories or direct HTTP URLs — without installing them locally first.
Features
- GitHub registry — multi-skill repos where each subdirectory (at any nesting depth) is a skill; ref-locked to a branch, tag, or SHA; supports public and private repos
- HTTP registry — a single direct URL pointing at a
SKILL.mdfile - Read-through disk cache — immutable SHA refs cache forever; branch/tag refs use a configurable TTL (default 1 hour)
- Flexible authentication — GitHub Personal Access Token (env-var),
ghCLI token (cached in-process), HTTP Bearer or Basic auth - Path traversal protection — companion file paths are validated against the skill root
- Graceful shutdown — cooperative SIGTERM handler with 1-second watchdog (no hung stdio readers)
Installation
Requires Python ≥ 3.11 and uv.
git clone https://github.com/afriemann/skills-mcp ~/git/skills-mcp
cd ~/git/skills-mcp
uv sync
Running the server
uv run --project ~/git/skills-mcp skills-mcp
# or with a custom config path:
uv run --project ~/git/skills-mcp skills-mcp --config /path/to/config.jsonc
# or with verbose logging:
uv run --project ~/git/skills-mcp skills-mcp --log-level INFO
Configuration
The server reads a JSONC file from the platform config directory:
| Platform | Default path |
|---|---|
| Linux | $XDG_CONFIG_HOME/skills-mcp/config.jsonc (default: ~/.config/skills-mcp/config.jsonc) |
| macOS | ~/Library/Application Support/skills-mcp/config.jsonc |
| Windows | %APPDATA%\skills-mcp\config.jsonc |
Override with --config PATH or the XDG_CONFIG_HOME env var.
Example config.jsonc
{
"registries": {
// GitHub registry — multi-skill repo, locked to a SHA (immutable cache)
"my-skills": {
"type": "github",
"owner": "myorg",
"repo": "agent-skills",
"skills_dir": "skills", // root of the skills tree; skills may be nested at any depth
"ref": "a1b2c3d", // branch, tag, or commit SHA
"description": "Engineering and workflow skills for AI agents", // optional — shown in list_registries
"auth": {
"type": "github_token",
"env_var": "GITHUB_TOKEN" // env var name — NOT the token value
}
},
// GitHub registry with gh CLI auth (no token needed if already logged in)
"public-skills": {
"type": "github",
"owner": "someorg",
"repo": "public-skills",
"skills_dir": "", // empty string = repo root
"ref": "main",
"auth": { "type": "gh_cli" }
},
// HTTP registry — a single SKILL.md at a direct URL
"custom-skill": {
"type": "http",
"url": "https://example.com/skills/my-skill/SKILL.md",
"skill_name": "my-skill",
"auth": {
"type": "bearer",
"env_var": "MY_SKILL_TOKEN" // env var name — NOT the token value
}
}
},
// Optional cache configuration
"cache": {
"enabled": true,
"ttl_seconds": 3600, // for branch/tag refs; SHA refs never expire
"dir": "~/.cache/skills-mcp" // override the default cache directory
}
}
Auth types
| Type | Registry | Description |
|---|---|---|
none (default) |
both | No authentication |
github_token |
GitHub | PAT from the named env var (env_var) |
gh_cli |
GitHub | Token from gh auth token (cached in-process; graceful fallback to no-auth if gh is absent or fails) |
bearer |
HTTP | Authorization: Bearer <token> from the named env var (env_var) |
basic |
HTTP | HTTP Basic from username_env_var and password_env_var |
Security: config values contain env var names only — secret values are never written to the config file.
GitHub repository layout
Skills can be placed at any nesting depth under skills_dir. A directory is treated as a skill
when it contains a SKILL.md file; directories without one (e.g. category folders) are skipped.
Skill names returned by list_skills are slash-delimited paths relative to skills_dir.
Flat layout (one level deep):
agent-skills/
└── skills/
├── my-skill/
│ ├── SKILL.md
│ └── references/
│ └── guide.md
└── another-skill/
└── SKILL.md
With skills_dir: "skills", list_skills returns ["another-skill", "my-skill"].
Nested layout (multiple levels):
agent-skills/
└── skills/
├── engineering/
│ └── testing/
│ └── tdd-development/
│ └── SKILL.md
└── business/
└── brainstorming/
└── SKILL.md
With skills_dir: "skills", list_skills returns
["business/brainstorming", "engineering/testing/tdd-development"].
Use the full slash-delimited path as the skill argument to get_skill.
MCP Tools
| Tool | Parameters | Returns |
|---|---|---|
list_registries |
— | JSON array: [{name, type, ref?, description?}] |
list_skills |
registry |
JSON array: ["skill-a", "skill-b"] |
get_skill |
registry, skill, file? |
Without file: JSON {content, files}; with file: raw companion file text |
file is an optional parameter on get_skill. Pass a companion file path from the files array (e.g. "references/guide.md") to fetch that file directly. Percent-encoded slashes are decoded automatically (e.g. "references%2Fguide.md"). Companion file access is only supported for GitHub registries; path traversal attempts are rejected.
MCP Resource Template
Skills and their companion files are also accessible as MCP resources via URI — useful for hosts that support read_resource directly:
skill://{registry}/{skill_path} → SKILL.md raw text
skill://{registry}/{skill_path}?file=references%2Fguide.md → companion file raw text
Discover the template with list_resource_templates. list_resources returns empty (no static registrations).
Error handling
Each tool uses a consistent error format for its response type:
- Model-recoverable errors (skill not found, unknown registry, unsupported operation, path traversal) — FastMCP marks the result
is_error=True; the agent reads the message and may retry with corrected arguments. - Infrastructure failures (registry unreachable, auth failure, rate-limited) — caught at the tool boundary and returned as a per-tool error value (
{"error": "…"}JSON forlist_skills;{"error": "…"}JSON or plain"Error: …"string forget_skilldepending on whetherfilewas provided). The server and other registries continue serving normally — one registry failure does not cascade.
Caching
Cached files live at:
| Platform | Default path |
|---|---|
| Linux | $XDG_CACHE_HOME/skills-mcp/ (default: ~/.cache/skills-mcp/) |
| macOS | ~/Library/Caches/skills-mcp/ |
| Windows | %LOCALAPPDATA%\skills-mcp\Cache |
The cache directory is created with mode 0700. Files are written atomically (temp + rename). Only successful fetches are cached.
TTL rules:
- SHA refs → immutable, cached indefinitely
- Branch/tag refs → expire after
cache.ttl_seconds(default 3600 s)
Set cache_enabled: false on a registry to disable caching for that registry. Set cache.enabled: false globally to disable entirely.
Integration with opencode
skills-mcp is a standard stdio MCP server and can be wired up in any MCP-compatible host.
opencode
Add to your opencode.jsonc (or ~/.config/opencode/opencode.jsonc for global access):
"mcp": {
"skills-mcp": {
"type": "local",
"command": [
"uv",
"run",
"--project",
"{env:HOME}/git/skills-mcp",
"skills-mcp"
],
"enabled": true
}
}
Tools are exposed as skills_mcp_list_registries, skills_mcp_list_skills, and skills_mcp_get_skill in opencode's permission system.
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"skills-mcp": {
"command": "uv",
"args": [
"run",
"--project", "/home/you/git/skills-mcp",
"skills-mcp"
]
}
}
}
Generic stdio MCP host
Any host that launches an MCP server as a child process:
command: uv
args: ["run", "--project", "/path/to/skills-mcp", "skills-mcp"]
Pass --config /path/to/config.jsonc as an additional arg to override the default config path.
Development
uv sync
uv run pytest tests/ -q # 107 tests
uv run ruff check src/ tests/
uv run mypy src/
Pre-commit hooks enforce lint, format, and type checks on every commit:
pre-commit install --install-hooks # once, after cloning
pre-commit run --all-files # check everything now
Non-goals (v1)
- Writing or publishing skills to a registry
- Local skill installation / sync
- Non-GitHub git hosts (GitLab, Bitbucket, Gists)
- GitHub App authentication
- Content-hash verification
- Cache size cap / LRU eviction
- Multi-process cache locking
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.