Module 12 — Plugins and marketplaces: package and share your tooling
The previous modules built separately what makes Claude Code valuable inside a team: skills, subagents, hooks, MCP servers. Each piece lives in its own corner. The closing module brings them together: a plugin is the single package that groups everything, versioned, installable in one command, shareable through a marketplace. It is the format to favor as soon as a team exceeds two people.
Plugin or standalone configuration?
Stay on standalone .claude/ when the content is specific to a repo. Move to a plugin when the piece is reusable across projects, must be versioned independently, must install on other teams, or groups several components (skill + subagent + hooks + MCP server).
A plugin can pack everything: skills, subagents, workflows, hooks, MCP servers, LSP servers, monitors, themes, output styles, executables. It exposes all of them under a namespace (kiosque-tools:deploy) that avoids collisions.
Anatomy of a plugin
A plugin is a folder with a single manifest at .claude-plugin/plugin.json. Every other component lives at the plugin root, not inside .claude-plugin/. A frequent confusion: putting agents/ or hooks/ inside .claude-plugin/ — Claude Code will not find them.
Reference structure:
my-plugin/
├── .claude-plugin/
│ └── plugin.json # Manifest (optional if everything is at the default locations)
├── skills/ # <name>/SKILL.md per skill
│ └── deploy/SKILL.md
├── commands/ # Skills as flat .md files (compatible; prefer skills/)
├── agents/ # Subagents (.md with frontmatter)
│ └── reviewer.md
├── workflows/ # Dynamic workflows
├── hooks/
│ └── hooks.json # Plugin hooks
├── .mcp.json # MCP servers provided by the plugin
├── .lsp.json # LSP servers
├── monitors/monitors.json # Background monitors
├── bin/ # Executables added to PATH when the plugin is active
└── settings.json # Default settings (only `agent` and `subagentStatusLine` keys are read)
A plugin that only exposes one skill can put SKILL.md directly at the plugin root, without a skills/ folder. For anything that grows, use skills/<name>/SKILL.md.
The plugin.json manifest
Only name is required, and the manifest itself is optional: without it, Claude Code discovers components at the default locations and derives the name from the folder. A minimal manifest:
{
"name": "kiosque-tools",
"displayName": "Kiosque Tools",
"version": "1.0.0",
"description": "Kiosque internal tooling: review, tests, deployment.",
"author": { "name": "Kiosque team", "email": "dev@kiosque.example" }
}
Excerpt of documented fields:
| Field | Role |
|---|---|
name | kebab-case identifier, serves as namespace (kiosque-tools:deploy) |
displayName / description | Shown in /plugin |
version | Semver; pins the install |
author, homepage, repository, license, keywords | Discovery metadata |
defaultEnabled | false = installs disabled (opt-in) |
skills, commands, agents, workflows, hooks, mcpServers, lspServers | Custom paths to components |
dependencies | Other required plugins, with semver constraints |
userConfig, channels | User configuration, messaging channels |
experimental.themes, experimental.monitors | Components with an evolving schema |
Unknown root fields are ignored — useful for coexisting with a npm package.json.
Three key environment variables
${CLAUDE_PLUGIN_ROOT}— absolute path of the install folder. For packaged scripts and binaries.${CLAUDE_PLUGIN_DATA}— persistent folder that survives updates (~/.claude/plugins/data/{id}/). For dependencies installed at first use.${CLAUDE_PROJECT_DIR}— project root.
All three are exported to hook, MCP, and LSP processes, and substituted in the content of skills, agents, hook/monitor commands, command/args/env of a stdio MCP, url/headers of an HTTP one.
Develop, test, reload
Four commands set the development rhythm.
- Bootstrap:
claude plugin init <name>creates a plugin in~/.claude/skills/<name>/with a manifest and a starterSKILL.md, loaded at the next session. - Local testing:
claude --plugin-dir ./my-pluginloads the plugin for the session. Stackable for several plugins. If the name conflicts with an installed plugin, the local copy takes precedence. - Reload:
/reload-pluginsapplies changes (skills, subagents, hooks, MCP, LSP) without restarting.--forceaccepts the prompt cache invalidation. - Validate:
claude plugin validate ./my-plugin --strictin CI.
In session, /plugin opens the manager: Installed, Discover, Errors tabs, favorites (key f), name filter.
Marketplaces: find and distribute
A plugin is rarely installed through --plugin-dir in production; it comes from a marketplace, a catalog of plugins you add once and then install entries from at will.
Official marketplaces and adding sources
Two public marketplaces maintained by Anthropic: claude-plugins-official (registered automatically on the first interactive session) and claude-community (third-party submissions after review, add with /plugin marketplace add anthropics/claude-plugins-community).
Four possible sources to add:
/plugin marketplace add owner/repo # GitHub
/plugin marketplace add https://gitlab.com/company/plugins.git # Git (https:// + .git)
/plugin marketplace add ./my-marketplace # Local path
/plugin marketplace add https://example.com/marketplace.json # Remote JSON
Shortcut: /plugin market. Target a branch: .../plugins.git#v1.0.0.
Installing a plugin
/plugin install kiosque-tools@my-marketplace
Three scopes offered: User (all your projects), Project (shared, added to versioned .claude/settings.json), Local (you only). Since v2.1.221, the install sometimes activates the plugin in the current session; otherwise the prompt says Run /reload-plugins to activate.
To distribute internally, host the marketplace in a private repository; Claude Code uses your Git credentials to clone. Administrators can force a marketplace through managed settings (extraKnownMarketplaces).
Kiosque: the complete kiosque-tools plugin
The Kiosque team consolidates what it built in modules 9 to 11 into a single plugin, published in a private marketplace kiosque/internal-plugins.
Plugin structure
kiosque-tools/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ └── deploy/SKILL.md
├── agents/
│ ├── reviewer.md
│ └── tester.md
├── hooks/
│ └── hooks.json
├── .mcp.json
├── bin/
│ └── format-python.sh
└── README.md
plugin.json
{
"name": "kiosque-tools",
"displayName": "Kiosque Tools",
"version": "1.0.0",
"description": "Kiosque internal tooling: code review, tests, deployment, GitHub and Postgres access.",
"author": {
"name": "Kiosque team",
"email": "dev@kiosque.example",
"url": "https://kiosque.example"
},
"homepage": "https://kiosque.example/docs/plugin",
"repository": "https://gitlab.kiosque.example/dev/kiosque-tools",
"license": "UNLICENSED",
"keywords": ["kiosque", "review", "deploy", "postgres", "github"],
"defaultEnabled": true
}
hooks/hooks.json
Module 9's three hooks, this time portable: ${CLAUDE_PLUGIN_ROOT} replaces ${CLAUDE_PROJECT_DIR} for the embedded script, and the scripts against migrations/ and tests stay tied to the project.
{
"hooks": {
"PostToolUse": [
{ "matcher": "Edit|Write", "hooks": [
{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/bin/format-python.sh" }
]}
],
"PreToolUse": [
{ "matcher": "Edit|Write", "hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-paths.sh" }
]}
],
"Stop": [
{ "hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/require-green-tests.sh", "timeout": 300 }
]}
]
}
}
Note: Python formatting lives in the plugin (bin/format-python.sh), common to every Kiosque project. Hooks specific to the repo (path protection, local tests) stay in ${CLAUDE_PROJECT_DIR} — that is the rule: what varies per project stays per project.
.mcp.json
Module 11's MCP server becomes an integral part of the plugin. Secrets stay outside, in ${VAR}:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${GITHUB_MCP_TOKEN}" }
},
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${KIOSQUE_DB_DSN}"]
}
}
}
Subagents and skill
agents/reviewer.md and agents/tester.md reuse module 10's definitions, except they are now namespaced as kiosque-tools:reviewer and kiosque-tools:tester. Karim invokes them with @"kiosque-tools:reviewer (agent)".
skills/deploy/SKILL.md captures the recurring deployment:
---
description: Deploys the current version of Kiosque to the staging environment.
---
# /kiosque-tools:deploy
Chains the staging deployment steps:
1. Confirms the branch is clean (`git status --porcelain`).
2. Builds the Docker image tagged with the short SHA.
3. Pushes the image to `registry.kiosque.example`.
4. Updates the Kubernetes manifest and restarts the deployment.
5. Waits for `Ready` then runs the smoke suite `make smoke`.
Uses the `github` and `postgres` MCP servers declared by this plugin to check,
respectively, PR compliance and the health of the staging database.
Publish and install
Nadia commits kiosque-tools/ in the repo gitlab.kiosque.example/dev/internal-plugins, with a .claude-plugin/marketplace.json at the root that lists the team's plugins. Each developer adds the marketplace only once:
/plugin marketplace add https://gitlab.kiosque.example/dev/internal-plugins.git
/plugin install kiosque-tools@internal-plugins
Karim sees an install summary, picks the User scope (he wants the plugin in every Kiosque project). If the message says Run /reload-plugins to activate., he types the command. From the current session, /kiosque-tools:deploy, @"kiosque-tools:reviewer (agent)" and the hooks work.
A month later, the team ships 1.1.0 with a new subagent. Nadia bumps version, tags, pushes. Colleagues pick up the update on the next session, or immediately through /plugin marketplace update internal-plugins then /reload-plugins.
An ideal team plugin is project-agnostic: what works everywhere goes into the plugin, what varies per repo stays in the repo's .claude/. This separation avoids the antipattern "the plugin that knows everything about Kiosque and works nowhere else."
Summary
- A plugin packages skills, subagents, workflows, hooks, MCP and LSP servers, monitors, themes, and executables under a single namespace.
- Structure:
.claude-plugin/plugin.jsonfor the manifest; everything else at the plugin root, never inside.claude-plugin/. - Minimal manifest:
name(kebab-case, serves as namespace). Useful fields:displayName,version,description,defaultEnabled,dependencies. - Variables:
${CLAUDE_PLUGIN_ROOT}(install),${CLAUDE_PLUGIN_DATA}(persistent),${CLAUDE_PROJECT_DIR}(project). - Development:
claude plugin init,--plugin-dirto test,/reload-pluginsto apply changes,claude plugin validate --strictin CI. - Marketplaces: official (
claude-plugins-official,claude-community); custom sources through/plugin marketplace add(GitHub, Git URL, local path, remotemarketplace.json). - Install:
/plugin install <plugin>@<marketplace>with choice of scope User / Project / Local./pluginopens the full manager. - For Kiosque, a
kiosque-toolsplugin bundles portable hooks,reviewerandtestersubagents, GitHub and Postgres MCP servers, and adeployskill — versioned in a private team marketplace.
Next module: Daily workflows: explore, fix, test, review, ship — how to chain all these tools through a typical day of engineering.