[proxy] code.claude.com← back | site home | direct (HTTPS) ↗ | proxy home | ◑ dark◐ light

Create and distribute a plugin marketplace - Claude Code Docs

A plugin marketplace is a catalog that lets you distribute plugins to others. Marketplaces provide centralized discovery, version tracking, automatic updates, and support for multiple source types (git repositories, local paths, and more). This guide shows you how to create your own marketplace to share plugins with your team or community. Looking to install plugins from an existing marketplace? See Discover and install prebuilt plugins.

Overview

Creating and distributing a marketplace involves:

  1. Creating plugins: build one or more plugins with commands, agents, hooks, MCP servers, or LSP servers. This guide assumes you already have plugins to distribute; see Create plugins for details on how to create them.
  2. Creating a marketplace file: define a marketplace.json that lists your plugins and where to find them (see Create the marketplace file).
  3. Host the marketplace: push to GitHub, GitLab, or another git host (see Host and distribute marketplaces).
  4. Share with users: users add your marketplace with /plugin marketplace add and install individual plugins (see Discover and install plugins).

Once your marketplace is live, you can update it by pushing changes to your repository. Users refresh their local copy with /plugin marketplace update.

Walkthrough: create a local marketplace

This example creates a marketplace with one plugin: a /review skill for code reviews. You’ll create the directory structure, add a skill, create the plugin manifest and marketplace catalog, then install and test it.

1

2

3

4

5

6

To learn more about what plugins can do, including hooks, agents, MCP servers, and LSP servers, see Plugins.

Create the marketplace file

Create .claude-plugin/marketplace.json in your repository root. This file defines your marketplace’s name, owner information, and a list of plugins with their sources. Each plugin entry needs at minimum a name and source (where to fetch it from). See the full schema below for all available fields.

{
  "name": "company-tools",
  "owner": {
    "name": "DevTools Team",
    "email": "[email protected]"
  },
  "plugins": [
    {
      "name": "code-formatter",
      "source": "./plugins/formatter",
      "description": "Automatic code formatting on save",
      "version": "2.1.0",
      "author": {
        "name": "DevTools Team"
      }
    },
    {
      "name": "deployment-tools",
      "source": {
        "source": "github",
        "repo": "company/deploy-plugin"
      },
      "description": "Deployment automation tools"
    }
  ]
}

Marketplace schema

Required fields

FieldTypeDescriptionExample
namestringMarketplace identifier (kebab-case, no spaces). This is public-facing: users see it when installing plugins (for example, /plugin install my-tool@your-marketplace)."acme-tools"
ownerobjectMarketplace maintainer information (see fields below)
pluginsarrayList of available pluginsSee below

Owner fields

FieldTypeRequiredDescription
namestringYesName of the maintainer or team
emailstringNoContact email for the maintainer

Optional metadata

FieldTypeDescription
metadata.descriptionstringBrief marketplace description
metadata.versionstringMarketplace version
metadata.pluginRootstringBase directory prepended to relative plugin source paths (for example, "./plugins" lets you write "source": "formatter" instead of "source": "./plugins/formatter")

Plugin entries

Each plugin entry in the plugins array describes a plugin and where to find it. You can include any field from the plugin manifest schema (like description, version, author, commands, hooks, etc.), plus these marketplace-specific fields: source, category, tags, and strict.

Required fields

FieldTypeDescription
namestringPlugin identifier (kebab-case, no spaces). This is public-facing: users see it when installing (for example, /plugin install my-plugin@marketplace).
sourcestring|objectWhere to fetch the plugin from (see Plugin sources below)

Optional plugin fields

Standard metadata fields:

FieldTypeDescription
descriptionstringBrief plugin description
versionstringPlugin version
authorobjectPlugin author information (name required, email optional)
homepagestringPlugin homepage or documentation URL
repositorystringSource code repository URL
licensestringSPDX license identifier (for example, MIT, Apache-2.0)
keywordsarrayTags for plugin discovery and categorization
categorystringPlugin category for organization
tagsarrayTags for searchability
strictbooleanControls whether plugin.json is the authority for component definitions (default: true). See Strict mode below.

Component configuration fields:

