workflowDistribution & Governance6 min read
Plugins & Private Marketplaces
The missing distribution layer. You already wrote the skills, the hooks and the subagents. A plugin turns that pile of files into one versioned install command, and a private marketplace turns it into the default setup for everyone in your org.
A plugin is a self-contained directory that bundles skills, subagents, hooks, MCP servers, LSP servers and default settings behind a plugin.json manifest. You publish plugins from a marketplace.json catalog in a git repo, teammates install with one command, and managed settings let an admin decide which marketplaces are allowed at all.
The Problem
Your best engineer builds a genuinely good setup: a frontend-release skill, a PostToolUse hook that runs the linter on every write, a security-reviewer subagent, the Figma MCP server wired up correctly. It lives in their .claude/ directory and their head.
Everyone else gets the README version. Six weeks later the team has four different setups, three of them stale, and nobody can answer "what does our agent actually enforce?" The hook that blocked bad merges is running on one laptop.
Copy-paste does not survive contact with a team. Skills solved "how do we encode a procedure." Plugins solve "how does that procedure reach the other twelve people, in a version we can review and roll back."
What It Is
Two layers.
A plugin is a directory with a .claude-plugin/plugin.json manifest. Everything else sits at the plugin root and is optional:
frontend-factory/├── .claude-plugin/│ └── plugin.json # name, description, version, author├── skills/│ └── frontend-release/│ └── SKILL.md # invoked as /frontend-factory:frontend-release├── agents/│ └── security-reviewer.md├── hooks/│ └── hooks.json # PostToolUse, PreToolUse, TaskCompleted...├── .mcp.json # MCP servers the plugin brings with it├── .lsp.json # language servers for code intelligence├── bin/ # executables added to PATH while enabled└── settings.json # defaults applied when the plugin is enabledOne warning worth internalizing, because it is the most common setup failure: only plugin.json goes inside .claude-plugin/. skills/, agents/ and hooks/ live at the plugin root. Put them in the wrong place and the plugin loads silently doing nothing.
A marketplace is a .claude-plugin/marketplace.json at the root of a git repo, listing the plugins it publishes and where each one comes from:
{ "name": "acme-frontend", "owner": { "name": "Platform Team", "email": "[email protected]" }, "plugins": [ { "name": "frontend-factory", "source": "./plugins/frontend-factory", "description": "Design system guardrails, release checklist, review agents", "version": "1.4.0" }, { "name": "deploy-tools", "source": { "source": "github", "repo": "acme/deploy-plugin" }, "description": "Deployment automation" } ]}Plugin sources can be a relative path inside the same repo, another GitHub repo, a git URL, a subdirectory of a monorepo, an npm package, or a zip archive over HTTPS. Git sources pin to a ref or an exact sha, so "which version of our guardrails is this engineer running" has an answer.
Try It Locally First
You do not need a marketplace to start. Point Claude Code at a directory:
claude --plugin-dir ./frontend-factoryEdit, then /reload-plugins to pick up changes without restarting. When it works, push the repo and add it as a marketplace:
/plugin marketplace add acme/claude-plugins/plugin install frontend-factory@acme-frontendAlready have a .claude/ directory worth sharing? Migration is mechanical: copy skills/ and agents/ to the plugin root, move the hooks object out of settings.json into hooks/hooks.json (same format), add the manifest. Then delete the originals, because project-level .claude/agents/ definitions override same-named plugin agents and you will spend an afternoon wondering why your edits do nothing.
Make It the Default for the Repo
This is the part that matters for a team. Commit the marketplace into .claude/settings.json and anyone who trusts the project folder gets prompted to install it:
{ "extraKnownMarketplaces": { "acme-frontend": { "source": { "source": "github", "repo": "acme/claude-plugins" } } }, "enabledPlugins": { "frontend-factory@acme-frontend": true, "deploy-tools@acme-frontend": true }}Clone the repo, open the agent, and the team's skills, hooks and MCP servers are already there. No onboarding doc, no drift.
Private marketplaces work over your existing git credentials, with one caveat worth knowing before you debug it at 11pm: the background auto-update disables credential helpers for its git pull, so private HTTPS marketplaces fall back to a full re-clone that can time out. Set CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE=1 so a failed refresh keeps the last good clone instead of deleting it, and configure a credential helper (gh auth setup-git) so the fallback can authenticate. SSH remotes with a key in ssh-agent avoid the problem entirely.
For containers and CI, pre-populate the cache at image build time with CLAUDE_CODE_PLUGIN_CACHE_DIR, then point CLAUDE_CODE_PLUGIN_SEED_DIR at it. Runtime clones the network on every job otherwise.
Governance: The Enterprise Half
Plugins execute code, bring their own MCP servers and can register hooks. That is the whole point, and it is also the risk. Managed settings let an administrator decide what is installable at all, and individual users and projects cannot override them:
{ "strictKnownMarketplaces": [ { "source": "github", "repo": "anthropics/claude-plugins-official" }, { "source": "github", "repo": "acme/claude-plugins" } ]}Three behaviors, chosen by value: undefined means no restriction, an empty array [] blocks every marketplace including Anthropic's own, and a list is an exact-match allowlist. For a self-hosted GitHub Enterprise or GitLab instance, use a hostPattern entry instead of listing every repo. Pair it with disableSideloadFlags to reject the CLI flags that sideload plugins, agents and MCP servers for a single run, otherwise the allowlist is a suggestion.
The check runs before any network or filesystem access, and on update as well as install, so a marketplace that was added before the policy existed stops being installable the moment its source falls outside the allowlist.
Why This Matters for Senior Teams
- Guardrails become infrastructure.The hook that blocks a merge until types pass is not a convention you remind people about. It installs with the repo and runs on every machine.
- Your setup gets a version number and a diff.Bump
version, and everyone picks up the change on the next update. Something regressed, so pin the previousshaand investigate. Neither is possible with copy-pasted config. - Onboarding collapses to one prompt.A new engineer, a PM, or a designer clones the repo and inherits the same review agents and the same quality gates as your staff engineer. That is the whole non-engineer-ships-frontend story in one install.
- Security has an actual answer."Which MCP servers can run in our org" stops being a per-laptop question and becomes a line in managed settings.
- It survives the person who built it.The setup lives in a reviewed repository with an owner, not in one engineer's dotfiles.
Where It Fits in the Stack
- Directly after skills.Skills encode a procedure. Plugins distribute it. If you have written two or more skills your team should be using and only some of them are, you are past the point where a plugin pays for itself.
- Wraps AGENTS.md, not replaces it.AGENTS.md is committed project context every agent reads. A plugin ships the procedures and tooling that context refers to.
- Carries your MCP servers.Stop asking people to hand-edit MCP config. Put the server in
.mcp.jsoninside the plugin and it arrives with the install. - Standardizes Agent Teams.Define
security-revieweronce in the plugin'sagents/directory, and every teammate spawned on any machine loads the same definition. - Ships the review layer.Bundle the review skills your team actually uses, so a code review means the same thing across the org.
- Carries the autonomy envelope.The deny rules, sandbox keys and
PreToolUsepolicy hook from The Autonomy Ladder install with the plugin, so every machine runs the same boundaries.
When Not to Use It
If it is one skill and you are the only user, keep it in .claude/ and skip the ceremony. Plugins add a manifest, a repo and a release step, and that overhead only pays back when more than one person needs the same thing, or when you need to answer which version someone is running. Start standalone, convert when the second person asks for it.
