Skip to content

🌐 Language: English | 中文版 → 📖 Read Online → — Sidebar nav, dark mode & full-text search. Better than raw GitHub.

Plugin System: 44 Files, Full Lifecycle Management

Source files: utils/plugins/ (44 files, 18,856 lines), components/ManagePlugins.tsx (2,089 lines)

TL;DR

Claude Code has a full plugin ecosystem hidden under the hood — with a marketplace, dependency resolution, auto-update, blocklisting, zip caching, and hot-reload. It's far more sophisticated than the official docs suggest. The plugin loader alone (pluginLoader.ts) is 113K bytes — larger than most entire npm packages.


1. Plugin Architecture at a Glance

04 plugin system 1


2. The Plugin Manifest

Every plugin must have a .claude-plugin/plugin.json manifest:

typescript
// From schemas.ts (60K — the largest schema file in the codebase)
{
  "name": "my-plugin",
  "description": "What this plugin does",
  "version": "1.0.0",
  "commands": ["commands/*.md"],
  "agents": ["agents/*.md"],
  "hooks": { ... },
  "mcpServers": { ... },
  "skills": ["skills/*/SKILL.md"],
  "outputStyle": "styles/custom.md"
}

The schema validation in schemas.ts is 60,595 bytes — more than most entire plugins. It validates everything from command frontmatter to MCP server configurations.


3. Key Files in the Plugin System

FileSizePurpose
pluginLoader.ts113KCore loader — discovers, reads, validates, and registers all plugins
marketplaceManager.ts96KMarketplace browsing, search, installation from official catalog
schemas.ts61KZod validation schemas for all plugin manifest formats
installedPluginsManager.ts43KManages installed plugin state, activation, deactivation
loadPluginCommands.ts31KParses markdown command files with YAML frontmatter
mcpbHandler.ts32KMCP bridge handler for plugin-provided MCP servers
validatePlugin.ts29KMulti-pass plugin validation before activation
pluginInstallationHelpers.ts21KGit clone, npm install, dependency resolution
mcpPluginIntegration.ts21KIntegrates plugin-declared MCP servers into the tool pool
marketplaceHelpers.ts19KHelper functions for marketplace operations
dependencyResolver.ts12KResolves plugin dependency graphs
zipCache.ts14KCaches downloaded plugins as zip files for offline use

4. Plugin Lifecycle

4.1 Discovery

Plugins are discovered from multiple locations:

1. Built-in plugins    — bundled with the binary
2. Project plugins     — .claude/ directory in the project
3. User plugins        — ~/.config/claude-code/plugins/
4. Marketplace plugins — official GCS bucket catalog

4.2 Installation

4.3 Validation

validatePlugin.ts (29K) runs extensive checks:

  • Manifest schema validation (Zod)
  • Command file syntax validation
  • Hook command safety checks
  • MCP server configuration validation
  • Circular dependency detection
  • Version compatibility checks

4.4 Loading

