Plugins are the highest-level extension mechanism in Claude Code. They bundle skills, subagents, hooks, MCP servers, and LSP configurations into a single installable package. A team installs one plugin and immediately gets everything configured — no manual setup for each component. This module covers the plugin structure, manifest format, distribution mechanisms, and how to build your own.
Plugin Architecture
A plugin is a directory with a specific structure. The only required file is .claude-plugin/plugin.json, the manifest that declares the plugin’s identity. Everything else is optional but follows conventions Claude Code recognizes:
my-plugin/
├── .claude-plugin/
│ └── plugin.json # Required manifest
├── skills/ # SKILL.md files
│ └── my-skill/
│ └── SKILL.md
├── agents/ # Subagent definitions
│ └── specialist.md
├── commands/ # Legacy command files (also work)
│ └── my-command.md
├── hooks/
│ └── hooks.json # Plugin-scoped hooks
├── .mcp.json # MCP server configs
├── .lsp.json # LSP server configs
├── settings.json # Default settings
└── bin/
└── helper.sh
The manifest identifies the plugin and its metadata:
{
"name": "pr-review",
"description": "Complete PR review workflow with security and test coverage checks",
"version": "1.0.0",
"author": {
"name": "Your Name"
},
"repository": "https://github.com/you/pr-review",
"license": "MIT"
}
Plugin commands and plugin-provided skills are namespaced as plugin-name:command-name to avoid conflicts with project-level configuration. Invoke them with the full namespaced form, such as /pr-review:check-security.
Manifest Features
The manifest supports several powerful fields for configuring plugin behavior. userConfig declares user-configurable options. Fields marked sensitive: true are stored in the system keychain rather than plain-text settings:
{
"name": "my-plugin",
"version": "1.0.0",
"userConfig": {
"apiKey": {
"description": "API key for the integration",
"sensitive": true
},
"region": {
"description": "Deployment region",
"default": "us-east-1"
}
}
}
Plugins get a persistent data directory via ${CLAUDE_PLUGIN_DATA} (v2.1.78+). This survives across sessions, making it suitable for caches, state files, and databases. Use ${CLAUDE_PLUGIN_ROOT} to reference paths relative to the plugin installation directory — essential for hooks and MCP configurations:
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/audit.js"
}
]
}
]
}
}
Plugin monitors require Claude Code v2.1.105 or later. The monitors manifest key wires the plugin into the Monitor tool. Point it at a JSON file (or inline the config) and its background watches auto-arm the moment the plugin is enabled at session start, or when one of the plugin’s skills is invoked. This is how a plugin ships “watch CI, flag failures” or “tail the dev server log” behaviour without the user having to set it up:
{
"name": "ci-watcher",
"version": "1.0.0",
"monitors": "./monitors.json"
}
When the manifest sets a custom monitors path, the default monitors/monitors.json location is no longer scanned — specify the default explicitly if you still want it loaded alongside your custom file.
LSP support adds real-time language server protocol integration. Put a .lsp.json in the plugin root to configure language servers that provide instant diagnostics, go-to-definition, and symbol search as Claude edits files:
{
"typescript": {
"command": "typescript-language-server",
"args": ["--stdio"],
"extensionToLanguage": {
".ts": "typescript",
".tsx": "typescriptreact"
}
}
}
Distribution and Development
Test a plugin locally with the --plugin-dir flag before distributing. It loads the plugin for that session only — no installation:
claude --plugin-dir ./my-plugin
# Test multiple plugins simultaneously:
claude --plugin-dir ./my-plugin --plugin-dir ./another-plugin
For plugins hosted as .zip archives, --plugin-url fetches and installs them for the current session without permanent installation. Repeat the flag for multiple plugins:
claude --plugin-url https://example.com/my-plugin.zip
claude --plugin-url https://example.com/a.zip --plugin-url https://example.com/b.zip
Only use --plugin-url with URLs you trust — loading remote archives executes third-party code on your machine.
Use /reload-plugins to hot-reload plugin files during development without restarting the session. This re-reads all manifests, skills, agents, hooks, and MCP configurations instantly.
The /plugin Discover and Browse screens now show a full inventory of what a plugin will install before you commit to it — commands, agents, skills, hooks, and any MCP or LSP servers it ships. This makes vetting a marketplace plugin a one-screen decision: you can confirm a pr-review plugin isn’t quietly registering MCP servers or hooks you didn’t expect, or check that a deploy plugin actually ships the skill you came for. Browse lists what’s already installed with the same breakdown; Discover does the same for the marketplace catalog. The preview is informational only — the plugin still has to be installed before any of its components run.
Plugin distribution follows a marketplace model. The official Anthropic marketplace is claude-plugins-official. Add additional marketplaces with /plugin marketplace add owner/repo-name. Install plugins with /plugin install plugin-name or claude plugin install plugin-name@marketplace:
# Install from official marketplace
/plugin install pr-review
# Install from GitHub
/plugin install github:username/my-plugin
# Install from local path (for testing)
/plugin install ./path/to/plugin
When a plugin source is a GitHub owner/repo shorthand, Claude Code defaults to cloning over SSH. That breaks in CI runners, containers, or any environment without a configured SSH key for github.com. Set CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1 to force HTTPS cloning instead — the rest of the install flow stays identical:
# Force HTTPS for plugin clones in CI
CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1 claude plugin install owner/repo
For enterprise environments, managed-mcp.json controls which MCP servers plugins can use. The deniedPlugins, enabledPlugins, extraKnownMarketplaces, and strictKnownMarketplaces settings in managed policy control which plugins and marketplaces are allowed organization-wide. Plugin subagents have restricted frontmatter — they cannot define hooks, mcpServers, or permissionMode to prevent privilege escalation.
Useful lifecycle commands include claude plugin list, claude plugin enable, claude plugin disable, claude plugin uninstall, claude plugin validate, claude plugin details <name> (shows a plugin’s component inventory and projected token cost per session), and claude plugin tag (new in v2.1.121) which creates a release git tag with version validation. Marketplaces can source plugins from GitHub, git URLs, local paths, npm, or other supported package sources.
claude plugin enable and claude plugin disable toggle an installed plugin on or off without removing it. Both accept a <plugin> name (or <plugin>@<marketplace> to disambiguate) and a --scope of user, project, or local (default: user). Disabling at project scope writes the choice into .claude/settings.json so the whole team picks it up; user scope keeps it personal:
# Personal: turn off a noisy plugin just for you
claude plugin disable formatter@anthropics/claude-plugins
# Team: keep the plugin in settings but turn it off project-wide
claude plugin disable formatter --scope project
# Re-enable later without touching its install or version
claude plugin enable formatter --scope project
The inline plugin pattern (source: 'settings' in v2.1.80+) lets you embed a plugin definition directly in a settings file without a separate repository. This is useful for small team-internal tools that don’t warrant a full git repository:
{
"pluginMarketplaces": [
{
"name": "internal-tools",
"source": "settings",
"plugins": [
{
"name": "code-standards",
"source": "./local-plugins/code-standards"
}
]
}
]
}