skills-mcp
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.mdfileRead-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 authPath 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 syncRunning 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 INFOConfiguration
The server reads a JSONC file from the platform config directory:
Platform | Default path |
Linux |
|
macOS |
|
Windows |
|
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 |
| both | No authentication |
| GitHub | PAT from the named env var ( |
| GitHub | Token from |
| HTTP |
|
| HTTP | HTTP Basic from |
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.mdWith 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.mdWith 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 |
| — | JSON array: |
|
| JSON array: |
|
| Without |
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 textDiscover 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 |
|
macOS |
|
Windows |
|
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 nowNon-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