Skip to main content
Glama
ashwnn
by ashwnn

Skills Registry

A personal, read-only MCP skill registry: one GitHub repository, one Cloudflare Worker, four stable MCP tools. Every approved push builds and deploys a validated snapshot; every client keeps the same endpoint URL.

Architecture

Deployment-time publishing, not runtime GitHub fetching:

flowchart TD
    G["GitHub repository"] --> V["CI validation and snapshot build"]
    V --> D["Deploy Worker at stable URL"]
    C["AI clients"] --> A["MCP authentication"]
    A --> W["Worker: four registry tools"]
    D --> W
    W --> S["Bundled index and skill files"]
    V --> F["Validation failure: retain existing deployment"]

The Worker embeds a generated catalog (src/generated/catalog.json) built from one exact Git commit. It answers MCP requests with no GitHub API calls, no database, and no session state — see src/worker.ts.

Related MCP server: skillet

Repository layout

Path

Purpose

skills/<id>/SKILL.md

Metadata (YAML frontmatter) + primary instructions

skills/<id>/references/, templates/

Supporting Markdown/text, explicitly allowlisted per skill

skills/<id>/scripts/

Supporting shell/Python/JS/TS source, same allowlist rules, never executed by the registry

registry.config.json

Schema, allowed file types, size limits, search/pagination defaults

src/

Worker, GitHub token check, MCP dispatch, tools, search, registry loader

scripts/

validate-skills.mjs, build-snapshot.mjs

tests/

Contract, search, retrieval, revision, security, auth tests

docs/client-setup.md

Per-client connection steps and the shared bootstrap instruction

.github/workflows/

validate.yml (PRs + push), deploy.yml (push to main)

src/generated/ is a build output (gitignored), regenerated by CI from the exact checked-out commit. It is never a second source of truth.

Authoring a skill

  1. Create skills/<id>/SKILL.md with frontmatter:

    ---
    schema_version: 1
    id: your-skill-id
    name: Human Readable Name
    description: One sentence, used in search and listings.
    version: 1.0.0
    tags: [development]
    keywords: [relevant, terms]
    requires: []        # capabilities the client needs; does not grant them
    files: []           # explicit retrieval allowlist, relative to the skill dir
    required_zdr: false # true = only served to callers cleared for Zero Data Retention (see "Data handling")
    ---
    
    # When to use
    ...
    
    # Instructions
    ...
  2. id must be a stable lowercase slug matching the directory name (^[a-z0-9]+(-[a-z0-9]+)*$) — treat renaming it as a breaking change.

  3. List every supporting file under files; anything on disk that isn't listed fails validation, and anything listed that doesn't exist also fails.

  4. Bump version (semver) whenever the body or a supporting file changes — CI computes a content hash against the base branch and fails if the version didn't move.

  5. Run npm run validate locally before pushing.

  6. If a skill's instructions genuinely need a local script, put it under scripts/ (.sh, .py, .js, .ts, .rb — see registry.config.json allowed_script_extensions) and declare it in files like any other supporting file. It is served as plain text via skills_get_file with executable: true; the registry never runs it. State plainly in the skill's own instructions that the client must read the script before running it under its own local execution permissions.

  7. If a skill's content must never leave a Zero-Data-Retention-cleared provider, set required_zdr: true. See "Data handling" below — this is an access-control decision, not a content-type decision, so it applies regardless of file type.

Local development

npm install
npm run validate   # metadata, duplicate IDs, file allowlist, size, version bumps
npm run build      # generates src/generated/catalog.json from the working tree
npm run typecheck
npm test           # unit tests against fixture catalogs, independent of build output
npm run dev        # wrangler dev, serves the just-built snapshot locally

MCP interface

Four tools, all read-only/non-destructive, all returning api_version, registry_sha, and published_at:

Tool

Purpose

skills_list

Paginated metadata, optional tags/limit/cursor/revision

skills_search

Ranked keyword/tag search with match reasons and excerpts

skills_get

