AnswerQA

How do I run a plugin marketplace for my team or community?

Answer

A marketplace is a `.claude-plugin/marketplace.json` catalog hosted in a git repo. You list plugins by name and source (relative path, github, git URL, git-subdir, or npm), users add the marketplace once with `/plugin marketplace add owner/repo`, and updates land via `/plugin marketplace update`. The hard parts are versioning discipline, gating for private repos, and using `strictKnownMarketplaces` in managed settings to lock down what users can install.

By Kalle Lamminpää Verified May 7, 2026

A marketplace is a .claude-plugin/marketplace.json catalog hosted in a git repo, and users add it once with /plugin marketplace add owner/repo. The hard parts are not the schema, they are versioning discipline, private-repo auth, and the strictKnownMarketplaces managed-setting that decides whether your team can install anything else.

The minimal marketplace

A marketplace repo with one plugin has three files. Save this layout under any directory you control (say, acme-plugins/):

acme-plugins/
├── .claude-plugin/
│   └── marketplace.json
└── plugins/
    └── code-formatter/
        ├── .claude-plugin/
        │   └── plugin.json
        └── skills/
            └── format/
                └── SKILL.md

acme-plugins/.claude-plugin/marketplace.json:

{
  "name": "acme-tools",
  "owner": {
    "name": "Acme DevTools",
    "email": "[email protected]"
  },
  "plugins": [
    {
      "name": "code-formatter",
      "source": "./plugins/code-formatter",
      "description": "Auto-format files on save"
    }
  ]
}

acme-plugins/plugins/code-formatter/.claude-plugin/plugin.json:

{
  "name": "code-formatter",
  "description": "Auto-format files on save",
  "version": "1.0.0"
}

Push to GitHub at acme-corp/acme-plugins and users add it with one command:

/plugin marketplace add acme-corp/acme-plugins
/plugin install code-formatter@acme-tools

Note the install syntax: <plugin-name>@<marketplace-name>, where acme-tools is the name field inside marketplace.json, not the GitHub repo path.

The five source types

The source field on each plugin entry tells Claude Code where to fetch that plugin. Pick based on where the plugin actually lives.

Source typeUse whenExample
Relative path ("./plugins/x")Plugin lives inside the marketplace repo"source": "./plugins/formatter"
githubPlugin lives in its own GitHub repo{ "source": "github", "repo": "acme/formatter", "ref": "v2.1" }
urlNon-GitHub git host (GitLab, Bitbucket, self-hosted){ "source": "url", "url": "https://gitlab.acme.com/team/plugin.git" }
git-subdirPlugin lives in a subdirectory of a monorepo{ "source": "git-subdir", "url": "acme/monorepo", "path": "tools/claude-plugin" }
npmPlugin distributed as an npm package{ "source": "npm", "package": "@acme/claude-plugin", "version": "^2.0.0" }

For github, url, and git-subdir you can pin to ref (branch or tag) and sha (40-char commit) for reproducible installs.

The git-subdir source uses a sparse partial clone, so a 5GB monorepo with a 50KB plugin only pulls the 50KB plus minimal git metadata. Use this instead of “we’ll just publish from the monorepo” guesswork.

Hosting on GitHub vs. anywhere else

GitHub gets the shortest user command:

/plugin marketplace add acme-corp/acme-plugins
/plugin marketplace add acme-corp/[email protected]   # pin to a tag

Other git hosts work, but the user types the full URL:

/plugin marketplace add https://gitlab.acme.com/devtools/plugins.git

You can also publish a plain marketplace.json over HTTP and have users add it by URL. Do not do this if any plugin uses a relative path source: URL-based marketplaces fetch the JSON only, not the plugin files, so "source": "./plugins/x" will fail with path-not-found at install time. Use git hosting or switch every plugin entry to github / url / npm sources.

Private repos: credentials in two places

Manual install and /plugin marketplace update use your existing git credential helpers. If gh auth status works, plugin install works. If you push and pull from a private GitLab over SSH, plugin install works as long as the host is in known_hosts and the key is loaded in ssh-agent.

Background auto-updates run at startup, before you can be prompted for a password. Set the appropriate env var so they can authenticate non-interactively:

ProviderEnv varNotes
GitHubGITHUB_TOKEN or GH_TOKENPAT or GitHub App token with repo scope for private
GitLabGITLAB_TOKEN or GL_TOKENPAT or project token with at least read_repository
BitbucketBITBUCKET_TOKENApp password or repo access token

Stick this in ~/.zshrc once and forget about it. In CI, set it as a secret. Without it, the marketplace shows up but auto-update silently fails.

Versioning: the bump-or-omit rule

Claude Code resolves a plugin’s version from the first of these that is set:

  1. version in the plugin’s plugin.json
  2. version in the plugin’s marketplace entry
  3. The git commit SHA of the source

Pick one strategy and stick to it:

  • Internal/actively-developed plugins: omit version everywhere. Every commit becomes a new version automatically. Users get updates whenever they /plugin marketplace update or auto-update fires.
  • Public/SemVer plugins: set version in plugin.json and bump it on every release. If you forget to bump, users keep the cached old copy even though the source has changed.

Avoid setting version in both plugin.json and the marketplace entry. The plugin.json value silently wins, so an outdated manifest can mask the version you wrote in marketplace.json.

Gating: extraKnownMarketplaces and strictKnownMarketplaces

Two managed settings control what your team can install.

extraKnownMarketplaces declares marketplaces team members get prompted to add automatically when they trust the project. Drop this in .claude/settings.json (project-shared) or in managed settings (org-wide):

