Skip to main content
Glama

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.md file

  • 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), gh CLI 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 for list_skills; {"error": "…"} JSON or plain "Error: …" string for get_skill depending on whether file was 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