- A plugin is packaging: skills, agents, hooks and MCP servers installed together under one name and version.
.claude-plugin/plugin.jsonholds 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
versionchanges. - Teams:
extraKnownMarketplacesandenabledPluginsin managed settings or the repo's.claude/settings.json. - A plugin runs code as you. Read its hooks,
.mcp.jsonandbin/before installing.
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.
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.jsonThe 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 agentreviewerin plugindeploy-toolsappears asdeploy-tools:reviewer.version: a string, not checked against semver. Setting it pins users until you change it.description,author(object,namerequired),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.dependenciesanduserConfig(values Claude Code prompts for, withsensitive: truekeeping secrets out ofsettings.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"
}
]
}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-toolsFrom 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
}
}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:
claude plugin validate ./my-pluginchecks the manifest and every skill, agent and command frontmatter. Add--strictin CI.claude plugin validate ./my-marketplacechecks the JSON and each entry.claude plugin marketplace add ./my-marketplaceand install from it. A relative path to a missing directory passes validation and fails at install.claude plugin details my-pluginprints 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.
- Source.
claude plugin marketplace listprints where each marketplace came from. - Hooks. Read
hooks/hooks.jsonend to end. The details pane shows a hook exists, not what it runs. - MCP. Read
.mcp.json: each server's command or URL. - bin/. Everything there lands on the Bash tool's
PATH. - Inventory.
claude --plugin-dir <dir> plugin details <name>on a clone lists skills, agents, hooks with events, and servers. - Updates. With auto-update on, reviewed files can change. Pin a
shafor 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.shplugins/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" }
}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.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"]
}
]
}
]
}
}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 0For 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-toolsIn 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.
Sources
- Plugins overview
- Create a plugin
- Plugin manifest reference
- Hooks reference
- Convert an existing .claude/ setup
- Marketplace reference
- Host and maintain a marketplace
- Publish and distribute a plugin
- Manage plugins for your organization
- Submit to Anthropic's directory
- Plugin security and trust
Every product mentioned is available at wowhow.cloud — pay once, ship forever.
Written by
WOWHOW
The WOWHOW team brings 14+ years of production engineering experience. Every tool and product in the catalog is built, tested, and curated in-house.
Monday memo · free
One insight, every Monday. 7am IST.
1 field report, 3 links, 1 tool we actually use. No fluff, no spam.
Beyond the writing
Need production-ready templates?
Free browser tools with no signup, plus premium dev templates and starter kits.
Comments · 0
Beta: comments are stored locally on your device and not visible to other readers.
No comments yet. Be the first to share your thoughts.