pluginLoader.ts (113K — the second largest file in the codebase) handles:

  • Parallel loading of all plugin components
  • Hook registration
  • Agent definition merging
  • Command registration with deduplication
  • MCP server startup
  • Skill directory registration
  • Error isolation (one plugin failing doesn't crash others)

5. Marketplace System

Official Marketplace (officialMarketplaceGcs.ts)

The marketplace is a GCS (Google Cloud Storage) bucket serving a plugin catalog:

typescript
// officialMarketplace.ts
const OFFICIAL_MARKETPLACE_NAMES = new Set([
  'official',     // Production marketplace
  'staging',      // Staging environment
])

Features:

  • Startup check (officialMarketplaceStartupCheck.ts, 15.6K) — checks for plugin updates on launch
  • Auto-update (pluginAutoupdate.ts, 9.8K) — background auto-update mechanism
  • Blocklist (pluginBlocklist.ts, 4.5K) — remotely disable compromised plugins
  • Flagging (pluginFlagging.ts, 5.8K) — flag plugins for review
  • Install counts (installCounts.ts, 8.6K) — telemetry for marketplace popularity

Zip Cache System

Downloaded plugins are cached as zip files to avoid re-downloading:

typescript
// zipCache.ts — 14K
// Plugins are cached in ~/.config/claude-code/plugin-cache/
// Keyed by content hash for deduplication
// zipCacheAdapters.ts — 5.5K  
// Adapters for different storage backends

6. What Plugins Can Provide

Agents

typescript
// loadPluginAgents.ts — 12.8K
// Plugins define agents as markdown files:
// agents/code-reviewer.md → becomes available as subagent_type: "code-reviewer"

Commands (Slash Commands)

typescript
// loadPluginCommands.ts — 31K
// Markdown files with YAML frontmatter:
// commands/review-pr.md → becomes /review-pr
// Frontmatter supports: allowed_tools, model, description

Hooks

typescript
// loadPluginHooks.ts — 10.4K
// Plugin hooks.json → registered as PreToolUse/PostToolUse/etc hooks
// Plugin hooks run with workspace trust checks

MCP Servers

typescript
// mcpPluginIntegration.ts — 21K
// Plugins can declare MCP servers that auto-start
// mcpbHandler.ts — 32K  
// MCP bridge: proxies between plugin MCP servers and Claude Code

Skills

typescript
// Skills are markdown files (SKILL.md) with trigger patterns
// Auto-discovered from plugin skill directories

Output Styles

typescript
// loadPluginOutputStyles.ts — 5.9K
// Custom output formatting (explanatory, learning, etc.)

7. Security & Trust Model

Plugin Policy

typescript
// pluginPolicy.ts
// Plugins from untrusted sources require explicit user approval
// Managed plugins (from official marketplace) have elevated trust

Blocklist

typescript
// pluginBlocklist.ts
// Remote blocklist can disable plugins by ID
// Checked on every startup and plugin load

Orphan Filter

typescript
// orphanedPluginFilter.ts — 4K
// Detects plugins whose source repo was deleted
// Prevents dangling references

8. Skill System Architecture

Source coordinates: src/skills/, src/commands/

Skills are Claude Code's "prompt as code" system — each Skill is a Markdown file with YAML frontmatter that defines when and how a capability should be activated.

8.1 Six Source Layers

typescript
// 源码位置: src/skills/types.ts
export type LoadedFrom =
  | 'commands_DEPRECATED'  // Legacy commands/ directory (migration path)
  | 'skills'               // .claude/skills/ directory
  | 'plugin'               // Installed via plugin
  | 'managed'              // Enterprise managed configuration
  | 'bundled'              // CLI built-in skills
  | 'mcp'                  // Provided by MCP server

Search paths by priority:

policySettings → /path/to/managed/.claude/skills/
userSettings   → ~/.claude/skills/
projectSettings → .claude/skills/
plugin         → from plugin installation
bundled        → compiled into the binary
mcp            → discovered at runtime from MCP servers

8.2 Skill Frontmatter

Every Skill is a Markdown file parsed through YAML frontmatter:

typescript
export function parseSkillFrontmatterFields(frontmatter, markdownContent, resolvedName): {
  displayName: string | undefined
  description: string
  allowedTools: string[]        // Restrict available tools
  argumentHint: string          // Argument prompt hint
  argumentNames: string[]       // Named argument list
  whenToUse: string             // Model trigger condition
  version: string
  model: string                 // Can specify 'inherit'
  disableModelInvocation: boolean  // Manual-only trigger
  userInvocable: boolean         // Show in /skill list
  hooks: HooksSettings           // Associated hooks
  executionContext: 'fork'       // Run in isolated context
  agent: string                  // Bind to specific agent type
  effort: EffortValue            // Reasoning effort level
  shell: FrontmatterShell        // Shell execution config
}

8.3 Bundled Skills Registration

Built-in skills are registered programmatically at startup:

typescript
// 源码位置: src/skills/initBundledSkills.ts
export function initBundledSkills(): void {
  registerUpdateConfigSkill()   // /update-config
  registerKeybindingsSkill()    // /keybindings
  registerVerifySkill()         // /verify
  registerDebugSkill()          // /debug
  registerSimplifySkill()       // /simplify
  registerBatchSkill()          // /batch
  registerStuckSkill()          // /stuck
  // Feature-gated:
  if (feature('AGENT_TRIGGERS')) registerLoopSkill()       // /loop
  if (feature('AGENT_TRIGGERS_REMOTE')) registerScheduleRemoteAgentsSkill()
  if (feature('BUILDING_CLAUDE_APPS')) registerClaudeApiSkill()  // /claude-api
}

8.4 Inline vs Fork Execution Context

Skills can execute in two contexts:

ContextBehaviorUse Case
inlineInjects prompt into current conversationSimple commands, config changes
forkRuns in an isolated sub-agent with independent token budgetComplex tasks, multi-step operations

Fork execution creates a fresh query loop with its own message history, preventing Skill execution from polluting the main conversation context.

8.5 Token Budget Management

Skill listings consume ~1% of the context window:

typescript
export const SKILL_BUDGET_CONTEXT_PERCENT = 0.01
export const MAX_LISTING_DESC_CHARS = 250  // Per-skill description limit

export function formatCommandsWithinBudget(commands, contextWindowTokens?): string {
  // 1. Try full descriptions
  // 2. Over budget? Bundled skills keep full descriptions, others truncated
  // 3. Extreme case? Non-bundled show only names
}

This three-tier degradation ensures that even with hundreds of installed Skills, the listing never overwhelms the context window.

8.6 Bundled Skill File Safety

When a bundled Skill carries reference files, extraction uses OS-level security:

typescript
// O_NOFOLLOW | O_EXCL prevents symlink attacks
const SAFE_WRITE_FLAGS = fsConstants.O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW

// Path traversal validation
function resolveSkillFilePath(baseDir: string, relPath: string): string {
  const normalized = normalize(relPath)
  if (isAbsolute(normalized) || normalized.split(pathSep).includes('..')) {
    throw new Error(`bundled skill file path escapes skill dir: ${relPath}`)
  }
  return join(baseDir, normalized)
}

9. Built-in Plugin Registration

Source coordinates: src/plugins/

9.1 Plugin Definition Type

typescript
export type BuiltinPluginDefinition = {
  name: string
  description: string
  version: string
  defaultEnabled?: boolean
  isAvailable?: () => boolean   // Environment detection
  skills?: BundledSkillDefinition[]
  hooks?: HooksSettings
  mcpServers?: McpServerConfig[]
}

Each built-in plugin is a triple of skills + hooks + MCP servers. The isAvailable function enables environment-aware activation (e.g., a JetBrains-specific plugin only activates when JetBrains IDE is detected).

9.2 Enable/Disable Logic

typescript
export function getBuiltinPlugins(): { enabled: LoadedPlugin[], disabled: LoadedPlugin[] } {
  for (const [name, definition] of BUILTIN_PLUGINS) {
    // 1. Skip if environment check fails
    if (definition.isAvailable && !definition.isAvailable()) continue

    // 2. Check user setting override
    const userSetting = settings?.enabledPlugins?.[pluginId]

    // 3. Resolve: explicit setting > defaultEnabled > true
    const isEnabled = userSetting !== undefined
      ? userSetting === true
      : (definition.defaultEnabled ?? true)
  }
}

9.3 Plugin → Skill → Command Conversion

typescript
// Plugin-provided skills become slash commands
export function getBuiltinPluginSkillCommands(): Command[] {
  const { enabled } = getBuiltinPlugins()
  for (const plugin of enabled) {
    const definition = BUILTIN_PLUGINS.get(plugin.name)
    if (!definition?.skills) continue
    for (const skill of definition.skills) {
      commands.push(skillDefinitionToCommand(skill))
    }
  }
}

10. MCP Integration Deep Dive

Source coordinates: src/services/mcp/client.ts, src/utils/plugins/mcpPluginIntegration.ts

10.1 Six Transport Types

MCP servers connect through six transport mechanisms:

TransportProtocolUse Case
stdiostdin/stdoutLocal CLI tools
sseServer-Sent EventsRemote HTTP servers
httpHTTP POST (streamable)Stateless API servers
wsWebSocketBidirectional streaming
sdkIn-process SDKSame-process tools
sse-ideSSE via IDE proxyIDE-bridged servers

10.2 Tool Discovery → Tool Object Conversion

When an MCP server registers tools, they are converted into Claude Code's internal Tool interface:

MCP Server Tool Discovery

listTools() response

Convert each MCP tool → Tool object:
  - name: "mcp__{serverName}__{toolName}"
  - inputSchema: from MCP tool schema
  - checkPermissions: checks mcp permission rules
  - call(): proxied to MCP server

Injected into assembleToolPool() with stable ordering

10.3 MCPConnectionManager

A React context component that manages MCP server lifecycle:

Session start → discover MCP servers from settings + plugins

Parallel connection → health check → tool registration

Mid-session changes → incremental reconnect

Session end → graceful disconnect all

11. Background Installation Manager

Source coordinates: src/utils/plugins/pluginInstallationManager.ts

The PluginInstallationManager reconciles declared plugins with actual installations:

Declared plugins (from settings + marketplace)

diff against actually installed plugins

Missing? → Install (git clone/zip download)
Extra? → Prompt user for cleanup
Updated? → Auto-update if marketplace source

Report progress via React state

Key design:

  • Reconciliation model: Instead of imperative "install this," the system declaratively states "these plugins should exist" and the manager handles the delta
  • Progress feedback: Each installation step emits progress events consumed by the ManagePlugins UI
  • Error isolation: One plugin failing to install doesn't block others

Transferable Design Patterns

The following patterns from the Plugin System can be directly applied to any extensible application architecture.

Pattern 1: Isolation by Default

Each plugin loads in isolation. One plugin crashing doesn't affect others. The loader wraps each plugin load in try/catch and reports failures individually.

Pattern 2: Multi-Source Discovery with Priority

Plugins from different sources (built-in, project, user, marketplace) are merged with clear priority rules. Project-local plugins override user-level ones.

Pattern 3: Content-Addressed Caching

The zip cache uses content hashes as keys, enabling deduplication across versions and users.

Pattern 4: Prompt as Code (Skills)

Skills turn Markdown files with YAML frontmatter into executable capabilities. The frontmatter provides structured metadata (triggers, tools, models) while the Markdown body provides the prompt content. This makes Skills version-controllable, shareable, and composable.

Pattern 5: Declarative Reconciliation

The PluginInstallationManager follows a declarative "desired state → actual state → diff → reconcile" model, borrowed from Kubernetes operators. This is more robust than imperative install/uninstall sequences because it's self-healing.


Summary

AspectDetail
Total code44 files, 18,856 lines in utils/plugins/ alone
Largest filespluginLoader.ts (113K), marketplaceManager.ts (96K)
Plugin sourcesBuilt-in, project, user, official marketplace (GCS)
ComponentsAgents, Commands, Hooks, Skills, MCP Servers, Output Styles
MarketplaceGCS bucket catalog, auto-update, blocklist, install counts
SecurityWorkspace trust, validation, blocklist, orphan detection
CachingContent-addressed zip cache for offline use

Previous: ← 03 — Multi-Agent CoordinatorNext: → 05 — Hook System

Released under the MIT License.