# How the Claude Code Plugin System Architecture Works

> Explore the Claude Code plugin system architecture. Discover how it efficiently scans, registers commands, agents, and more without core code modification.

- Repository: [Anthropic/claude-code](https://github.com/anthropics/claude-code)
- Tags: architecture
- Published: 2026-04-02

---

**Claude Code discovers and activates plugins at startup by scanning the `plugins/` directory for [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-code/blob/main/.claude-plugin/plugin.json) manifests, then registers commands, agents, skills, hooks, and MCP servers without modifying core code.**

The **Claude Code plugin system architecture** provides a declarative, file-based extension mechanism for the `anthropics/claude-code` repository. By placing a structured directory with a JSON manifest and optional component folders, third-party developers can inject new slash commands, validation hooks, and autonomous agents into the runtime. The system relies on a small Python loader that walks the plugin tree at initialization and wires components into the internal Agent SDK.

## Plugin Directory Structure and Manifest

Each plugin lives in its own folder under `plugins/` and exposes capabilities through a **[`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-code/blob/main/.claude-plugin/plugin.json)** manifest file. This JSON descriptor tells Claude Code what components the plugin provides and where to find them.

| Component | Purpose | Location |
|---|---|---|
| **Commands** | Slash-command definitions (e.g., `/my-cmd`) invoked by users | `plugins/<plugin-name>/commands/` (markdown files with front-matter) |
| **Agents** | Autonomous Claude agents for long-running or multi-step work | `plugins/<plugin-name>/agents/` |
| **Skills** | Re-usable skill files ([`SKILL.md`](https://github.com/anthropics/claude-code/blob/main/SKILL.md)) that agents import | `plugins/<plugin-name>/skills/` |
| **Hooks** | Event-driven scripts that run before/after tools, on stop, etc. | `plugins/<plugin-name>/hooks/` (bash, python, or JSON schemas) |
| **MCP Server** | Optional Model-Context-Protocol server exposing tools to Claude | [`.mcp.json`](https://github.com/anthropics/claude-code/blob/main/.mcp.json) at the plugin root |
| **Metadata** | Name, version, author, and description for discovery | [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-code/blob/main/.claude-plugin/plugin.json) |

## Component Registration and Loading Flow

When Claude Code starts, it executes a four-stage loading process to integrate plugins into the runtime.

### Discovery

The runtime walks the `plugins/*/.claude-plugin/plugin.json` tree to locate every installed plugin.

### Manifest Parsing

Each discovered JSON file is loaded into a Python `Plugin` object. The manifest declares which component types are present and their relative paths within the plugin folder.

### Component Registration

- **Commands** are parsed from markdown front-matter in `commands/*.md` files.
- **Hooks** are collected and wrapped in JSON schemas that declare which events they handle (e.g., `PreToolUse`, `Stop`).
- **Agents** and **skills** are registered with the internal Agent SDK for dynamic invocation.
- **MCP servers** defined in [`.mcp.json`](https://github.com/anthropics/claude-code/blob/main/.mcp.json) are surfaced to Claude as native tools.

### Runtime Execution

When a user triggers an event—such as typing a slash command or invoking a tool—Claude Code looks up the matching component and executes it. Hook scripts communicate back to the runtime via stdout using a JSON protocol; specific exit codes signal whether to allow, block, or modify the operation.

## Hook System Deep Dive: The Hookify Example

The `hookify` plugin demonstrates how event-driven hooks intercept tool calls using a **RuleEngine** ([`plugins/hookify/core/rule_engine.py`](https://github.com/anthropics/claude-code/blob/main/plugins/hookify/core/rule_engine.py)). This architecture allows users to block dangerous operations without modifying Claude Code's core.

### Rule Definition and Loading

Users write rules in markdown files stored in `.claude/` directories (e.g., [`.claude/hookify.dangerous-rm.local.md`](https://github.com/anthropics/claude-code/blob/main/.claude/hookify.dangerous-rm.local.md)). These files contain YAML front-matter defining match conditions and actions.

The `config_loader` module parses these files into `Rule` objects:

```python

# plugins/hookify/hooks/pretooluse.py

from hookify.core.config_loader import load_rules

# Load all rules tagged for the 'bash' event

rules = load_rules(event='bash')

```

### Runtime Evaluation

For every incoming tool call (`Bash`, `Write`, etc.), the `RuleEngine` evaluates each rule. If conditions match, the engine can **block** the operation or emit a warning.

```python

# plugins/hookify/hooks/pretooluse.py

import json, sys
from hookify.core.config_loader import load_rules
from hookify.core.rule_engine import RuleEngine

def main():
    input_data = json.load(sys.stdin)  # Tool input from Claude

    rules = load_rules(event='bash')
    engine = RuleEngine()
    result = engine.evaluate_rules(rules, input_data)

    if result.get("decision") == "block":
        # Protocol: JSON stdout with message, exit code 2

        print(json.dumps({"systemMessage": result["reason"]}))
        sys.exit(2)

    # Exit 0 allows the tool to proceed

    sys.exit(0)

if __name__ == "__main__":
    main()

```

### Example Rule Configuration

Rules are defined in markdown with front-matter:

```markdown
---
name: dangerous-rm
enabled: true
event: bash
tool_matcher: Bash
conditions:
  - field: command
    operator: regex_match
    pattern: "rm\\s+-rf"
---
⚠️ **Dangerous command** – `rm -rf` can delete important data. Review before running.

```

## Extending Claude Code: Implementation Patterns

| Extension Point | Implementation Method | Key Source Reference |
|---|---|---|
| **Add a new command** | Create [`commands/my-cmd.md`](https://github.com/anthropics/claude-code/blob/main/commands/my-cmd.md) with front-matter (`name`, `description`, `tools`) and a markdown body sent to Claude when invoked | [`plugins/plugin-dev/README.md`](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/README.md) |
| **Add a hook** | Drop a script in [`hooks/pretooluse.py`](https://github.com/anthropics/claude-code/blob/main/hooks/pretooluse.py) that reads JSON from stdin and writes a JSON response to stdout; use `RuleEngine` or custom logic | [`plugins/hookify/hooks/pretooluse.py`](https://github.com/anthropics/claude-code/blob/main/plugins/hookify/hooks/pretooluse.py) |
| **Expose a tool via MCP** | Add [`.mcp.json`](https://github.com/anthropics/claude-code/blob/main/.mcp.json) describing the tool schema (`name`, `description`, `input_schema`) | [`plugins/plugin-dev/README.md`](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/README.md) |
| **Add a skill** | Write [`skills/my-skill/SKILL.md`](https://github.com/anthropics/claude-code/blob/main/skills/my-skill/SKILL.md) following the progressive-disclosure pattern; agents reference it with `skill: my-skill` | [`plugins/plugin-dev/skills/skill-development/SKILL.md`](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/skill-development/SKILL.md) |

### Minimal Plugin Manifest

Every plugin requires a [`plugin.json`](https://github.com/anthropics/claude-code/blob/main/plugin.json) manifest at [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-code/blob/main/.claude-plugin/plugin.json):

```json
{
  "name": "my-example-plugin",
  "description": "Demo plugin showing the manifest format",
  "version": "0.1.0",
  "author": { "name": "Your Name", "email": "you@example.com" }
}

```

### Simple Slash Command Definition

Create [`commands/hello.md`](https://github.com/anthropics/claude-code/blob/main/commands/hello.md) to register `/hello`:

```markdown
---
name: hello
description: Say hello to Claude
tools: []
---
Hello, Claude! 👋

```

When the user types `/hello`, Claude renders the markdown body as the response.

## Summary

- **Discovery mechanism**: Claude Code scans `plugins/*/.claude-plugin/plugin.json` at startup to find extensions.
- **Component types**: Plugins can provide **commands** (slash commands), **hooks** (event interceptors), **agents** (autonomous workers), **skills** (reusable prompts), and **MCP servers** (tool schemas).
- **Hook protocol**: Hook scripts communicate via JSON over stdin/stdout, using exit code `2` to block operations and code `0` to allow them.
- **Rule engine**: The `hookify` plugin demonstrates pluggable validation using [`config_loader.py`](https://github.com/anthropics/claude-code/blob/main/config_loader.py) and [`rule_engine.py`](https://github.com/anthropics/claude-code/blob/main/rule_engine.py) to evaluate markdown-defined rules against tool inputs.
- **Zero-core changes**: The architecture supports new functionality through file placement alone, as implemented in `anthropics/claude-code`.

## Frequently Asked Questions

### What file triggers plugin discovery in Claude Code?

Claude Code looks for [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-code/blob/main/.claude-plugin/plugin.json) inside each immediate subdirectory of `plugins/`. This JSON manifest declares the plugin's metadata and available components, enabling the runtime to register the extension without additional configuration.

### How do hooks communicate blocking decisions to the runtime?

Hooks write a JSON object to stdout and exit with a specific code. Exit code `0` allows the operation to proceed, while exit code `2` blocks it and displays the `systemMessage` field from the JSON response to the user. This protocol is implemented in files like [`plugins/hookify/hooks/pretooluse.py`](https://github.com/anthropics/claude-code/blob/main/plugins/hookify/hooks/pretooluse.py).

### Can plugins expose new tools to Claude via MCP?

Yes. By adding a [`.mcp.json`](https://github.com/anthropics/claude-code/blob/main/.mcp.json) file at the plugin root that defines the tool name, description, and input schema, Claude Code automatically surfaces the tool to the user through the Model-Context-Protocol integration.

### Where should skill files be placed in a plugin directory?

Skills belong in `plugins/<plugin-name>/skills/` as [`SKILL.md`](https://github.com/anthropics/claude-code/blob/main/SKILL.md) files following the progressive-disclosure pattern. Agents can then reference these skills using the syntax `skill: my-skill` to import the capabilities defined in the markdown.