DEV Community

Anup Karanjkar
Anup Karanjkar

Posted on Originally published at wowhow.cloud

Claude Code Plugin Marketplace 2026: Package and Publish

A Claude Code plugin is a directory that bundles skills, agents, hooks and MCP servers so Claude Code installs them as one unit, and a marketplace is a git repository with a .claude-plugin/marketplace.json file that lists plugins. Only name is required in the plugin manifest. Only name, owner and plugins are required in the marketplace file. If the repository is private, the marketplace is private. I checked every command and field below against the current Claude Code docs and claude plugin --help on version 2.1.299. Below: plugin vs skill vs MCP vs hook, the layout and manifest, plugin hooks vs settings.json hooks, the marketplace schema, updates, team rollout, a security checklist, and a plugin you can publish in 15 minutes.

TL;DR

  • A plugin is packaging: skills, agents, hooks and MCP servers installed together under one name and version.

  • .claude-plugin/plugin.json holds only the manifest. Everything else sits at the plugin root.

  • Publish a repo with .claude-plugin/marketplace.json. Users run /plugin marketplace add owner/repo, then /plugin install name@marketplace.

  • Updates arrive only when version changes.

  • Teams: extraKnownMarketplaces and enabledPlugins in managed settings or the repo's .claude/settings.json.

  • A plugin runs code as you. Read its hooks, .mcp.json and bin/ before installing.

Plugin vs skill vs MCP server vs hook

Skills, subagents, hooks and MCP servers all work on their own. A skill in ~/.claude/skills/ is available in every project on your machine[1]. Make a plugin when you want several pieces installed together, shared with teammates, or released with versions. Plugin skills and agents get the plugin name as a prefix, so two plugins can each ship a hello skill without colliding[2]. For the layer question itself, see MCP vs CLI vs skills.

Thing What it is Runs code? Where it lives Shareable as

| Skill | SKILL.md instructions Claude loads when relevant, also runnable as a command | No, it steers Claude | skills/<name>/SKILL.md | A folder, or inside a plugin |

| MCP server | Tool server Claude Code connects to | Yes, a stdio server is a process on your machine | .mcp.json | Config file, or inside a plugin |

| Hook | Command Claude Code runs at a lifecycle event | Yes, a shell command with your permissions | settings.json or hooks/hooks.json | Settings snippet, or inside a plugin |

| Plugin | Directory that bundles any mix of the above with a manifest | If it contains hooks, MCP servers, monitors or bin/ | Plugin root, with optional .claude-plugin/plugin.json | A marketplace entry, a zip, or --plugin-dir |

One cost: an enabled plugin is part of every session. The name and description of each skill, agent and command Claude can invoke on its own sit in context every turn, counting toward usage even when nothing from the plugin runs[1]. The Claude Max API credits planner shows what a heavier context footprint does to a monthly budget.

Plugin directory layout and the manifest

The layout is strict in one place. Put only plugin.json inside .claude-plugin/. Components saved there do not load[3]. Every other directory sits at the plugin root:

deploy-tools/
├── .claude-plugin/
│   └── plugin.json
├── skills/
│   └── deploy/
│       └── SKILL.md
├── agents/
│   └── reviewer.md
├── hooks/
│   └── hooks.json
├── bin/               (on the Bash tool PATH while enabled)
└── .mcp.json
Enter fullscreen mode Exit fullscreen mode

The manifest is optional. Without it, Claude Code loads components from these default locations and takes the name from the marketplace entry or directory[3]. Write one for metadata, a version, or components outside the defaults. Fields worth knowing:

  • name: the only required key. Kebab-case. Every component is namespaced under it, so agent reviewer in plugin deploy-tools appears as deploy-tools:reviewer.

  • version: a string, not checked against semver. Setting it pins users until you change it.

  • description, author (object, name required), homepage (must parse as a URL or the plugin fails to load), repository, license, keywords, displayName.

  • skills, commands, agents, hooks, mcpServers: paths or inline config for components outside the defaults. Paths start with ./ and stay inside the plugin root.

  • dependencies and userConfig (values Claude Code prompts for, with sensitive: true keeping secrets out of settings.json).

