Module 5: Plugins — Creating and Distributing
2. Anatomy of a Plugin — Agents, Skills, Hooks, and MCP Servers
2. Anatomy of a Plugin — Agents, Skills, Hooks, and MCP Servers
Description
Before creating a plugin, you need to understand what it contains and how it's structured. A plugin isn't an arbitrary folder with files — it has a precise anatomy: a manifest (package.json) that declares what the plugin is, standard directories for each type of component (agents/, skills/), hook configuration, and optionally MCP servers scoped to the plugin's subagents. Each piece has its place, its purpose, and its rules.
In this capsule you break down the complete structure of a plugin. You'll see the package.json field by field, understand why agents/ and skills/ are separate directories, how hooks are configured inside the plugin, and how an MCP server is scoped to a specific subagent. By the end, you'll be able to look at any plugin and understand exactly what it contains and how each piece fits.
⚠️ EXPERIMENTAL FEATURE
The plugin structure described reflects the specification available as of March 2026. The manifest fields, the directory convention, and the way of declaring hooks may change. The organization principles (separating agents from skills, a declarative manifest, server scoping) are stable.
Last check: March 2026
Directory Structure
The visual anatomy
my-plugin/
├── package.json ← Plugin manifest
├── agents/ ← Subagent files
│ ├── reviewer.md ← Specialized agent
│ └── implementer.md ← Specialized agent
├── skills/ ← Skill files
│ └── api-conventions.md ← Domain knowledge
├── hooks/ ← Hook scripts (optional)
│ └── pre-review.sh ← Script executed by a hook
└── README.md ← Plugin documentation
Each directory has a single responsibility:
| Directory | Contains | Loaded as |
|---|---|---|
agents/ | .md files with YAML frontmatter | Subagents available in the session |
skills/ | .md files with domain knowledge | Skills preloadable by agents |
hooks/ | Scripts executed by hooks | Automatic actions on events |
| Root | package.json, README.md | Metadata and documentation |
Structure rules
- The directory names are conventions —
agents/andskills/must be named exactly that - Files at the root — Only
package.json,README.md, and configuration files - No deep nesting — Agent files go directly in
agents/, not in subdirectories - Everything included in
files— Thefilesfield of package.json must list all the directories the plugin distributes
The Manifest: package.json
Required fields
A plugin's package.json is a standard npm manifest with an additional field:
{
"name": "@team/code-quality-plugin",
"version": "1.0.0",
"description": "Code quality agents with reviewer and implementer",
"claudeCodePlugin": true,
"files": [
"agents",
"skills"
]
}
Let's analyze it field by field:
name — The plugin's identity
"name": "@team/code-quality-plugin"
- Use scoped packages (
@org/name) for team plugins - Use descriptive names that indicate what the plugin does
- Avoid generic names like
my-pluginorclaude-tools
Naming conventions:
@team/code-quality-plugin ← Team code quality plugin
@team/api-standards-plugin ← API standards plugin
@team/testing-agents-plugin ← Plugin with testing agents
claude-react-agents ← Public plugin for React
claude-fastapi-quality ← Public plugin for FastAPI
version — Semver versioning
"version": "1.0.0"
Follow strict semver:
MAJOR.MINOR.PATCH
1.0.0 → 1.0.1 Patch: bug fix in an agent file
1.0.0 → 1.1.0 Minor: new skill added, existing agents unchanged
1.0.0 → 2.0.0 Major: agent renamed, skill removed, breaking change
claudeCodePlugin — The marker
"claudeCodePlugin": true
This field is mandatory. Without it, Claude Code treats the package as a normal npm dependency and doesn't load its components. It's a boolean — true or not present.
files — What is distributed
"files": [
"agents",
"skills"
]
Lists the directories npm should include when publishing. If you forget a directory here, it won't be included in the published package even if it exists locally.
Relevant optional fields
keywords— Include"claude-code"and"plugin"for discoverabilityauthor— Who maintains the pluginlicense— MIT for public, proprietary for internalrepository— Repository URL for issues and contributionsengines— Minimum Node.js version (">=18.0.0")
Component 1: agents/ — Subagent Files
What goes in agents/
The files in agents/ are subagent files identical to the ones you created in module 1. The only difference: they're designed to be generic (they work in any project).
agents/
├── reviewer.md ← Reviews code for quality and conventions
└── implementer.md ← Implements changes following conventions
Agent file inside a plugin
---
name: reviewer
description: Reviews code for quality, security, and convention compliance. Read-only agent that produces structured reports.
tools: Read, Glob, Grep
model: sonnet
maxTurns: 15
---
## Role
You are a code reviewer. You analyze code for:
1. Code quality (duplication, complexity, naming)
2. Security issues (injection, exposed secrets, unsafe operations)
3. Convention compliance (based on CLAUDE.md and project patterns)
## Boundaries
- You ONLY read code. You NEVER modify files.
- You analyze ALL file types in the project.
- You report findings in structured format.
## Output Format
### Review Report
**Scope:** [files/directories reviewed]
**Issues found:** [count by severity]
#### Critical
- [file:line] — [issue] — [recommendation]
#### Warning
- [file:line] — [issue] — [recommendation]
#### Info
- [file:line] — [suggestion]
**Overall assessment:** [PASS | NEEDS_WORK | CRITICAL_ISSUES]
Differences from local agent files
| Aspect | Local agent file | Agent file in a plugin |
|---|---|---|
| Location | .claude/agents/reviewer.md | @pkg/agents/reviewer.md |
| Paths in system prompt | Specific (src/api/routes/) | Generic ("API directories") |
| Dependencies | Can reference other local agents | Only references agents in the same plugin |
| Installation | Manual (copy-paste) | Automatic (claude plugins add) |
| Update | Manual | npm update |
Key rule: generic paths
A local agent file can say:
You work in src/api/routes/ and src/api/schemas/
A plugin agent file must be generic:
You work in API-related directories (routes, schemas, controllers).
Check CLAUDE.md for project-specific directory conventions.
The plugin doesn't know how the user's project is organized. The agent file must adapt by reading CLAUDE.md or the project conventions.
Component 2: skills/ — Knowledge Files
What goes in skills/
Skills are Markdown files with domain knowledge that agents can preload. They aren't agents — they're context.
skills/
└── api-conventions.md ← Team's API conventions
Skill file inside a plugin
---
name: api-conventions
description: API design conventions for the team. Covers endpoint naming, response format, error handling, and versioning.
---
## Endpoint Naming
- Use kebab-case for URLs: `/user-profiles`, not `/userProfiles`
- Use plural nouns for collections: `/users`, not `/user`
- Version in URL: `/api/v1/users`
## Response Format
- Success: `{ "data": {...}, "meta": { "timestamp": "ISO-8601", "request_id": "uuid" } }`
- Error: `{ "error": { "code": "...", "message": "...", "details": [] } }`
## Authentication
- Bearer token in Authorization header (JWT with sub, role, exp)
## Pagination
- Cursor-based: `?cursor=abc&limit=20`, response includes `next_cursor`
Skills vs Agents: the difference
AGENT = behavior + tools + restrictions
→ DOES things (reads, writes, analyzes)
SKILL = knowledge + conventions + context
→ INFORMS the agent about how to do things
A reviewer agent without skills reviews with generic judgment. A reviewer agent with the api-conventions skill reviews against your team's specific conventions.
How an agent uses a skill
In the agent file, reference the skill:
---
name: reviewer
description: Reviews code for quality and convention compliance
tools: Read, Glob, Grep
model: sonnet
---
## Knowledge
Load the `api-conventions` skill for API endpoint reviews.
Apply those conventions when reviewing route handlers,
response schemas, and error handling.
When Claude Code loads the plugin's reviewer, it also loads the associated skills and injects them as additional context.
Component 3: Hooks in Plugins
What hooks are in the plugin context
Hooks are automatic actions that run on specific Claude Code events. In a plugin, hooks are declared in the package.json or in a configuration file, and they activate automatically when the plugin is installed.
Available hook types
PreToolUse → Before an agent uses a tool
PostToolUse → After an agent uses a tool
SessionStart → When a Claude Code session starts
Notification → When Claude Code needs to notify something
Hook configuration in package.json
{
"name": "@team/code-quality-plugin",
"version": "1.0.0",
"claudeCodePlugin": true,
"files": ["agents", "skills", "hooks"],
"claudeCodeHooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"command": "node hooks/pre-write-check.js $FILE"
}
],
"PostToolUse": [
{
"matcher": "Write",
"command": "node hooks/post-write-lint.js $FILE"
}
]
}
}
Example: pre-review hook
// hooks/pre-write-check.js
const fs = require('fs');
const path = require('path');
const file = process.argv[2];
if (!file) process.exit(0);
const ext = path.extname(file);
const apiDirs = ['routes', 'api', 'endpoints'];
const isApiFile = apiDirs.some(dir => file.includes(dir));
if (isApiFile && ext === '.py') {
console.log('API_FILE_MODIFIED: Consider running reviewer agent after this change');
}
This hook runs each time an agent writes a file. If the file is part of the API, it suggests running the reviewer.
Hooks vs Agent behavior
| Aspect | Hook | Agent behavior |
|---|---|---|
| When it runs | System event (PreToolUse, etc.) | When the agent decides |
| Who controls it | Claude Code (automatic) | The agent (based on the prompt) |
| What it can do | Bash/node scripts | Any allowed tool |
| Scope | Global (the whole plugin) | Per-agent |
Use hooks for automatic validations that must always happen. Use agent behavior for logic that depends on the task's context.
Component 4: MCP Servers in Plugins (Optional)
A plugin can include an MCP (Model Context Protocol) server scoped to its subagents. The configuration goes in the package.json:
{
"claudeCodeMcp": {
"servers": {
"quality-metrics": {
"command": "node",
"args": ["mcp/metrics-server.js"],
"scope": "plugin"
}
}
}
}
Use MCP servers when the plugin needs access to external APIs (Jira, GitHub, Slack) or custom tools. You don't need them when the built-in tools (Read, Write, Bash) are enough. For this module, MCP servers are optional — most useful plugins are built with agents + skills + hooks.
Comparison: Plugin vs Manual Configuration
Side-by-side
MANUAL CONFIGURATION PLUGIN
──────────────────── ──────
.claude/agents/reviewer.md @team/quality/agents/reviewer.md
.claude/agents/implementer.md @team/quality/agents/implementer.md
.claude/skills/conventions.md @team/quality/skills/conventions.md
settings.json (hooks) package.json (claudeCodeHooks)
Installation: Installation:
cp -r files/ .claude/ claude plugins add @team/quality
Update: Update:
cp -r new-files/ .claude/ npm update @team/quality
Versioning: Versioning:
(none) "version": "1.2.3"
Sharing: Sharing:
zip + Slack + instructions npm publish + one command
Consistency across projects: Consistency across projects:
Manual, error-prone Automatic, versioned
When you DON'T need a plugin
Not everything should be a plugin:
NOT a plugin:
├── Configuration specific to ONE project (stick with .claude/agents/)
├── A single agent file that only you use
├── Experimental configuration that changes every day
└── Skills that depend on project-specific paths
IS a plugin:
├── Configuration you reuse in 3+ projects
├── Agents + skills your team should use consistently
├── Quality hooks you want to standardize
└── Any configuration you've shared over Slack more than 2 times
Plugin Lifecycle
From creation to use
1. CREATE 2. DEVELOP 3. TEST
npm init agents/ claude plugins add ./
+ claudeCodePlugin skills/ verify loading
hooks/ test agents
4. PUBLISH 5. INSTALL 6. UPDATE
npm publish claude plugins npm update
(registry) add @pkg/name version bump
What happens when Claude Code loads a plugin
Session starts
│
├── Reads installed plugins
│
├── For each plugin:
│ ├── Reads package.json
│ ├── Verifies claudeCodePlugin: true
│ ├── Loads agents/ → available as subagents
│ ├── Loads skills/ → available as knowledge
│ ├── Registers hooks → activate on events
│ └── Starts MCP servers → tools available
│
└── Session ready with all plugins active
Exercises
Exercise 1: Identify a plugin's components (Easy)
For each listed file, indicate which plugin component it belongs to (agent, skill, hook, manifest, or none):
1. package.json
2. agents/linter.md
3. skills/python-style.md
4. hooks/post-lint.sh
5. src/utils.js
6. README.md
7. agents/helpers/format.md
See solution
- Manifest — The plugin's mandatory manifest
- Agent — Subagent file in the correct directory
- Skill — Skill file in the correct directory
- Hook — Hook script
- None — Not a valid plugin component (arbitrary source code)
- None — Documentation, not a functional component (but it should be included)
- None — Agent files must not be nested in subdirectories of
agents/
Exercise 2: Fix a broken package.json (Easy)
Identify the 4 errors in this package.json and fix them:
{
"name": "my plugin",
"version": "1.0",
"plugin": true,
"files": ["src"]
}
See solution
Errors:
"name": "my plugin"— Spaces not allowed in npm names →"my-plugin""version": "1.0"— Semver requires 3 numbers →"1.0.0""plugin": true— Incorrect field →"claudeCodePlugin": true"files": ["src"]— Must listagentsand/orskills, notsrc→"files": ["agents", "skills"]
{
"name": "my-plugin",
"version": "1.0.0",
"claudeCodePlugin": true,
"files": ["agents", "skills"]
}
Exercise 3: Design a plugin's structure (Medium)
Design the directory structure for a testing plugin that contains:
- An agent that writes unit tests
- An agent that writes integration tests
- A skill with the team's testing conventions (naming, structure, assertions)
- A hook that runs tests automatically after an agent writes a file in
tests/
Write the directory tree and the complete package.json.
See solution
@team/testing-agents-plugin/
├── package.json
├── agents/
│ ├── unit-tester.md
│ └── integration-tester.md
├── skills/
│ └── testing-conventions.md
├── hooks/
│ └── auto-run-tests.sh
└── README.md
{
"name": "@team/testing-agents-plugin",
"version": "1.0.0",
"description": "Testing agents with unit and integration test specialists",
"claudeCodePlugin": true,
"files": [
"agents",
"skills",
"hooks"
],
"claudeCodeHooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "bash hooks/auto-run-tests.sh $FILE"
}
]
},
"keywords": ["claude-code", "plugin", "testing", "pytest"],
"license": "MIT"
}
Exercise 4: Local agent file → plugin-ready (Medium)
Convert this local (project-specific) agent file to a plugin-ready (generic) one:
---
name: api-reviewer
description: Reviews FastAPI code in src/api/routes/ and src/api/schemas/
tools: Read, Glob, Grep
model: sonnet
maxTurns: 15
---
## Role
Review all Python files in src/api/routes/ and src/api/schemas/.
Check for compliance with our Pydantic v2 conventions defined in
/Users/mike/projects/backend/docs/api-standards.md.
## Rules
- Check src/api/routes/ for endpoint naming
- Check src/api/schemas/ for Pydantic model patterns
- Reference /Users/mike/projects/backend/CLAUDE.md for style guide
See solution
---
name: api-reviewer
description: Reviews API code for quality, naming conventions, and schema patterns. Read-only agent compatible with FastAPI, Express, and Django projects.
tools: Read, Glob, Grep
model: sonnet
maxTurns: 15
---
## Role
Review API-related files (routes, schemas, controllers, serializers)
for quality and convention compliance. You work with any backend
framework.
## How to Find API Files
1. Read CLAUDE.md for project-specific directory conventions
2. Use Glob to find route/endpoint files: **/*route*, **/*endpoint*, **/*view*
3. Use Glob to find schema files: **/*schema*, **/*model*, **/*serializer*
4. Adapt to the project's actual structure
## Rules
- Discover project conventions from CLAUDE.md (do not assume paths)
- Check endpoint naming against conventions
- Check schema/model patterns for consistency
- Report findings in structured format
## Output Format
### Review Report
**Project structure:** [detected framework and directories]
**Files reviewed:** [count]
**Issues:** [count by severity]
Key changes:
- Hardcoded paths (
src/api/routes/) → dynamic discovery via Glob and CLAUDE.md - Framework-specific ("FastAPI", "Pydantic v2") → generic ("any backend framework")
- Absolute paths (
/Users/mike/...) → removed entirely - Description expanded to cover multiple frameworks
Exercise 5: Plugin with a coordinated hook and agent (Hard)
Design a plugin where the hook and the agent work together:
- A
PostToolUsehook detects when a new file is created insrc/ - The hook writes the new file's path to a temporary file
.claude/new-files.log - The
new-file-revieweragent reads.claude/new-files.logand reviews each new file
Write: the package.json, the hook script, and the agent file.
See solution
package.json:
{
"name": "@team/new-file-reviewer-plugin",
"version": "1.0.0",
"claudeCodePlugin": true,
"files": ["agents", "hooks"],
"claudeCodeHooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "bash hooks/log-new-file.sh $FILE"
}
]
}
}
hooks/log-new-file.sh:
#!/bin/bash
FILE="$1"
if [[ -z "$FILE" ]]; then
exit 0
fi
if [[ "$FILE" == src/* ]]; then
mkdir -p .claude
echo "$FILE" >> .claude/new-files.log
fi
agents/new-file-reviewer.md:
---
name: new-file-reviewer
description: Reviews recently created files logged by the new-file hook. Read-only analysis.
tools: Read, Glob, Grep
model: sonnet
maxTurns: 20
---
## Role
You review files listed in .claude/new-files.log. These are files
recently created during this session.
## Process
1. Read .claude/new-files.log
2. For each file listed:
a. Read the file
b. Check for common issues (missing types, no error handling, etc.)
c. Record findings
3. Produce a consolidated review report
4. Clear .claude/new-files.log after review
## Output Format
### New File Review
**Files reviewed:** [count]
Per file:
- **[path]** — [status: OK | NEEDS_WORK] — [brief note]
**Summary:** [overall assessment]
Troubleshooting
Problem 1: "The plugin installs but the agents don't appear"
Symptom: claude plugins add ./my-plugin gives no error, but the subagents aren't available.
Causes and solutions:
claudeCodePluginisn't in package.json — Verify that the field exists and istruefilesdoesn't includeagents— Without this field, npm doesn't package the directory- Agent files without frontmatter — Each file in
agents/needs the---block withnameanddescription - Incorrect directory name — It must be
agents/, notagent/orsubagents/
# Quick diagnosis
cat my-plugin/package.json | grep claudeCodePlugin
ls my-plugin/agents/
head -5 my-plugin/agents/reviewer.md
Problem 2: "The skill isn't preloaded — the agent doesn't know it"
Symptom: The plugin's agent doesn't have the skill's context.
Causes and solutions:
- The
skills/directory isn't included infiles— Add"skills"to thefilesarray - Skill file without frontmatter — It needs
nameanddescriptionin the YAML block - The agent file doesn't reference the skill — Add a section in the agent's system prompt indicating which skill to load
Problem 3: "The plugin's hook doesn't run"
Symptom: The event happens but the hook doesn't fire.
Causes and solutions:
claudeCodeHooksmisformatted — Verify the JSON structure (matcher, command)- The script doesn't have execution permissions —
chmod +x hooks/pre-review.sh - The script path is relative to the plugin, not the project — Hooks must use paths relative to the plugin directory
- The matcher doesn't match — The matcher must match the tool's exact name (
Write, notwrite)
Problem 4: "Error publishing — 'files not found'"
Symptom: npm publish fails or the published package is empty.
Causes and solutions:
.npmignoreexcludes plugin directories — Verify thatagents/andskills/aren't in.npmignorefilesin package.json doesn't list all the directories — Add each directory that should be included- Test with
npm packbefore publishing — Generates a.tgzto inspect the content
npm pack
tar -tzf *.tgz
Problem 5: "Name conflict with another plugin"
Symptom: Two plugins have an agent with the same name field.
Causes and solutions:
- Use prefixes —
@team/quality/reviewervs@team/testing/reviewer - Unique names per plugin — Instead of
reviewer, usequality-reviewerortest-reviewer - Check installed plugins —
claude plugins listshows all the available agents
Summary
- A plugin has 4 main components: agents/ (subagents), skills/ (knowledge), hooks (automatic actions), and optionally MCP servers
- The package.json is the manifest —
claudeCodePlugin: trueis mandatory,fileslists which directories are distributed - Agent files in plugins must be generic — dynamically discovered paths, no specific-project dependencies
- Skills provide domain knowledge — conventions, patterns, standards that agents consume as context
- Hooks automate actions on events — pre-write validations, post-write linting, notifications
- MCP servers are scoped to the plugin — they provide additional tools only for the plugin's agents
- A plugin makes sense when the configuration is reused in 3+ projects or shared with the team
- Local agent files (
.claude/agents/) are still the right option for project-specific configuration
Additional Resources
- Claude Code Sub-Agents (Anthropic Docs) — Agent files and YAML frontmatter
- Create Custom Subagents — Complete agent file reference
- Claude Code Settings — Hooks, permissions, and session configuration
- npm package.json Reference — npm manifest fields
- Semantic Versioning — The versioning standard for plugins
- Claude Code Best Practices — Organization and distribution
- Model Context Protocol — MCP reference for plugins with custom servers
- npm Files Field — How to control what gets published
Next capsule: In capsule 03 you'll create a plugin from scratch — from npm init to claude plugins add ./my-plugin. Step by step: directory structure, package.json, generic agent files, skill files, local testing, and verification that everything loads correctly.