Skip to content

Libraries

Kelly Ferrone edited this page Sep 21, 2026 · 1 revision

Libraries

A library is the first segment of every URI served — skill://grafana/… — and a client sees the catalogue one library at a time. A library holds no material of its own: it picks plugins, and every field is in Configuration.

libraries:
- name: grafana
  description: Observability skills — Grafana, Loki and friends.
  source: github://grafana/skills?ref=51d33e71e191b409bbd25fc7be2684c610d18166
- name: ops
  description: Everything anybody here publishes about running things.
  plugins: [house-runbooks]
  pluginSelector:
    categories: [operations]
    tags: ["oncall,runbooks", lgtm]

Three ways to pick plugins

A library has a name, an optional description, and any of three ways to pick plugins. Their union is the library.

Written Means
source: <address> a marketplace: every entry of the marketplace.json published there
plugins: [a, b@other] those plugins by name, yours or a marketplace's
pluginSelector: {categories, tags} every plugin matching the query, marketplace entries included

source and pluginSelector on one library is refused at load: a marketplace library's shape is the catalogue's, and a query would have to run before the catalogue had been read. source plus a named plugins: list is fine — that adds beside the catalogue without reshaping it.

A library carries no tags and no category of its own. Tagging is what a plugin does, so ?tags=observability reaches a library only through what its plugins say, and ?library=grafana is how a client asks for Grafana. See Scoping.

The selector

pluginSelector:
  categories: [design, engineering]   # any of these
  tags: ["ui,tokens", penpot]         # (ui and tokens) or penpot

Across the two fields it is AND, as in every faceted search: a category and a set of tags narrow each other. Within categories it can only be OR — a plugin has exactly one category, so "in design and engineering" would match nothing, and a comma in one is refused. Within tags a comma means all of and separate items mean any of. That is one comma rule for the YAML and for the query string alike.

What a tag matches is a plugin's tags and its keywords together: which of the two a publisher reached for is not a distinction worth making to whoever is searching.

Marketplaces

A library whose source is an address is read as somebody else's catalogue.

It is looked for at .claude-plugin/marketplace.json, .github/plugin/marketplace.json and .agents/plugins/marketplace.json, first found. A library whose source holds none of them carries an error in /health naming what was looked for, and still serves whatever its plugins: list adds beside it.

Every entry becomes a plugin with the entry's own name, description, category, tags and version, and an id of <entry>@<library> — so two catalogues may publish a testing and both are reachable. An entry's own skills list is what that plugin serves, which is how Grafana's seven plugins arrive already split and how its repository's template skill stays out without a glob written here.

Where an entry's source may point:

Form Read as
"./", "./plugins/x" a subdirectory of the marketplace's own tree — one clone for the catalogue and all of it
{source: github, repo, ref|sha} that repository, through your declared git source for github.com
{source: url, url, ref|sha} that repository, through the declared source it sits under
{source: git-subdir, url, path, ref|sha} the same, at path inside it

A catalogue does not get to introduce a host: an entry naming a repository under no declared source is skipped, because a host is where a credential and a refresh interval are configured. npm and archive entries are skipped — nothing here unpacks a package — and a command entry always is, because nothing fetched is ever executed. strict: false entries, where the entry would replace the plugin's own manifest, are skipped until something we pull publishes one.

Every skip carries its reason under the library's skipped in /health, so a plugin an operator expected and cannot find says why, and one bad entry never costs the rest of the catalogue.

Resolution happens in two phases

  1. Every marketplace is read, and its entries join the set of known plugins beside the ones the config declares.
  2. Every library is resolved against that whole set: its marketplace's entries, then its plugins: names, then its pluginSelector, deduplicated, in that order.

The order is the point. A selector runs over marketplace plugins too, so a query of your own can regroup somebody else's catalogue into a library of your own without copying anything.

A remote plugin is never edited here

No field of a marketplace entry can be overridden. To change anything about one — its category, its tags, which of its files are served — declare your own plugin against the same repository and pick what you want:

plugins:
- name: penpot-shared
  category: design
  source: github://penpot/penpot-ai-kit?ref=efbefc935ee43804502976aa5ec9659a8bb7e207
  skills: []
  prompts: []
  files: ["shared/**/*", "workflows/**/*"]

libraries:
- name: penpot
  source: github://penpot/penpot-ai-kit?ref=efbefc935ee43804502976aa5ec9659a8bb7e207
  plugins: [penpot-shared]

The cost is nil in fetches: the address is the same URL at the same ref, so the catalogue's plugin and yours share one clone. What you get is a plugin that is yours — your category, your tags, your globs — beside one that stays exactly what its publisher published.

When two plugins want one address

Two plugins in one library can mint the same skill URI, the same library-level file URI or the same prompt name. Only one of the two could ever be read, so the later one in the library's order fails in that library and says what it collided with, under the library's conflicts in /health. The earlier one serves unaffected, and the failed plugin serves normally in every other library it belongs to.

Inside a single plugin the same clash is not a conflict: the first of two skills or prompts at one name serves, and each thing left out is listed under that plugin's skipped.


Home · Config file · Plugins · Scoping · Configuration

Clone this wiki locally