Metadata, primary instructions, supporting-file manifest for one skill

skills_get_file

Bounded, paginated text from one allowlisted supporting file

revision means the registry's Git SHA, not a skill's semantic version. An omitted revision targets the active snapshot; a stale one returns REVISION_UNAVAILABLE naming the active SHA so a client restarts discovery instead of mixing revisions. Application errors (SKILL_NOT_FOUND, FILE_NOT_FOUND, INVALID_PATH, REVISION_UNAVAILABLE, INVALID_CURSOR) come back as a normal tool result with isError: true, never disguised as an empty search result. Malformed arguments are JSON-RPC -32602 errors.

Search is deterministic keyword/tag matching (src/search.ts): exact id/name match ranks highest, then tag, then keyword, then description substring, tie-broken by id. No embeddings, no invented relevance.

Every skill's metadata carries required_zdr (see "Data handling"), and every file manifest entry carries executable (true for anything under scripts/). A caller not cleared for required_zdr skills doesn't see them at all — skills_list/skills_search omit them, skills_get/ skills_get_file return SKILL_NOT_FOUND rather than a permission error, so their existence isn't leaked to callers who can't read them.

Authentication

Decision: direct GitHub personal-access-token validation, no OAuth App, no redirect flow. A client sends a GitHub PAT as its bearer token; the Worker asks GitHub who it belongs to (one call to api.github.com/user, see src/github.ts) and checks the returned login against ALLOWED_GITHUB_LOGINS. See src/worker.ts's resolveCaller.

This replaced an earlier full OAuth-provider design (GitHub OAuth App + /authorize + /github/callback + @cloudflare/workers-oauth-provider issuing its own per-client tokens). For a single-operator personal registry where every client can be configured with a static bearer/API-key header (Claude Code, Codex, OpenCode all support this), that was more machinery than the problem needed: no OAuth App to register, no callback URL, no KV namespace for grant storage, no DCR/CIMD/PKCE surface to maintain. The tradeoff is Claude Web / ChatGPT Web connectors, which are generally built around an interactive "Authorize" redirect button rather than a pasted token field — verify each one actually has a usable auth field before relying on it (see "Open questions still unresolved").

Setup required before this works (not done yet — Cloudflare account secrets were just added, GitHub PAT/allowlist still need setting):

  1. Create a GitHub personal access token (github.com → Settings → Developer settings → Personal access tokens). No scopes are required — this only reads the token owner's public identity via /user.

  2. wrangler secret put ALLOWED_GITHUB_LOGINS — your GitHub username(s), comma-separated. Optionally ZDR_GITHUB_LOGINS (see "Data handling").

  3. Configure each client with Authorization: Bearer <your PAT> — see docs/client-setup.md.

