Plugins System
Plugins extend Claude Code with skills, hooks, and MCP servers. They can be built-in (shipped with the CLI) or installed from marketplaces (Git repositories).
Plugin ID Format
Every plugin is identified by a string in the format {name}@{source}:
- Built-in plugins:
my-plugin@builtin - Marketplace plugins:
my-plugin@marketplace-name
The isBuiltinPluginId() helper checks if an ID ends with @builtin.
BuiltinPluginDefinition
Built-in plugins ship with the CLI and appear in the /plugin UI. Users can enable or disable them, with preferences persisted to user settings.
type BuiltinPluginDefinition = {
name: string
description: string
version?: string
skills?: BundledSkillDefinition[]
hooks?: HooksSettings
mcpServers?: Record<string, McpServerConfig>
isAvailable?: () => boolean
defaultEnabled?: boolean
}{name}@builtin identifier./plugin UI.false are hidden entirely.true.Built-in plugins differ from bundled skills in that they appear in the /plugin UI under a "Built-in" section and users can explicitly toggle them. Use built-in plugins for features that should be user-toggleable. For features with complex setup or automatic enabling logic, use src/skills/bundled/ instead.
registerBuiltinPlugin()
Registers a built-in plugin at startup. Called from initBuiltinPlugins():
function registerBuiltinPlugin(definition: BuiltinPluginDefinition): voidPlugins are stored in an internal Map<string, BuiltinPluginDefinition> keyed by name.
Enable/Disable Logic
The getBuiltinPlugins() function returns all built-in plugins split into enabled and disabled lists:
function getBuiltinPlugins(): {
enabled: LoadedPlugin[]
disabled: LoadedPlugin[]
}The enabled state is resolved in priority order:
User preference
Check settings.enabledPlugins[pluginId]. If the user has explicitly set a preference, use it.
Plugin default
Fall back to the plugin's defaultEnabled property.
Global default
If neither is set, default to true (enabled).
Plugins whose isAvailable() returns false are omitted from both lists entirely.
LoadedPlugin
The runtime representation of any plugin (built-in or marketplace):
type LoadedPlugin = {
name: string
manifest: PluginManifest
path: string
source: string
repository: string
enabled?: boolean
isBuiltin?: boolean
sha?: string
commandsPath?: string
commandsPaths?: string[]
commandsMetadata?: Record<string, CommandMetadata>
agentsPath?: string
hooksConfig?: HooksSettings
mcpServers?: Record<string, McpServerConfig>
}For built-in plugins, path is set to the sentinel string "builtin" since there is no filesystem path.
Marketplace Plugins
Marketplace plugins are installed from Git repositories. The PluginInstallationManager handles their lifecycle.
Plugin Installation Manager
The performBackgroundPluginInstallations() function runs during startup without blocking the UI:
async function performBackgroundPluginInstallations(
setAppState: SetAppState,
): Promise<void>It reconciles declared marketplaces (from settings) against materialized ones (on disk):
Compute diff
Compare declared marketplaces against the known-marketplaces config to find missing installs and source changes.
Initialize UI status
Set pending status in AppState.plugins.installationStatus for each marketplace requiring work.
Reconcile
Call reconcileMarketplaces() with progress callbacks that update app state (pending, installing, installed, failed).
Refresh
After reconciliation, new installs trigger an automatic plugin refresh. Updates only set needsRefresh and show a notification for /reload-plugins.
Installation Status
Each marketplace tracks its installation state:
type MarketplaceStatus = {
name: string
status: 'pending' | 'installing' | 'installed' | 'failed'
error?: string
}Plugin Operations
| Operation | Description |
|---|---|
| Install | Clone the marketplace repository and discover plugins within it |
| Enable | Set enabledPlugins[pluginId] = true in user settings |
| Disable | Set enabledPlugins[pluginId] = false in user settings |
| Remove | Remove the marketplace from known-marketplaces config and clear cache |
| Refresh | Re-scan all marketplaces and reload plugin commands, hooks, and MCP servers |
Plugin Components
A single plugin can provide multiple component types:
Skills (Commands)
Plugins specify a commandsPath (or multiple commandsPaths) pointing to directories of Markdown skill files. These are loaded using the same frontmatter parser as disk-based skills.
Hooks
Plugins can declare hooksConfig matching the HooksSettings type. Plugin hooks are merged into the global hook registry alongside user and project hooks.
MCP Servers
Plugins can declare mcpServers mapping server names to McpServerConfig objects. These MCP servers are started alongside user-configured ones.