AI Assistant

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
}
namestring
Plugin name, used in the {name}@builtin identifier.
descriptionstring
Description shown in the /plugin UI.
versionstring | undefined
Semantic version string.
skillsBundledSkillDefinition[]
Skills provided by this plugin.
hooksHooksSettings
Lifecycle hooks provided by this plugin.
mcpServersRecord<string, McpServerConfig>
MCP server configurations provided by this plugin.
isAvailable() => boolean
Runtime availability check. Plugins returning false are hidden entirely.
defaultEnabledboolean
Default enabled state before user preference is set. Defaults to 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): void

Plugins 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:

1

User preference

Check settings.enabledPlugins[pluginId]. If the user has explicitly set a preference, use it.

2

Plugin default

Fall back to the plugin's defaultEnabled property.

3

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):

1

Compute diff

Compare declared marketplaces against the known-marketplaces config to find missing installs and source changes.

2

Initialize UI status

Set pending status in AppState.plugins.installationStatus for each marketplace requiring work.

3

Reconcile

Call reconcileMarketplaces() with progress callbacks that update app state (pending, installing, installed, failed).

4

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

OperationDescription
InstallClone the marketplace repository and discover plugins within it
EnableSet enabledPlugins[pluginId] = true in user settings
DisableSet enabledPlugins[pluginId] = false in user settings
RemoveRemove the marketplace from known-marketplaces config and clear cache
RefreshRe-scan all marketplaces and reload plugin commands, hooks, and MCP servers

Plugin Components

A single plugin can provide multiple component types:

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.

Plugins can declare hooksConfig matching the HooksSettings type. Plugin hooks are merged into the global hook registry alongside user and project hooks.

Plugins can declare mcpServers mapping server names to McpServerConfig objects. These MCP servers are started alongside user-configured ones.