Optional service-token path. A static bearer token (SKILLS_READ_TOKENS) is checked first, before the GitHub lookup, so automation (the deploy workflow's smoke test, a script) doesn't need to hold your actual GitHub credential. It's equivalent in privilege to a GitHub-authenticated call, not a separate tier.

Cloudflare deployment credentials (CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID) live only in CI secrets — never in client configuration or tool responses. Your GitHub PAT lives only in your own client configs and is never sent anywhere but GitHub's API and this Worker.

Data handling: required_zdr

Some skills may describe workflows unsuitable for disclosure to every connected AI provider (per your answer to the open question below). Those skills set required_zdr: true in frontmatter. A caller — a GitHub identity or a static token — is cleared to see them only if it's in ZDR_GITHUB_LOGINS or SKILLS_ZDR_TOKENS respectively; both default to empty, so clearance is opt-in and fails closed. This is configured per client, exactly as asked: a client whose connected provider has no ZDR agreement simply never gets a login/token added to those lists, and the skill doesn't exist as far as it can tell (see "MCP interface" above for why that's SKILL_NOT_FOUND, not a permission error).

required_zdr is an access-control flag, not a content-type flag — it applies the same way to a Markdown instruction file or a scripts/ file.

Publishing

  1. Edit a skill, bump its version.

  2. Push/merge to main.

  3. validate.yml runs on PRs and pushes; deploy.yml runs only on push to main: validate → build snapshot from the exact commit → typecheck → test → wrangler deploy → smoke test.

  4. A failed step leaves the previous deployment live. Deploys are serialized (concurrency: production-deploy) so an older build can never overwrite a newer one.

  5. Rollback: re-run deploy.yml from a previous green commit (or wrangler rollback if using Cloudflare's built-in version history).

"Latest" means latest successfully deployed commit, not necessarily branch HEAD.

Security notes

  • Skill files are restricted to .md/.txt (or, under scripts/, .sh/.py/.js/.ts/.rb) under configured size limits; retrieval rejects path traversal, absolute paths, and anything outside a skill's declared files allowlist (src/tools.ts, tested in tests/tools.test.ts).

  • No skill script is ever executed by validation, the build, or retrieval — scripts/ files are served as inert text with an executable flag; a client chooses whether to run one, under its own local permission model.

  • requires in frontmatter documents what a skill's instructions assume a client has access to — it grants nothing by itself.

  • Retrieved content still enters the connected AI provider's context. Don't put secrets or incident-specific sensitive detail into a skill.

  • Registry instructions are not higher priority than system instructions, user authorization, or local access controls — the bootstrap text in docs/client-setup.md says this explicitly so it travels with every client's standing instructions.

Resolved decisions

  • Auth mechanism: direct GitHub PAT validation against api.github.com, no OAuth App, no redirect flow — see "Authentication" above. (An earlier pass built a full OAuth-provider design; it was replaced once a simpler option was pointed out — see git history if you want the OAuth version.)

  • Client surfaces: all of them — Claude Web, Claude Code, Codex CLI and hosted surfaces, OpenCode, and ChatGPT Web. Claude Code, Codex, and OpenCode all take a static bearer/API-key header in their MCP config, so a pasted GitHub PAT works directly. Claude Web and ChatGPT Web connectors are generally built around an interactive OAuth "Authorize" redirect rather than a pasted-token field — confirm each actually has a usable auth field for this before relying on it (see "Open questions still unresolved"). If one doesn't, the OAuth-provider design is the fallback.

  • Publish freshness: "publish after CI finishes" is fine as-is; no change made.

  • Local scripts/binaries: text scripts (.sh/.py/.js/.ts/.rb) are now supported under scripts/, served as plain text with an executable flag, never run by the registry — see "Authoring a skill" step 6. Compiled/binary assets remain out of scope. A binary can't be reviewed by a human or model before a client runs it, which defeats the trust model this whole registry depends on (retrieval never grants execution); it also adds integrity/signing and storage concerns this registry doesn't currently handle. If a real skill turns out to need a binary, the right shape is a separate, explicitly-confirmed release channel with signed artifacts — not skills_get_file — and should be designed against a concrete skill that needs it, not speculatively.

  • Long-running tasks spanning a deploy: staying with active-snapshot- only for now. The existing REVISION_UNAVAILABLE behavior already keeps a client from silently mixing revisions mid-task — it just has to restart discovery. No evidence yet that this is a real problem for any actual task; revisit with historical-revision serving only if one shows up.

  • ZDR-sensitive skills: yes, some may need this — see required_zdr under "Data handling" above.

Open questions still unresolved

  • Does the Claude Web and ChatGPT Web connector UI actually expose a field for a static bearer token / API key, or only an OAuth "Authorize" button? Not yet verified — no live deployment exists yet to test against.

Explicitly out of scope for now

Embeddings/full-text search, a database, an admin UI, remote execution, automatic native-skill installation, write tools, one-tool-per-skill registration, runtime GitHub sync, historical-revision retrieval, and compiled/binary skill assets.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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
    27
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to discover, install, and manage SKILL.md skills from a Git-backed registry via MCP tools for search, install, and list operations.
    17
    1
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ashwnn/skills'

If you have feedback or need assistance with the MCP directory API, please join our Discord server