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:

DirectoryContainsLoaded as
agents/.md files with YAML frontmatterSubagents available in the session
skills/.md files with domain knowledgeSkills preloadable by agents
hooks/Scripts executed by hooksAutomatic actions on events
Rootpackage.json, README.mdMetadata and documentation

Structure rules

  1. The directory names are conventions — agents/ and skills/ must be named exactly that
  2. Files at the root — Only package.json, README.md, and configuration files
  3. No deep nesting — Agent files go directly in agents/, not in subdirectories
  4. Everything included in files — The files field 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-plugin or claude-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 discoverability
  • author — Who maintains the plugin
  • license — MIT for public, proprietary for internal
  • repository — Repository URL for issues and contributions
  • engines — 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

AspectLocal agent fileAgent file in a plugin
Location.claude/agents/reviewer.md@pkg/agents/reviewer.md
Paths in system promptSpecific (src/api/routes/)Generic ("API directories")
DependenciesCan reference other local agentsOnly references agents in the same plugin
InstallationManual (copy-paste)Automatic (claude plugins add)
UpdateManualnpm 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

AspectHookAgent behavior
When it runsSystem event (PreToolUse, etc.)When the agent decides
Who controls itClaude Code (automatic)The agent (based on the prompt)
What it can doBash/node scriptsAny allowed tool
ScopeGlobal (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
  1. Manifest — The plugin's mandatory manifest
  2. Agent — Subagent file in the correct directory
  3. Skill — Skill file in the correct directory
  4. Hook — Hook script
  5. None — Not a valid plugin component (arbitrary source code)
  6. None — Documentation, not a functional component (but it should be included)
  7. 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:

  1. "name": "my plugin" — Spaces not allowed in npm names → "my-plugin"
  2. "version": "1.0" — Semver requires 3 numbers → "1.0.0"
  3. "plugin": true — Incorrect field → "claudeCodePlugin": true
  4. "files": ["src"] — Must list agents and/or skills, not src → "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:

  1. Hardcoded paths (src/api/routes/) → dynamic discovery via Glob and CLAUDE.md
  2. Framework-specific ("FastAPI", "Pydantic v2") → generic ("any backend framework")
  3. Absolute paths (/Users/mike/...) → removed entirely
  4. 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 PostToolUse hook detects when a new file is created in src/
  • The hook writes the new file's path to a temporary file .claude/new-files.log
  • The new-file-reviewer agent reads .claude/new-files.log and 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:

  1. claudeCodePlugin isn't in package.json — Verify that the field exists and is true
  2. files doesn't include agents — Without this field, npm doesn't package the directory
  3. Agent files without frontmatter — Each file in agents/ needs the --- block with name and description
  4. Incorrect directory name — It must be agents/, not agent/ or subagents/
# 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:

  1. The skills/ directory isn't included in files — Add "skills" to the files array
  2. Skill file without frontmatter — It needs name and description in the YAML block
  3. 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:

  1. claudeCodeHooks misformatted — Verify the JSON structure (matcher, command)
  2. The script doesn't have execution permissions — chmod +x hooks/pre-review.sh
  3. The script path is relative to the plugin, not the project — Hooks must use paths relative to the plugin directory
  4. The matcher doesn't match — The matcher must match the tool's exact name (Write, not write)

Problem 4: "Error publishing — 'files not found'"

Symptom: npm publish fails or the published package is empty.

Causes and solutions:

  1. .npmignore excludes plugin directories — Verify that agents/ and skills/ aren't in .npmignore
  2. files in package.json doesn't list all the directories — Add each directory that should be included
  3. Test with npm pack before publishing — Generates a .tgz to 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:

  1. Use prefixes — @team/quality/reviewer vs @team/testing/reviewer
  2. Unique names per plugin — Instead of reviewer, use quality-reviewer or test-reviewer
  3. Check installed plugins — claude plugins list shows 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: true is mandatory, files lists 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

  1. Claude Code Sub-Agents (Anthropic Docs) — Agent files and YAML frontmatter
  2. Create Custom Subagents — Complete agent file reference
  3. Claude Code Settings — Hooks, permissions, and session configuration
  4. npm package.json Reference — npm manifest fields
  5. Semantic Versioning — The versioning standard for plugins
  6. Claude Code Best Practices — Organization and distribution
  7. Model Context Protocol — MCP reference for plugins with custom servers
  8. 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.