Complete guide to building Claude Code plugins — manifest schema, command/skill/agent/hook authoring, MCP server development, marketplace publishing, and testing
From claude-code-expertnpx claudepluginhub markus41/claude --plugin claude-code-expertThis skill is limited to using the following tools:
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Building Claude Code plugins means creating directories, manifests, and markdown files that extend Claude's capabilities through commands, skills, agents, and hooks.
Every plugin follows this directory structure:
my-plugin/
├── .claude-plugin/
│ └── plugin.json # Manifest (REQUIRED)
├── commands/ # Slash commands
│ ├── index.json # Command index (optional)
│ └── my-command.md
├── skills/ # Knowledge packs
│ ├── skill-name/
│ │ └── SKILL.md
│ └── another-skill/
│ └── SKILL.md
├── agents/ # Specialized workers
│ ├── index.json # Agent index (optional)
│ ├── my-agent.md
│ └── another-agent.md
├── hooks/ # Lifecycle scripts
│ ├── hooks.json # Hook configuration
│ ├── my-hook.sh
│ └── scripts/
│ └── helper.sh
├── mcp-server/ # Optional: custom MCP server
│ ├── src/
│ │ └── index.js
│ ├── package.json
│ └── dist/
├── CLAUDE.md # Plugin routing guide (recommended)
├── CONTEXT_SUMMARY.md # Bootstrap context (recommended, 700 tokens max)
└── README.md # Marketplace documentation
Located at .claude-plugin/plugin.json, the manifest defines plugin identity, permissions, and capabilities.
| Field | Type | Example |
|---|---|---|
$schema | string | "https://claude.local/schemas/plugin.schema.json" |
name | string | "my-awesome-plugin" |
version | string | "1.0.0" (semver) |
description | string | One-line summary (50-80 chars) |
author.name | string | Your name or org |
license | string | "MIT" or other SPDX |
permissions.requires | string[] | Minimum permissions needed |
{
"$schema": "https://claude.local/schemas/plugin.schema.json",
"name": "my-awesome-plugin",
"version": "1.0.0",
"description": "Does something amazing with Claude Code",
"author": {
"name": "Your Name"
},
"license": "MIT",
"repository": "https://github.com/user/my-awesome-plugin",
"keywords": [
"automation",
"productivity",
"development"
],
"permissions": {
"requires": [
"read",
"write"
],
"optional": [
"bash",
"agent",
"mcp"
]
},
"capabilities": {
"provides": [
"my-custom-capability"
],
"requires": []
},
"contextEntry": "CONTEXT_SUMMARY.md",
"context": {
"entry": "CONTEXT_SUMMARY.md",
"title": "My Awesome Plugin",
"summary": "Essential context for using the plugin",
"tags": [
"automation",
"claude-code"
],
"bootstrapFiles": [
"CONTEXT_SUMMARY.md"
],
"maxTokens": 700,
"excludeGlobs": [
"**/node_modules/**",
"**/.git/**"
],
"lazyLoadSections": [
"CLAUDE.md",
"commands/advanced.md",
"skills/expert-mode/SKILL.md"
]
}
}
$schema: Validates against Claude's plugin schema. Use as shown above.
name: Unique identifier (lowercase, hyphens). Used in claude plugin install commands.
version: Semantic versioning. Increment when publishing updates (1.0.0 → 1.0.1 for patches, 1.1.0 for features, 2.0.0 for breaking changes).
permissions.requires: Minimum permissions needed. Plugin will not load without these:
read — read fileswrite — write filesbash — execute shell commandsagent — spawn subagentsmcp — use MCP serversnetwork — external HTTPpermissions.optional: Nice-to-have permissions. Plugin works without them but features are limited.
capabilities.provides: Custom capabilities your plugin exposes. Use for plugin discovery and dependency resolution.
contextEntry: Points to the bootstrap file. Always "CONTEXT_SUMMARY.md".
context.bootstrapFiles: Files loaded into context when plugin installs. Keep to 700 tokens.
context.lazyLoadSections: Files loaded on-demand when user references them. Helps large plugins stay responsive.
context.excludeGlobs: Patterns to exclude from context scanning (node_modules, build artifacts, etc.).
Commands are markdown files in the commands/ directory with YAML frontmatter and implementation body.
---
name: my-command
intent: What this command does (one sentence)
inputs:
- name: description
- task: what the user wants
- query: optional search term
flags:
- name: force
type: boolean
description: Skip confirmations
- name: output
type: choice
choices: [json, text, html]
description: Output format
risk: low
cost: low
tags:
- my-plugin
- productivity
---
After frontmatter, the body contains:
---
name: batch-process
intent: Process multiple files with transformation rules
inputs:
- files: pattern matching files to process
- rule: transformation function (e.g., "uppercase", "snake_case")
flags:
- name: dry-run
type: boolean
description: Show changes without applying
- name: workers
type: string
description: Number of parallel workers (default 4)
risk: medium
cost: medium
tags:
- my-plugin
- batch
---
# Batch Process Command
Process multiple files with transformation rules applied in parallel.
## Usage
```bash
/batch-process --files "src/**/*.ts" --rule uppercase
/batch-process --files "*.md" --rule snake_case --dry-run
/batch-process --files "src/**/*.js" --rule lowercase --workers 8
Returns JSON object:
{
"processed": 42,
"modified": 40,
"skipped": 2,
"failed": 0,
"duration_ms": 1234,
"files": [
{"path": "src/file.ts", "status": "modified", "size_before": 100, "size_after": 120}
]
}
### Command Naming
Prefix commands with plugin name for namespacing:
- Good: `/my-plugin-batch`, `/my-plugin-audit`
- Avoid: `/batch`, `/process` (too generic)
## Skill Authoring
Skills are knowledge packs in `skills/SKILLNAME/SKILL.md` with frontmatter and progressive loading.
### Skill Frontmatter
```yaml
---
name: my-skill
description: What this skill teaches (one sentence)
allowed-tools:
- Read
- Write
- Bash
- Grep
triggers:
- use my skill
- teach me about skill
- how to use skill
disable-model-invocation: false
---
Set disable-model-invocation: true for user-only reference skills (no model can invoke them).
---
name: database-migrations
description: Safely design and test database migrations with rollback paths
allowed-tools:
- Read
- Write
- Bash
triggers:
- database migration
- db migration
- migration strategy
- rollback plan
---
# Database Migrations
Design, test, and execute database migrations safely with automatic rollback paths.
## Goal
Write migrations that are **testable**, **reversible**, and **traceable**. Catch issues in development, not production.
## Core Loop
1. **Analyze** existing schema using introspection (PRAGMA for SQLite, DESCRIBE for MySQL)
2. **Design** migration: identify impact zones (tables, columns, indexes affected)
3. **Write up migration** in idempotent SQL (use CREATE IF NOT EXISTS, etc.)
4. **Test forward** locally: apply migration to dev DB, verify no errors
5. **Test backward** locally: rollback, verify schema returns to original state
6. **Document** rollback procedure: exact SQL to undo, any data caveats
7. **Plan downtime** or use zero-downtime pattern (shadow columns, dual-write)
8. **Execute in staging** first, verify application compatibility
9. **Run in production** during maintenance window with team ready to rollback
10. **Verify** schema matches expectation post-migration
## Examples
### SQLite: Add Column (Reversible)
Forward:
```sql
ALTER TABLE users ADD COLUMN last_login DATETIME DEFAULT NULL;
Backward:
-- SQLite doesn't support DROP COLUMN in older versions, so recreate:
-- Copy old schema to backup, drop table, recreate without new column
Forward (in 3 phases to avoid locking):
-- Phase 1: Add column nullable
ALTER TABLE users ADD COLUMN status VARCHAR(20);
-- Phase 2: Backfill existing rows (in batches to avoid locks)
UPDATE users SET status = 'active' WHERE status IS NULL LIMIT 1000;
-- Phase 3: Add constraint and default
ALTER TABLE users ALTER COLUMN status SET DEFAULT 'active';
ALTER TABLE users ALTER COLUMN status SET NOT NULL;
Backward:
ALTER TABLE users DROP COLUMN status;
# Create test database
sqlite3 /tmp/test.db < schema.sql
# Apply migration
sqlite3 /tmp/test.db < migration_up.sql
# Verify schema
echo "SELECT * FROM pragma_table_info(users);" | sqlite3 /tmp/test.db
# Rollback
sqlite3 /tmp/test.db < migration_down.sql
# Verify rollback
echo "SELECT * FROM pragma_table_info(users);" | sqlite3 /tmp/test.db
### Skill Triggers
Choose triggers users actually type. Avoid generic phrases; be specific:
- Good: `database migration`, `migration strategy`, `zero-downtime pattern`
- Avoid: `how to`, `help`, `explain` (too broad)
## Agent Authoring
Agents are specialized workers in markdown with YAML frontmatter and workflow steps.
### Agent Frontmatter
```yaml
---
name: my-agent
intent: One-sentence summary of what agent does
inputs:
- task: primary input
- context: optional background
risk: low
cost: low
tags:
- my-plugin
description: Multi-sentence description (2-3 sentences)
model: claude-sonnet-4-6
tools:
- Read
- Write
- Bash
---
claude-opus-4-6: Complex reasoning, multi-step problems, code architectureclaude-sonnet-4-6: Implementation, bug fixing, most tasks (recommended default)claude-haiku-4-5: Fast research, summaries, quick lookupsMandatory sections:
---
name: code-architect
intent: Design system architecture and produce implementation roadmap
inputs:
- problem: architecture challenge to solve
- constraints: technical/business constraints
risk: medium
cost: medium
tags:
- my-plugin
- architecture
- design
description: Analyzes requirements, designs modular architecture, produces detailed implementation roadmap with testing strategy.
model: claude-opus-4-6
tools:
- Read
- Write
- Grep
- Bash
---
# Code Architect Agent
System design specialist that analyzes requirements, designs modular architecture, and produces step-by-step implementation roadmaps.
## Purpose
When teams face architecture decisions, they need structured reasoning about tradeoffs: monolith vs microservices, SQL vs NoSQL, sync vs async. This agent produces **evidence-backed design docs** that make these tradeoffs explicit and testable.
## When to Use
- Starting a new project: need architecture foundation
- Scaling existing system: need refactor strategy
- Evaluating tech choices: need comparison framework
- Code review: need architecture validation
## Workflow
1. **Understand Requirements**
- Read problem statement and constraints
- List functional requirements (features, user stories)
- List non-functional requirements (scale, latency, availability)
- Identify known edge cases
2. **Design Tradeoff Matrix**
- List candidate architectures (3-5 options)
- For each, score on: scalability, complexity, cost, time-to-market
- Highlight which constraints rule out which options
- Recommend top 2 designs
3. **Deep-Dive Recommended Design**
- Draw module boundaries (responsibility, data ownership)
- Define interfaces between modules
- Show data flow end-to-end
- Identify external dependencies
4. **Produce Roadmap**
- Break into 4-6 phases
- For each phase: deliverables, test strategy, rollback plan
- Estimate effort (3-point estimates: optimistic/likely/pessimistic)
- Call out blockers and risks
5. **Create Implementation Checklists**
- Phase-by-phase tasks
- Definition of done for each phase
- Testing/validation criteria
## Known Limitations
- Architecture cannot predict all runtime issues (load patterns, failure modes)
- Assume team has required skills for chosen tech stack
- Roadmap assumes no major scope creep; update if requirements change
- Does not cover devops/infrastructure details (separate discipline)
Hooks are bash scripts in hooks/ that respond to lifecycle events with JSON on stdin/stdout.
| Event | Fires | Input | Decision |
|---|---|---|---|
SessionStart | Claude Code session begins | session context | additionalContext |
PreToolUse | Before tool call | tool name, input | allow/block/passthrough |
PostToolUse | After successful tool call | tool output | additionalContext |
PostToolUseFailure | After tool call fails | tool name, error | additionalContext (can auto-heal) |
Stop | Session ends | reason | cleanup |
Notification | User sends message | text | prompt override |
{
"tool_name": "Read",
"tool_input": {
"file_path": "/home/user/file.txt"
},
"session_id": "s_abc123",
"user_message": "optional context"
}
{
"additionalContext": "Helpful context to pass to Claude",
"decision": "allow",
"blocked_reason": "optional reason if decision is block"
}
#!/bin/bash
# PreToolUse hook: validate dangerous operations
set -e
# Read input
input=$(cat)
# Extract fields
tool_name=$(echo "$input" | jq -r '.tool_name')
tool_input=$(echo "$input" | jq '.tool_input')
file_path=$(echo "$tool_input" | jq -r '.file_path // empty')
# Block if deleting system files
if [[ "$tool_name" == "Bash" ]] && echo "$input" | jq -r '.tool_input.command' | grep -q "rm -rf /"; then
echo '{"decision": "block", "blocked_reason": "Dangerous operation: rm -rf / blocked"}'
exit 0
fi
# Allow
echo '{"decision": "allow"}'
exit 0
{
"hooks": [
{
"event": "PreToolUse",
"script": "hooks/validate-dangerous.sh",
"enabled": true
},
{
"event": "PostToolUseFailure",
"script": "hooks/capture-errors.sh",
"enabled": true
}
]
}
jq -r prevents injectionflock for atomic file writes (avoid race conditions)Optional: plugins can include custom MCP servers for new tools.
// mcp-server/src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "analyze_code",
description: "Analyzes code for patterns",
inputSchema: {
type: "object",
properties: {
code: { type: "string", description: "Code to analyze" },
language: { type: "string", description: "Programming language" }
},
required: ["code", "language"]
}
}
]
}));
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "analyze_code") {
const { code, language } = request.params.arguments;
const analysis = {
lines: code.split('\n').length,
language,
patterns: ["if-else", "loops", "functions"]
};
return {
content: [{ type: "text", text: JSON.stringify(analysis) }]
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
{
"name": "my-mcp-server",
"version": "1.0.0",
"description": "Custom MCP server for my plugin",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
}
}
Short routing guide (under 50 lines) for the plugin's capabilities.
# My Awesome Plugin
## Purpose
Use this plugin when you need to automate batch processing, validate schemas, and generate reports.
## Fast Routing
- Batch process files? → `/batch-process`
- Validate schema? → `/validate-schema`
- Generate report? → `/gen-report`
- Need advanced options? → Open `skills/advanced-batch/SKILL.md`
## Operating Rules
1. Always preview with `--dry-run` before modifying
2. Use `--workers 1` for debugging
3. Check `CONTEXT_SUMMARY.md` for permission requirements
Bootstrap context loaded when plugin installs. Keep under 700 tokens.
# My Awesome Plugin Bootstrap
What this plugin does in 2-3 sentences. When to install it.
## Quick Start
- `/batch-process --help` for batch operations
- `/validate-schema FILE` to validate JSON/YAML
- Skills: `advanced-batch`, `schema-validation`
## Permissions Needed
- read, write (required)
- bash (optional, for advanced batch processing)
## Key Concepts
- **Batch Processing**: Parallel file transformation using rules
- **Schema Validation**: JSON Schema or custom validators
- **Dry-Run**: Preview changes without applying
## When to Use
- Processing 10+ files with same rule
- Ensuring data consistency
- Automating repetitive tasks
# Check if plugin.json is valid JSON
python3 -m json.tool .claude-plugin/plugin.json > /dev/null && echo "JSON valid"
# Check all referenced files exist
python3 << 'EOF'
import json, os
data = json.load(open('.claude-plugin/plugin.json'))
bootstrap_files = data.get('context', {}).get('bootstrapFiles', [])
for f in bootstrap_files:
if not os.path.exists(f):
print(f"MISSING: {f}")
EOF
1.0.0 — Initial release1.0.1 — Bug fix (patch)1.1.0 — New feature (minor)2.0.0 — Breaking change (major)Choose keywords from common Claude Code domains:
Keep to 10-15 keywords total for discoverability.
Plugins are published to the Claude Code marketplace:
.claude-plugin/plugin.json and filesv1.0.0claude plugin search| Error | Cause | Fix |
|---|---|---|
| YAML parse error | Invalid frontmatter | Use valid YAML (quotes, hyphens for lists) |
| File not found | Referenced file missing | Add file or update manifest |
| Unknown permission | Typo in permissions | Use: read, write, bash, agent, mcp, network |
| Too many tokens | CONTEXT_SUMMARY too long | Trim to essentials, use lazyLoadSections |
| Tool not available | Tool not in allowed-tools | Add tool to skill frontmatter |