FieldTypeDescription
commandsstring|arrayCustom paths to command files or directories
agentsstring|arrayCustom paths to agent files
hooksstring|objectCustom hooks configuration or path to hooks file
mcpServersstring|objectMCP server configurations or path to MCP config
lspServersstring|objectLSP server configurations or path to LSP config

Plugin sources

Plugin sources tell Claude Code where to fetch each individual plugin listed in your marketplace. These are set in the source field of each plugin entry in marketplace.json. Once a plugin is cloned or copied into the local machine, it is copied into the local versioned plugin cache at ~/.claude/plugins/cache.

SourceTypeFieldsNotes
Relative pathstring (e.g. "./my-plugin")Local directory within the marketplace repo. Must start with ./
githubobjectrepo, ref?, sha?
urlobjecturl (must end .git), ref?, sha?Git URL source
npmobjectpackage, version?, registry?Installed via npm install
pipobjectpackage, version?, registry?Installed via pip

Relative paths

For plugins in the same repository:

{
  "name": "my-plugin",
  "source": "./plugins/my-plugin"
}

GitHub repositories

{
  "name": "github-plugin",
  "source": {
    "source": "github",
    "repo": "owner/plugin-repo"
  }
}

You can pin to a specific branch, tag, or commit:

{
  "name": "github-plugin",
  "source": {
    "source": "github",
    "repo": "owner/plugin-repo",
    "ref": "v2.0.0",
    "sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
  }
}
FieldTypeDescription
repostringRequired. GitHub repository in owner/repo format
refstringOptional. Git branch or tag (defaults to repository default branch)
shastringOptional. Full 40-character git commit SHA to pin to an exact version

Git repositories

{
  "name": "git-plugin",
  "source": {
    "source": "url",
    "url": "https://gitlab.com/team/plugin.git"
  }
}

You can pin to a specific branch, tag, or commit:

{
  "name": "git-plugin",
  "source": {
    "source": "url",
    "url": "https://gitlab.com/team/plugin.git",
    "ref": "main",
    "sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
  }
}
FieldTypeDescription
urlstringRequired. Full git repository URL (must end with .git)
refstringOptional. Git branch or tag (defaults to repository default branch)
shastringOptional. Full 40-character git commit SHA to pin to an exact version

npm packages

Plugins distributed as npm packages are installed using npm install. This works with any package on the public npm registry or a private registry your team hosts.

{
  "name": "my-npm-plugin",
  "source": {
    "source": "npm",
    "package": "@acme/claude-plugin"
  }
}

To pin to a specific version, add the version field:

{
  "name": "my-npm-plugin",
  "source": {
    "source": "npm",
    "package": "@acme/claude-plugin",
    "version": "2.1.0"
  }
}

To install from a private or internal registry, add the registry field:

{
  "name": "my-npm-plugin",
  "source": {
    "source": "npm",
    "package": "@acme/claude-plugin",
    "version": "^2.0.0",
    "registry": "https://npm.example.com"
  }
}
FieldTypeDescription
packagestringRequired. Package name or scoped package (for example, @org/plugin)
versionstringOptional. Version or version range (for example, 2.1.0, ^2.0.0, ~1.5.0)
registrystringOptional. Custom npm registry URL. Defaults to the system npm registry (typically npmjs.org)

Advanced plugin entries

This example shows a plugin entry using many of the optional fields, including custom paths for commands, agents, hooks, and MCP servers:

{
  "name": "enterprise-tools",
  "source": {
    "source": "github",
    "repo": "company/enterprise-plugin"
  },
  "description": "Enterprise workflow automation tools",
  "version": "2.1.0",
  "author": {
    "name": "Enterprise Team",
    "email": "[email protected]"
  },
  "homepage": "https://docs.example.com/plugins/enterprise-tools",
  "repository": "https://github.com/company/enterprise-plugin",
  "license": "MIT",
  "keywords": ["enterprise", "workflow", "automation"],
  "category": "productivity",
  "commands": [
    "./commands/core/",
    "./commands/enterprise/",
    "./commands/experimental/preview.md"
  ],
  "agents": ["./agents/security-reviewer.md", "./agents/compliance-checker.md"],
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
          }
        ]
      }
    ]
  },
  "mcpServers": {
    "enterprise-db": {
      "command": "${CLAUDE_PLUGIN_ROOT}/servers/db-server",
      "args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"]
    }
  },
  "strict": false
}