Skills live in skills/<name>/SKILL.md and run as /plugin-name:skill-name. Agents are Markdown files in agents/. Hooks go in hooks/hooks.json, MCP servers in .mcp.json. A CLAUDE.md at the plugin root is not loaded as context, so put instructions in a skill[3]. Names starting with claude- or anthropic- fail validation. An unknown top-level key is stripped with only a warning.

hooks/hooks.json vs settings.json hooks

The event, matcher and handler nesting is identical. Two differences matter. A plugin hooks file wraps the event map in a top-level "hooks" key, and a bare event map fails to load, while inline hooks in plugin.json have no wrapper[3]. And bundled scripts are referenced through ${CLAUDE_PLUGIN_ROOT}, which changes on every update (use ${CLAUDE_PLUGIN_DATA} for state that must survive). Plugin hooks merge with your user and project hooks[4]. Hooks have no prefix, so a hook migrated into a plugin but left in settings.json runs twice[5].

The marketplace file

A marketplace is a directory or repository with .claude-plugin/marketplace.json. Required top-level keys are name, owner (with name) and plugins. Each plugin entry needs name and source[6].

{
  "name": "team-tools",
  "description": "Plugins for my team",
  "owner": { "name": "Your Name" },
  "plugins": [
    {
      "name": "no-bypass",
      "source": "./plugins/no-bypass",
      "description": "Blocks git --no-verify and teaches commit hygiene"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The source is a relative path from the marketplace root (the directory containing .claude-plugin/, never with ..) or an object: github (repo, ref, sha), url, git-subdir, npm, archive (zip over HTTPS, optional sha256 pin) or command[6]. Keep the entry name identical to the one in the plugin's plugin.json, or installing by the manifest name reports the plugin as not found[7]. Names like claude-plugins-official and claude-community are accepted only for marketplaces sourced from github.com/anthropics/[6].

Install and manage

Users register a marketplace once, then install by name. Inside a session:

/plugin marketplace add your-org/your-marketplace
/plugin install no-bypass@team-tools
Enter fullscreen mode Exit fullscreen mode

From a shell: claude plugin marketplace add and claude plugin install no-bypass@team-tools. The add command takes a GitHub owner/repo, a git URL, a hosted marketplace.json URL, or a local path. Append #<ref> to pin a branch or tag. Shorthand always means github.com, so GitLab users need the full URL. A URL-hosted marketplace downloads only the JSON, so relative-path entries fail there[7].

Versioning and updates

This is where people lose an afternoon. Users get a new copy only when the computed version changes. Set "version": "1.0.0", push ten commits, and claude plugin update reports the plugin is already at the latest version[8]. Either bump version on every release or omit it from both plugin.json and the marketplace entry so the commit SHA decides. Never set it in both: plugin.json wins silently[7].

Auto-update is off by default and marketplace.json has no field for it. Each user enables it under Marketplaces in /plugin, or an admin sets "autoUpdate": true in managed settings[7]. Otherwise users run claude plugin update name@marketplace. Pin a release with ref or a 40-character sha on the entry. claude plugin tag creates a {name}--v{version} git tag[8]. Never rename a published plugin; add a renames map to the marketplace instead[7].

Team distribution

For a team, a private GitHub repository is enough: anyone who can clone it can install from it. Claude Code has no git token of its own and marketplace.json has no credential field, so installs use the machine's git credentials. For HTTPS, gh auth login then gh auth setup-git stores one. Without a GitHub SSH key, set CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1. The catch: auto-update against a private repo needs a stored credential, and a helper that has to prompt fails quietly[7].

To register the marketplace for one repository, run claude plugin marketplace add your-org/your-marketplace --scope project and commit the .claude/settings.json it writes. It applies once a contributor trusts the folder. Company-wide, the verified setting names go in managed settings:

{
  "extraKnownMarketplaces": {
    "team-tools": {
      "source": { "source": "github", "repo": "your-org/team-tools" },
      "autoUpdate": true
    }
  },
  "enabledPlugins": {
    "no-bypass@team-tools": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Managed settings win over every other scope, so a user disabling the plugin does not stop it loading; false in managed enabledPlugins blocks a plugin everywhere[9]. strictKnownMarketplaces (alias allowedMarketplaces) is an allowlist, blockedMarketplaces a blocklist checked first, and disableSideloadFlags rejects --plugin-dir. An empty allowlist blocks every source, official marketplace included, and any allowlist stops plugins under ~/.claude/skills/ unless you add { "source": "skills-dir" }[9]. The aliases need v2.1.232 or later.

The official marketplace and the world

Claude Code registers the official marketplace (claude-plugins-official) on first interactive start unless managed policy blocks it. Community and demo marketplaces are not added automatically[1]. To reach everyone, publish a public marketplace repo and share the two commands, or submit to Anthropic's directory at claude.ai/directory/manage (paid claude.ai plan required; it reaches Claude Code through account sync as name@synced). The official marketplace takes no submissions through that portal[10]. Anthropic sorts marketplaces into official, community and third-party tiers by name, and says the name tells you who publishes the catalog, not what each plugin does[11].

Test locally before you publish

Load a plugin for one session with claude --plugin-dir ./my-plugin (repeatable, also accepts a .zip). Point it at one plugin's root, not a marketplace root, or nothing loads and you get no error. After edits, run /reload-plugins[2]. Then:

  1. claude plugin validate ./my-plugin checks the manifest and every skill, agent and command frontmatter. Add --strict in CI.

  2. claude plugin validate ./my-marketplace checks the JSON and each entry.

  3. claude plugin marketplace add ./my-marketplace and install from it. A relative path to a missing directory passes validation and fails at install.

  4. claude plugin details my-plugin prints the component inventory.

The docs name CLAUDE_CONFIG_DIR as the configuration directory Claude Code uses in place of ~/.claude[7] but publish no isolated-install walkthrough, so try that on a scratch machine first.

Security checklist before installing a third-party plugin

Anthropic's wording: a plugin can execute arbitrary code on your machine with your user privileges[11]. Hooks, monitors, MCP servers and LSP servers run outside the sandbox and your permission rules. Skills and agents enter context as instructions. Read a plugin like an install script. For the container side, see sandboxing Claude Code and Codex.

  1. Source. claude plugin marketplace list prints where each marketplace came from.

  2. Hooks. Read hooks/hooks.json end to end. The details pane shows a hook exists, not what it runs.

  3. MCP. Read .mcp.json: each server's command or URL.

  4. bin/. Everything there lands on the Bash tool's PATH.

  5. Inventory. claude --plugin-dir <dir> plugin details <name> on a clone lists skills, agents, hooks with events, and servers.

  6. Updates. With auto-update on, reviewed files can change. Pin a sha for marketplaces you do not control.

Uninstall with claude plugin uninstall at the scope you installed; cached files stay under ~/.claude/plugins/cache/ for 14 days[11].

A complete example in 15 minutes

One repo, one marketplace, one plugin named no-bypass with a skill and a PreToolUse hook that blocks git ... --no-verify.

team-tools/
├── .claude-plugin/
│   └── marketplace.json
└── plugins/
    └── no-bypass/
        ├── .claude-plugin/
        │   └── plugin.json
        ├── skills/
        │   └── commit-hygiene/
        │       └── SKILL.md
        ├── hooks/
        │   └── hooks.json
        └── scripts/
            └── block-no-verify.sh
Enter fullscreen mode Exit fullscreen mode

plugins/no-bypass/.claude-plugin/plugin.json

{
  "name": "no-bypass",
  "description": "Blocks git --no-verify and teaches commit hygiene",
  "version": "1.0.0",
  "author": { "name": "Your Name" }
}
Enter fullscreen mode Exit fullscreen mode

plugins/no-bypass/skills/commit-hygiene/SKILL.md

---
name: commit-hygiene
description: Commit rules for this repo. Use before running git commit.
---

Never bypass git hooks. If a pre-commit hook fails, read the output,
fix the cause, and commit again. Do not pass --no-verify.
Keep commits small.
Enter fullscreen mode Exit fullscreen mode

plugins/no-bypass/hooks/hooks.json (note the top-level "hooks" wrapper; exec form with args keeps the path as a single argument)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash",
            "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/block-no-verify.sh"]
          }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

plugins/no-bypass/scripts/block-no-verify.sh

#!/bin/bash
# Reads the PreToolUse JSON on stdin. Exit 2 blocks the call and
# shows stderr to Claude. Needs jq on PATH.
command=$(jq -r '.tool_input.command // ""')

if echo "$command" | grep -qE 'git[[:space:]].*--no-verify'; then
  echo "Blocked: --no-verify skips git hooks. Fix the failing check instead." >&2
  exit 2
fi

exit 0
Enter fullscreen mode Exit fullscreen mode

For a Bash call the hook receives JSON on stdin with the command in tool_input.command. Exit code 2 blocks the call and shows stderr to Claude[4]. Use one blocking method per hook. The short flag git commit -n does the same as --no-verify and this regex misses it, so extend the pattern if that matters.

.claude-plugin/marketplace.json at the repo root is the one shown earlier. Then:

claude plugin validate ./team-tools
claude plugin validate ./team-tools/plugins/no-bypass --strict
claude --plugin-dir ./team-tools/plugins/no-bypass
git init && git add . && git commit -m "no-bypass 1.0.0"
# push to github.com/your-org/team-tools, then on any machine:
claude plugin marketplace add your-org/team-tools
claude plugin install no-bypass@team-tools
Enter fullscreen mode Exit fullscreen mode

In the --plugin-dir session, ask Claude to run git commit --no-verify -m test. The call is blocked and Claude sees your message. For a marketplace-ready example you can install and read, the Opus 5.5 Claude Code Plugin ships five guard hooks, effort presets and ten skills in exactly this layout; pair it with the Opus 5.5 effort settings and CLAUDE.md rules.

Quick answers

Do I need a plugin, or is a skill enough?

A skill is enough while it serves you or one project. Make a plugin to install several components together, share them across projects, or release versions.

Why did my users not get my update?

The version did not change. With version set in plugin.json, claude plugin update reports already latest until you bump it. Or omit it so the commit SHA decides.

How do I roll a plugin out to a whole team?

Private repo, then extraKnownMarketplaces and enabledPlugins in the repo's .claude/settings.json or in managed settings. Repository entries apply after each contributor trusts the folder. Managed settings cannot be overridden.

Is a plugin from an official or community marketplace safe?

Not automatically. Anthropic says a plugin can run code with your user privileges. Review hooks, MCP config and bin/ whatever the tier.

Can a plugin hook replace a settings.json hook?

Yes, with the same event and matcher structure, wrapped in a top-level "hooks" key. Remove the original afterwards or it runs twice.

Sources

  1. Plugins overview

  2. Create a plugin

  3. Plugin manifest reference

  4. Hooks reference

  5. Convert an existing .claude/ setup

  6. Marketplace reference

  7. Host and maintain a marketplace

  8. Publish and distribute a plugin

  9. Manage plugins for your organization

  10. Submit to Anthropic's directory

  11. Plugin security and trust

Every product mentioned is available at wowhow.cloud — pay once, ship forever.

Originally published at wowhow.cloud

Top comments (1)

Collapse
 
autenai profile image
Auten •

Useful section on the version trap. One more case we hit shipping an MCP server as a plugin (I'm on the Auten team): the server starts with npx -y <package> in .mcp.json, so two versions are in play.

Unpinned, npx -y can run a newer npm release between sessions with no plugin update at all, which quietly sidesteps your point 6: the files the user reviewed stay the same while the code that runs changes. Pinned (<package>@0.1.5), the server only moves when you also bump the plugin version, so every npm release needs a plugin bump in the same commit or users stay on the old server. We pin and bump both together.

For checklist item 3 I'd add: check whether an npx or uvx server in .mcp.json is pinned to a version.

Do you know if claude plugin validate --strict warns about an unpinned npx command, or does it leave that to the reader?