{
  "extraKnownMarketplaces": {
    "acme-tools": {
      "source": {
        "source": "github",
        "repo": "acme-corp/acme-plugins"
      }
    }
  },
  "enabledPlugins": {
    "code-formatter@acme-tools": true
  }
}

enabledPlugins pre-flags which plugins should be on by default once the marketplace is added.

strictKnownMarketplaces in managed settings (set by IT, cannot be overridden by users or projects) restricts which marketplaces users are allowed to add at all. Four useful shapes:

Complete lockdown (no marketplaces, period):

{ "strictKnownMarketplaces": [] }

Allowlist specific marketplaces:

{
  "strictKnownMarketplaces": [
    { "source": "github", "repo": "acme-corp/approved-plugins" },
    { "source": "github", "repo": "acme-corp/security-tools", "ref": "v2.0" }
  ]
}

Allow anything from your internal git host (regex on host):

{
  "strictKnownMarketplaces": [
    { "source": "hostPattern", "hostPattern": "^github\\.acme-corp\\.com$" }
  ]
}

Allow filesystem-based marketplaces from a specific directory (regex on path):

{
  "strictKnownMarketplaces": [
    { "source": "pathPattern", "pathPattern": "^/opt/approved/" }
  ]
}

Pair extraKnownMarketplaces with strictKnownMarketplaces so users do not have to add anything by hand: extraKnownMarketplaces auto-registers the approved marketplaces and strictKnownMarketplaces blocks anything else. The order in the JSON file does not matter; what matters is that both live in managed settings together.

Footguns

Relative paths fail in URL-based marketplaces. A marketplace added via /plugin marketplace add https://example.com/marketplace.json only fetches the JSON; it does not download the plugin files. Any plugin with "source": "./plugins/x" fails with “path not found”. Why this matters: you can ship a marketplace that works perfectly when you test it from a local checkout and breaks the moment a user adds it by URL. Either host on git, or use github / url / npm source for every plugin entry.

Pinning version without bumping silently freezes users. If plugin.json says "version": "1.0.0" and you push three releases without changing the field, every existing user keeps the cached 1.0.0 forever. The cache key is the resolved version, not the commit SHA. Why this matters: you fix a security bug, push to main, run /plugin marketplace update, and nothing changes for anyone. Bump version on every release, or omit it and let the commit SHA do the work.

Background auto-update needs a token, not a credential helper. Manual /plugin marketplace update uses gh auth login credentials. Auto-update runs at startup, when interactive prompts would block Claude Code from launching, so it skips credential helpers and reads GITHUB_TOKEN / GITLAB_TOKEN / BITBUCKET_TOKEN from the environment. Why this matters: a private marketplace that works perfectly for manual updates silently stops auto-updating in CI or on a fresh laptop where the env var is not set. Verify echo $GITHUB_TOKEN returns a value, in the shell that launches Claude Code.

Reserved marketplace names. claude-code-marketplace, claude-code-plugins, claude-plugins-official, anthropic-marketplace, anthropic-plugins, agent-skills, knowledge-work-plugins, and life-sciences are reserved for Anthropic. Names that impersonate official ones (like official-claude-plugins or anthropic-tools-v2) are also blocked. Why this matters: pick a name like acme-tools early; renaming a published marketplace forces every user to remove and re-add it, which uninstalls every plugin they had installed from it.

/plugin marketplace remove uninstalls every plugin from that marketplace. Removing the marketplace and re-adding it is not a refresh, it is a wipe-and-reinstall. Why this matters: do not “fix” a stale marketplace by removing and re-adding; use /plugin marketplace update instead. The remove path is documented as such, but it is the kind of thing you only learn the hard way after losing the team’s installed plugin set on a Friday afternoon.

plugin.json version always wins over marketplace-entry version. If you set both, the manifest version masks the marketplace one with no warning. Why this matters: you bump marketplace.json from 2.1.0 to 2.2.0 and nothing changes for users because plugin.json still says 2.1.0. Pick one location to declare version and leave the other unset.

When NOT to run a marketplace

  • You have one plugin. Users can install directly from a plugin repo with /plugin install owner/repo without going through a marketplace. The marketplace shape pays off at three or more plugins, when the catalog itself becomes useful.
  • The plugin is for one project, not a team. A .claude/ directory checked into the project repo plus extraKnownMarketplaces pointing at someone else’s marketplace covers most “ship our setup with the repo” cases. You do not need to be a marketplace operator to share a plugin.
  • You want to publish to the public. The community marketplaces (anthropics/claude-plugins, third-party catalogs) accept submissions; running your own competing marketplace fragments discovery. Reach for your own marketplace when you need control over the catalog (private plugins, gated access, internal-only) or when scale demands it.
  • You are not ready for the versioning commitment. Every plugin you list is a thing teammates will rely on. A marketplace is a deployment surface; if you cannot commit to bumping versions or watching auto-update telemetry, keep plugins as one-off repos until you are.

Sources

  • Create and distribute a plugin marketplace
    Authoritative: marketplace.json schema, all five source types (relative, github, url, git-subdir, npm), private-repo auth env vars, strictKnownMarketplaces managed-settings restriction, version-resolution order.
  • Discover and install prebuilt plugins
    User-side: how `/plugin marketplace add` accepts owner/repo shorthand, `@ref` pinning, local paths, and remote URLs to a `marketplace.json`.
  • Plugins reference
    Plugin manifest schema, plugin caching behavior, `${CLAUDE_PLUGIN_ROOT}` and `${CLAUDE_PLUGIN_DATA}` path variables for marketplace-distributed plugins.

Was this helpful?