Key things to notice:

Strict mode

The strict field controls whether plugin.json is the authority for component definitions (commands, agents, hooks, skills, MCP servers, output styles).

ValueBehavior
true (default)plugin.json is the authority. The marketplace entry can supplement it with additional components, and both sources are merged.
falseThe marketplace entry is the entire definition. If the plugin also has a plugin.json that declares components, that’s a conflict and the plugin fails to load.

When to use each mode:

Host and distribute marketplaces

GitHub provides the easiest distribution method:

  1. Create a repository: Set up a new repository for your marketplace
  2. Add marketplace file: Create .claude-plugin/marketplace.json with your plugin definitions
  3. Share with teams: Users add your marketplace with /plugin marketplace add owner/repo

Benefits: Built-in version control, issue tracking, and team collaboration features.

Host on other git services

Any git hosting service works, such as GitLab, Bitbucket, and self-hosted servers. Users add with the full repository URL:

/plugin marketplace add https://gitlab.com/company/plugins.git

Private repositories

Claude Code supports installing plugins from private repositories. For manual installation and updates, Claude Code uses your existing git credential helpers. If git clone works for a private repository in your terminal, it works in Claude Code too. Common credential helpers include gh auth login for GitHub, macOS Keychain, and git-credential-store. Background auto-updates run at startup without credential helpers, since interactive prompts would block Claude Code from starting. To enable auto-updates for private marketplaces, set the appropriate authentication token in your environment:

ProviderEnvironment variablesNotes
GitHubGITHUB_TOKEN or GH_TOKENPersonal access token or GitHub App token
GitLabGITLAB_TOKEN or GL_TOKENPersonal access token or project token
BitbucketBITBUCKET_TOKENApp password or repository access token

Set the token in your shell configuration (for example, .bashrc, .zshrc) or pass it when running Claude Code:

export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

Test locally before distribution

Test your marketplace locally before sharing:

/plugin marketplace add ./my-local-marketplace
/plugin install test-plugin@my-local-marketplace

For the full range of add commands (GitHub, Git URLs, local paths, remote URLs), see Add marketplaces.

Require marketplaces for your team

You can configure your repository so team members are automatically prompted to install your marketplace when they trust the project folder. Add your marketplace to .claude/settings.json:

{
  "extraKnownMarketplaces": {
    "company-tools": {
      "source": {
        "source": "github",
        "repo": "your-org/claude-plugins"
      }
    }
  }
}

You can also specify which plugins should be enabled by default:

{
  "enabledPlugins": {
    "code-formatter@company-tools": true,
    "deployment-tools@company-tools": true
  }
}

For full configuration options, see Plugin settings.

Managed marketplace restrictions

For organizations requiring strict control over plugin sources, administrators can restrict which plugin marketplaces users are allowed to add using the strictKnownMarketplaces setting in managed settings. When strictKnownMarketplaces is configured in managed settings, the restriction behavior depends on the value:

ValueBehavior
Undefined (default)No restrictions. Users can add any marketplace
Empty array []Complete lockdown. Users cannot add any new marketplaces
List of sourcesUsers can only add marketplaces that match the allowlist exactly

Common configurations

Disable all marketplace additions:

{
  "strictKnownMarketplaces": []
}

Allow specific marketplaces only:

{
  "strictKnownMarketplaces": [
    {
      "source": "github",
      "repo": "acme-corp/approved-plugins"
    },
    {
      "source": "github",
      "repo": "acme-corp/security-tools",
      "ref": "v2.0"
    },
    {
      "source": "url",
      "url": "https://plugins.example.com/marketplace.json"
    }
  ]
}

Allow all marketplaces from an internal git server using regex pattern matching:

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

How restrictions work

Restrictions are validated early in the plugin installation process, before any network requests or filesystem operations occur. This prevents unauthorized marketplace access attempts. The allowlist uses exact matching for most source types. For a marketplace to be allowed, all specified fields must match exactly:

Because strictKnownMarketplaces is set in managed settings, individual users and project configurations cannot override these restrictions. For complete configuration details including all supported source types and comparison with extraKnownMarketplaces, see the strictKnownMarketplaces reference.

Version resolution and release channels

Plugin versions determine cache paths and update detection. You can specify the version in the plugin manifest (plugin.json) or in the marketplace entry (marketplace.json).

Set up release channels

To support “stable” and “latest” release channels for your plugins, you can set up two marketplaces that point to different refs or SHAs of the same repo. You can then assign the two marketplaces to different user groups through managed settings.

Example
{
  "name": "stable-tools",
  "plugins": [
    {
      "name": "code-formatter",
      "source": {
        "source": "github",
        "repo": "acme-corp/code-formatter",
        "ref": "stable"
      }
    }
  ]
}
{
  "name": "latest-tools",
  "plugins": [
    {
      "name": "code-formatter",
      "source": {
        "source": "github",
        "repo": "acme-corp/code-formatter",
        "ref": "latest"
      }
    }
  ]
}
Assign channels to user groups

Assign each marketplace to the appropriate user group through managed settings. For example, the stable group receives:

{
  "extraKnownMarketplaces": {
    "stable-tools": {
      "source": {
        "source": "github",
        "repo": "acme-corp/stable-tools"
      }
    }
  }
}

The early-access group receives latest-tools instead:

{
  "extraKnownMarketplaces": {
    "latest-tools": {
      "source": {
        "source": "github",
        "repo": "acme-corp/latest-tools"
      }
    }
  }
}

Validation and testing

Test your marketplace before sharing. Validate your marketplace JSON syntax:

claude plugin validate .

Or from within Claude Code:

/plugin validate .

Add the marketplace for testing:

/plugin marketplace add ./path/to/marketplace

Install a test plugin to verify everything works:

/plugin install test-plugin@marketplace-name

For complete plugin testing workflows, see Test your plugins locally. For technical troubleshooting, see Plugins reference.

Troubleshooting

Marketplace not loading

Symptoms: Can’t add marketplace or see plugins from it Solutions:

Marketplace validation errors

Run claude plugin validate . or /plugin validate . from your marketplace directory to check for issues. Common errors:

ErrorCauseSolution
File not found: .claude-plugin/marketplace.jsonMissing manifestCreate .claude-plugin/marketplace.json with required fields
Invalid JSON syntax: Unexpected token...JSON syntax errorCheck for missing commas, extra commas, or unquoted strings
Duplicate plugin name "x" found in marketplaceTwo plugins share the same nameGive each plugin a unique name value
plugins[0].source: Path traversal not allowedSource path contains ..Use paths relative to marketplace root without ..

Warnings (non-blocking):

Plugin installation failures

Symptoms: Marketplace appears but plugin installation fails Solutions:

Private repository authentication fails

Symptoms: Authentication errors when installing plugins from private repositories Solutions: For manual installation and updates:

For background auto-updates:

Git operations time out

Symptoms: Plugin installation or marketplace updates fail with a timeout error like “Git clone timed out after 120s” or “Git pull timed out after 120s”. Cause: Claude Code uses a 120-second timeout for all git operations, including cloning plugin repositories and pulling marketplace updates. Large repositories or slow network connections may exceed this limit. Solution: Increase the timeout using the CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS environment variable. The value is in milliseconds:

export CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS=300000  # 5 minutes

Plugins with relative paths fail in URL-based marketplaces

Symptoms: Added a marketplace via URL (such as https://example.com/marketplace.json), but plugins with relative path sources like "./plugins/my-plugin" fail to install with “path not found” errors. Cause: URL-based marketplaces only download the marketplace.json file itself. They do not download plugin files from the server. Relative paths in the marketplace entry reference files on the remote server that were not downloaded. Solutions:

Files not found after installation

Symptoms: Plugin installs but references to files fail, especially files outside the plugin directory Cause: Plugins are copied to a cache directory rather than used in-place. Paths that reference files outside the plugin’s directory (such as ../shared-utils) won’t work because those files aren’t copied. Solutions: See Plugin caching and file resolution for workarounds including symlinks and directory restructuring. For additional debugging tools and common issues, see Debugging and development tools.

See also