marketplace.json catalog where other people can add it with /plugin marketplace add, install its plugins, and keep receiving your changes after you push.
This page is for the person who operates a marketplace.
These cases are covered on other pages:
- You haven’t written the catalog file yet: start with Create a marketplace
- You’re an admin requiring, restricting, or pre-installing marketplaces across your organization’s machines: read Manage plugins for your organization
name.
Host your marketplace
You can host the marketplace on GitHub, on another git host, as a hostedmarketplace.json URL, or in a directory on a shared filesystem. Send your users the add command for your host and tell them what they need on their machine:
To pin a branch or tag of a GitHub or git-URL marketplace, tell users to append
#<ref>, as in your-org/your-marketplace#stable. The plugin commands reference lists every form the command accepts.
A successful add prints Successfully added marketplace: your-marketplace. Claude Code takes that name from the name field in your marketplace.json, not from the repository name.
Users then install a plugin by its entry’s name and the marketplace’s name, as in /plugin install code-formatter@your-marketplace.
Register the marketplace for everyone in a repository
To share the marketplace with everyone who works in one repository, runclaude plugin marketplace add your-org/your-marketplace --scope project there once from your shell and commit the .claude/settings.json it writes. Claude Code then registers the marketplace for each teammate who trusts the folder.
Avoid relative-path entries in a URL-hosted marketplace
When users add your marketplace as a baremarketplace.json URL, Claude Code downloads only that file. An entry in your plugins array whose source is a relative path such as ./plugins/formatter then fails at install with its marketplace entry path does not stay inside the marketplace directory. Give every entry a source that can be fetched on its own, such as a github repository or an archive URL, or host the marketplace in a git repository so Claude Code clones the whole tree.
Edit plugins in place on a shared directory
When users add your marketplace from a shared directory, Claude Code reads plugins with relative-path sources directly from that directory instead of copying them. Users see your edits when they next start a session or run/reload-plugins, without an update step or a version bump.
Keep plugin files out of Git LFS
Keep the files your plugins need out of Git LFS. When users add a marketplace hosted in a git repository, or install a git-based plugin it lists, Claude Code clones that marketplace or plugin repository onto their machine. The clone never downloads LFS content, so LFS-tracked files arrive as pointer files.Share files within a marketplace with symlinks
To share files between your plugin and other parts of the same marketplace, create symbolic links inside your plugin directory. When Claude Code copies the plugin into its cache, it handles each symlink by where the target resolves:- Within the plugin’s own directory: the symlink is preserved as a relative symlink in the cache, so it keeps resolving to the copied target at runtime.
- Elsewhere within the same marketplace: the symlink is dereferenced. The target’s content is copied into the cache in its place. This lets a meta-plugin’s
skills/directory link to skills defined by other plugins in the marketplace. - Outside the marketplace: the symlink is skipped for security.
command source whose mode is the default copy, Claude Code preserves only symlinks that resolve within the plugin’s own directory and skips all others.
The following command creates a link from inside a marketplace plugin to a shared skill defined by a sibling plugin. On Windows, use mklink /D from an elevated Command Prompt or enable Developer Mode:
Distribute through organization settings
On a Team or Enterprise plan, you can also distribute the marketplace through Organization settings > Plugins & skills on claude.ai instead of hosting it somewhere users add it themselves. Organization sync reads the repository through your organization’s GitHub or GitLab connection on claude.ai, so your users’ git credentials aren’t involved. Organization sync is stricter about the repository than/plugin marketplace add is:
- Marketplace repository: on github.com and gitlab.com, it must be private or internal
- Plugin sources: each plugin source must be of type
github,url, orgit-subdir, or a relative path that starts with./ - Top-level
bin/directory: claude.ai rejects a plugin that has one and syncs the rest of the marketplace. The error message starts withPlugin contains a top-level bin/ directory. Keep executables in another directory, such asscripts/, and reference them as${CLAUDE_PLUGIN_ROOT}/scripts/<name>from your hooks or MCP server configs
Grant access to a private marketplace
When a user adds, installs from, or updates your marketplace, Claude Code runsgit on their machine with interactive prompts turned off and relies on whatever credentials that machine already holds. Claude Code has no git token of its own, and marketplace.json has no field for one.
You choose whether the clone runs over SSH or HTTPS by the form of the add command you send users:
- GitHub
owner/repo: Claude Code probesssh -T git@github.comand clones over SSH when the probe succeeds. If the probe fails, or the SSH clone itself fails, it clones over HTTPS. Users on machines without a GitHub SSH key can setCLAUDE_CODE_PLUGIN_PREFER_HTTPS=1to skip the probe and clone over HTTPS. git@host:path.git: SSH.https://example.com/repo.git: HTTPS.
- SSH: the key must work without a passphrase prompt, for example because it’s loaded in
ssh-agent. The host must already be inknown_hosts. - HTTPS: Claude Code leaves the user’s git credential helper enabled but forbids it from prompting. A credential the helper already stores works; one it would have to ask for fails. On GitHub,
gh auth loginfollowed bygh auth setup-gitstores one.
Serve users who have no git-host account
Users without a git-host account can add a marketplace you serve as amarketplace.json URL or from a shared directory, but they can install only the plugins whose entry sources they can also reach. An entry that points at a private github repository still fails at install for them, because Claude Code fetches it with the same non-interactive git it uses for a git-hosted marketplace.
These entry sources need no git account:
archive: a zip downloaded over HTTPS. Users need neithergitnor an account, only network access to the URL. Requires Claude Code v2.1.224 or later. Pin each archive withsha256so Claude Code refuses a changed download. To send credentials with the download, see Authenticate archive downloads.- A public git repository: Claude Code clones a public
urlorgit-subdirsource over HTTPS without credentials when the entry gives anhttps://URL. For agithubsource, or agit-subdirsource written asowner/repo, users without a GitHub SSH key setCLAUDE_CODE_PLUGIN_PREFER_HTTPS=1.
directory marketplace on a shared filesystem also works without git accounts. Users need only read access to the path.
What background auto-update does with credentials
Background auto-update is Claude Code’s unattended refresh of marketplaces and installed plugins after a session starts. It’s off for your marketplace until a user or admin turns it on, as covered under Keep users up to date. When it’s on for a private marketplace, the background check for new commits uses the user’s configured git credential helpers and never prompts. Each kind of remote and helper gives a different result:- SSH remotes: a key loaded in
ssh-agentauthenticates the check. - HTTPS remotes with a stored credential: a helper that can supply a stored credential without prompting authenticates the check. Git Credential Manager, the macOS Keychain helper, and
git-credential-storework this way once they hold a credential for the host. - HTTPS remotes with a helper that needs to prompt: the helper can’t answer in the background. The update fails quietly and the existing checkout stays in place, so the user’s plugins keep working from the last synced state.
- The checkout is up to date: Claude Code leaves it as it is.
- The check finds new commits, or fails because it can’t reach or authenticate to the remote: Claude Code clones the marketplace again and replaces the existing checkout with the new clone. If that clone fails, the existing checkout stays in place. The re-clone can time out on large repositories.
- Store a credential: sign in to the credential helper first so it holds a credential for the host. For GitHub, run
gh auth login, thengh auth setup-git. - Keep the checkout on failure: if the user sets
CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE=1, Claude Code keeps the existing checkout without attempting the re-clone when the background check can’t reach or authenticate to the remote. Plugins keep working from the last synced state.
GITHUB_TOKEN or another provider token in the environment, that alone doesn’t authenticate the background check. A token takes effect through a credential helper, such as the gh CLI’s helper, which reads GH_TOKEN and GITHUB_TOKEN.
Roll out to a whole company
Rolling a plugin out to a company involves you as the marketplace owner, an administrator who controls managed settings, and each person who uses Claude Code. You can run the rollout without the administrator, in which case each person adds the marketplace and installs the plugin themselves.
For people who have no git-host account, these sections each cover one way to reach them:
- Entry sources that need no git account: Serve users who have no git-host account
- A pre-populated plugins directory: Seed containers and CI, which also serves users who have no git-host account
- claude.ai organization settings: Distribute through organization settings, where your users’ git credentials aren’t involved
Keep users up to date
Your changes reach users through background auto-update, once it’s turned on for your marketplace, or when users update the plugin themselves. In both cases a user gets a new copy of a plugin only when its computed version changes, as described under Release a new version.Turn on auto-update
Background auto-update is off for your marketplace by default, andmarketplace.json has no field to turn it on. A user or an admin turns it on:
- Tell users to turn it on: each user goes to Marketplaces in
/plugin, selects your marketplace, and selects Enable auto-update. - Ask an admin to set it: if an admin sets
"autoUpdate": trueon your marketplace’sextraKnownMarketplacesentry in managed settings, it’s on for everyone who receives those settings. See Set update policy.
/plugin marketplace update <name> in a session or claude plugin update <plugin>@<name> in the shell.
For what users see when an update reaches them, see When auto-update runs.
Release a new version
To release a new version to users, change the plugin’sversion. Users get a new copy only when the plugin’s computed version differs from the one they have. That version comes from plugin.json first, then from the marketplace entry, per Versions and updates.
A plugin that users load in place from a marketplace they added as a local directory isn’t controlled by version. It loads your current files at every session start, whatever its version string says.
For every install other than an in-place load or one from a command source, either increase version on each release or omit it:
- Bump
versionon each release: users stay on their cached copy until the string changes. If you set"version": "1.0.0"and push new commits without changing it, users don’t receive them. - Omit
version: users track your commits instead. Leaveversionout of bothplugin.jsonand the marketplace entry.
version in both plugin.json and the marketplace entry. If you do, Claude Code uses the plugin.json value without warning, and claude plugin validate reports the mismatch as Entry declares version "<a>" but <path>/plugin.json says "<b>".
Hold users on one version
One marketplace serves one version of each plugin at a time, so you hold users on a version by choosing what each entry points at:refandshaon the plugin entry:refnames a branch or tag andshanames a commit for agithub,url, orgit-subdirsource. See Plugin sources.#<ref>on the add command: users who addyour-org/your-marketplace#stableget that branch or tag of the catalog. For two release lines at once, see Run release channels.<plugin>--v<version>tags: a dependency’s version range resolves against these tags. See Release a plugin that others depend on.
Change the command of a command source
If you change thecommand of a command source, or switch its mode, each user has to accept the new command before Claude Code runs it. Claude Code runs only the exact command a user accepted when they installed or last updated the plugin.
After a user’s copy of your marketplace picks up the change, that user sees the following:
- No more background runs: the once-per-session run of the command stops for that user, so the tool’s new output doesn’t reach them.
- An entry in the
/pluginErrors tab: the entry shows the new command and theclaude plugin updatecommand to run.
claude plugin update command that entry shows, in a terminal. Claude Code shows them the new command and asks them to accept it.
Run release channels
To offer stable and early-access tracks, host two marketplaces whose entries point at different refs of the same plugin, and let each user add the one they want. Claude Code has no release-channel concept, and one marketplace serves one version of each plugin at a time. Give the twomarketplace.json files different name values. Claude Code identifies a marketplace by its name, so a user can’t have two marketplaces with the same name registered at once.
With these two catalogs, users who add stable-tools install code-formatter from the stable branch, and users who add latest-tools install it from latest:
plugin.json versions, or omit version so the commit SHA distinguishes them. Updates are detected by comparing versions, so a ref that moves without a version change leaves users on the cached copy.
To assign the channels to user groups instead of letting users choose, an admin gives each group the matching extraKnownMarketplaces entry, as described under Set update policy.
Rename or remove a plugin
A plugin’sname is its identifier. Users reference it in the enabledPlugins and pluginConfigs settings keys and in /plugin install, so changing it breaks every existing install.
To change the label users see in /plugin without breaking anything, set displayName in plugin.json and keep name unchanged.
Migrate users with a renames map
When you must change aname, add a top-level renames map to marketplace.json so Claude Code migrates existing users instead of reporting Plugin "<name>" not found in marketplace. Do the same when you remove an entry from plugins. Automatic migration requires Claude Code v2.1.193 or later.
Map each former name to its current name, or to null when the plugin is gone. This marketplace renames formatter to code-formatter and records that legacy-linter was removed:
- Renamed entry: the plugin loads under its new name.
claude plugin listand the plugin’s details under/pluginshowRenamed to "code-formatter" in the "your-marketplace" marketplaceonce, and Claude Code rewrites the old key to the new one inenabledPluginsandpluginConfigsin the user, project, and local settings scopes. nullentry: the old key is dropped from those scopes and the user seesRemoved from the "your-marketplace" marketplace.- Enabled in managed settings: the plugin still loads under its new name, but Claude Code can’t rewrite managed settings, so the notice recurs until an admin updates
enabledPluginsthere.
Plugin "<name>" not cached at <path> until the user runs /plugin install code-formatter@your-marketplace once in a session.
Treat renames as append-only history. Keep old entries after everyone has migrated. When you rename again, add a second entry rather than editing the first, because Claude Code follows the chain from the oldest name.
In your shell, run claude plugin validate . after editing the map. It rejects a chain that cycles or that ends anywhere other than null or a name in plugins, with renames.<name>: chain does not resolve.
Uninstall removed plugins from users’ machines
To uninstall a removed plugin from users’ machines rather than leave a copy behind, set"forceRemoveDeletedPlugins": true at the top level of marketplace.json. Without the field, a removed plugin stays installed and reports Plugin "<name>" not found in marketplace when a session loads it. With it, Claude Code does the following at each session start:
- Compares what users installed from your marketplace against the entries and the
renamesmap, and treats any plugin that is neither listed nor renamed as removed. - Uninstalls each removed plugin from the user, project, and local scopes. Plugins that only managed settings installed stay in place.
- Lists each removed plugin under a Flagged heading in
/pluginwith the statusRemoved from marketplace.
Authenticate archive downloads
To authenticate anarchive download, such as a download from a private registry, set the HTTP headers Claude Code sends with it. You can set headers in either of these places:
- The marketplace’s
urlsource: theurlsource you registered the marketplace from, such as anextraKnownMarketplacesentry. - The plugin’s entry: on Claude Code v2.1.238 or later, you can set it on the plugin’s
marketplace.jsonentry instead, besidesource.
headersHelper command instead of headers when the value is short-lived, such as a token your registry generates on request. Claude Code runs the command and sends the JSON object it prints as that place’s headers. Requires Claude Code v2.1.238 or later.
The marketplace reference lists the headers and headersHelper entry fields.
The place you choose decides which downloads get the headers and when Claude Code runs the command:
Where both places set a header of the same name, Claude Code sends the entry’s value. Within one place, a header the command prints overrides a header of the same name listed in
headers.
Add a headersHelper to a plugin entry
This entry setsheadersHelper beside source. It also sets "strict": false, which Claude Code requires of a marketplace.json entry that sets headersHelper:
claude plugin install my-plugin@your-marketplace in your shell. Claude Code shows you the command and the archive URL, and downloads the zip after you accept.
Write the headersHelper command
Whether you setheadersHelper on a marketplace’s url source or on a plugin entry, write the command to meet these requirements:
- Command text: at most 500 characters of printable ASCII, with no run of four or more spaces.
- Output: print one JSON object of header names and string values on stdout, then exit 0 within 10 seconds.
- Shell and working directory: Claude Code runs the command through
sh, or throughcmd.exeon Windows. The working directory is the configuration directory, which is~/.claudeorCLAUDE_CONFIG_DIR. Give an absolute path or a command onPATH, because a relative path resolves against that directory, not the user’s project. - Variables Claude Code removes: when the command is set in a
marketplace.jsonentry, or in a project’s.claude/settings.jsonor.claude/settings.local.json, Claude Code removes from the environment every variable whose name looks like a credential, by the same rule it applies to an MCPheadersHelper.ANTHROPIC_API_KEYandMY_REGISTRY_TOKENare both removed, so have the command read its credential from a file or a credential store. This removal doesn’t apply to a command set in user settings, a--settingsfile, or managed settings. - Variables Claude Code sets:
CLAUDE_CODE_MARKETPLACE_URLandCLAUDE_CODE_MARKETPLACE_NAMEfor aurlsource’s command, andCLAUDE_CODE_PLUGIN_NAMEandCLAUDE_CODE_PLUGIN_ARCHIVE_URLfor an entry’s command.CLAUDE_CODE_MARKETPLACE_NAMEis unset on the first fetch after a user adds a marketplace by URL, because that fetch is what supplies the name.
When Claude Code skips a headersHelper command or drops its output
AheadersHelper command doesn’t run, or headers from headers or from the command’s output are dropped, when one of the following applies:
- Command fails: if the command exits non-zero, runs past 10 seconds, or prints anything other than a JSON object of string values, the fetch or download the command was run for doesn’t happen.
- Marketplace URL doesn’t start with
https://: thaturlsource’s command doesn’t run, and requests carry only the headers listed in itsheadersfield. - Redirect leaves the origin: when a download is redirected off the archive URL’s origin, the redirected request carries no
headersvalues or command output from either the marketplaceurlsource or the plugin entry. - Entry sets a routing or identity header: Claude Code drops request-routing and client-identity names such as
Host,Cookie, andX-Forwarded-*from an entry’sheadersand command output, and keeps authentication names such asAuthorization. Everymarketplace.jsonentry is filtered this way. For an inline plugin entry in settings, seeextraKnownMarketplaces. - Command set in an
--add-dirdirectory’s settings: the command is ignored, on aurlsource and on an inline plugin entry alike, and only that file’sheadersare sent. - Managed settings block the command: setting
disableCommandPluginSourcestotrueblocksheadersHelpercommands, andallowManagedHooksOnlyblocks them too unlessdisableCommandPluginSourcesis explicitlyfalse. Under either block, Claude Code still runs the command for a marketplace that managed settings themselves declare.
How users accept a headersHelper command
A user accepts a plugin entry’s command each time they install or update that one plugin by itself. They do that from the plugin’s own view in/plugin, or with claude plugin install or claude plugin update. Claude Code shows the command and the archive URL, and runs the command only after the user accepts.
In a non-interactive shell, pass --yes to accept the command. To accept only the command that a previous --json run displayed, pass --accept-command with the sha256 the run reported.
Claude Code runs only the command it showed, for the archive URL it showed. If the entry’s command or archive URL changed in between, Claude Code refuses the install or update. A change in the query string alone doesn’t count.
Installs and updates that refuse a command instead of asking
On any operation other than a single-plugin install or update, Claude Code neither runs an entry’s command nor downloads its archive. The plugin stays at its installed version or stays uninstalled, and the user sees one of these results:- Installing several plugins at once, from a plugin suggestion, or as another plugin’s dependency: Claude Code refuses the plugin that has the command and directs the user to that plugin’s own view in
/plugin. The other plugins in a bulk install still install. A plugin that depends on the refused plugin fails to install until the user installs the refused plugin by itself. - Background auto-update, or session start for a plugin whose archive was never downloaded: Claude Code lists the plugin in the
/pluginErrors tab so the user knows to install or update it themselves.
When a marketplace url source’s command runs
You declare a marketplace url source’s headersHelper in a settings file, such as an extraKnownMarketplaces entry, rather than in the catalog the marketplace publishes. Claude Code therefore doesn’t ask the user to accept it on each install or update. Instead, the settings file that declares it decides when Claude Code runs it:
For an inline plugin entry in one of these files, Claude Code requires the same folder trust or settings approval as for a marketplace-level command in that file, and the user also accepts the entry’s command on each install or update.
Depend on and recommend other plugins
An entry can declare dependencies on other plugins.- Version ranges: a dependency can carry a semver range.
- Cross-marketplace dependencies: a dependency from another marketplace installs only when your marketplace lists that marketplace in
allowCrossMarketplaceDependenciesOn.
<plugin>--v<version> git-tag convention they resolve against, and cross-marketplace trust, see Plugin dependencies.
To have Claude Code suggest a plugin when a project matches it, add a relevance block to the entry with the signals that identify the project. Users see suggestions from your marketplace only when an admin lists it in pluginSuggestionMarketplaces. For the signals and the enablement step, see Plugin relevance.
Work around what a marketplace can’t do
Some things owners ask for have no field inmarketplace.json. Here is the nearest option for each:
- Restrict what else users install: the marketplace allowlist is a managed setting,
strictKnownMarketplaces. See Restrict what users can install. - Install or enable a plugin without the user asking: no entry field installs a plugin. Managed
enabledPluginsdoes that for a fleet; see Pre-install and require plugins. - Show different entries to different users: entries carry no audience field, and every user who adds the marketplace sees the whole catalog. Host separate marketplaces for separate audiences.
- Mark a plugin deprecated: there is no deprecation state. The option is to remove the entry, map its name to
nullinrenames, and optionally setforceRemoveDeletedPlugins. - Turn on auto-update for your users: each user turns it on under Marketplaces in
/plugin, or an admin setsautoUpdatein managed settings. See Turn on auto-update. - Carry git credentials: no marketplace field holds a git token. Access to a git-hosted marketplace or plugin follows the user’s git setup, per Grant access to a private marketplace. For
archivesources, an entry can setheadersorheadersHelperinstead.
Next steps
- Marketplace reference:
marketplace.jsonfields, source types, and validation messages - Manage plugins for your organization: require, restrict, or seed your marketplace across your organization’s machines
- Plugin dependencies: tag releases so plugins that depend on yours can resolve versions
- Troubleshoot plugins: the errors your users see when adding or updating from your marketplace