Packaging Claude Code Plugins: Bundle Skills, Hooks, Subagents, and MCP Servers
Most plugin packaging failures stem from treating skills as standalone units. Learn the correct architecture for bundling capabilities, hooks, subagents, and MCP servers into production-ready Claude Code plugins.
Packaging Claude Code Plugins: Bundle Skills, Hooks, Subagents, and MCP Servers
Most plugin packaging failures stem from treating skills as isolated functions. Teams create individual capabilities—a linter, a deployment script, a code analyzer—and distribute them separately. This approach creates installation sprawl, version conflicts, and integration headaches.
The correct pattern is the plugin: a single installable unit that bundles related skills, lifecycle hooks, subagents, and MCP servers together. One install gives you a complete toolkit.
What Claude Code Plugins Are and Why They Matter in 2026
A Claude Code plugin is a packaging layer that wraps multiple agent capabilities into one distributable unit. Unlike skills (which execute specific tasks) or extensions (which modify editor behavior), plugins solve the integration problem.
When developers build agentic workflows, they need coherent capability groups. A "Deploy to AWS" plugin might bundle:
- A skill that validates infrastructure configurations
- A lifecycle hook that runs pre-deployment checks
- A subagent that monitors deployment progress
- An MCP server that connects to AWS APIs
Without plugins, installing those four components separately creates dependency hell. With plugins, one claude plugin install aws-deploy command activates the entire stack.
The market shift in 2026 reflects this. Plugin marketplaces now dominate AI agent ecosystems because they solve distribution at the right abstraction level. Developers who understand plugin architecture ship faster and maintain cleaner codebases.
The Anatomy of a Plugin: Skills, Hooks, Subagents, and MCP Servers
A plugin's structure mirrors application packaging. The core components form a hierarchy where each layer serves a distinct purpose.
Skills are the execution primitives. They perform discrete operations: parse a file, call an API, transform data. Skills receive structured inputs and return typed outputs. When a plugin bundles three skills, those skills share context and configuration.
Hooks intercept the agent lifecycle. A beforeTask hook might validate prerequisites. An afterCompletion hook might trigger cleanup. Hooks let plugins inject behavior without modifying core agent logic.
Subagents are specialized AI workers. A code review plugin might include a subagent that handles security scans while the main agent focuses on logic changes. Subagents run in parallel and report results back to the orchestrator, as detailed in our guide to Claude Code subagents.
MCP servers provide protocol-level integrations. They expose tools, resources, and prompts that the agent can invoke through standardized interfaces. An MCP server handles authentication, rate limiting, and API versioning so skills don't duplicate that logic.
%% alt: Plugin architecture showing skills, hooks, subagents, and MCP servers in a hierarchical structure
flowchart TD
Plugin["Plugin Package"]
Skills["Skills Layer"]
Hooks["Lifecycle Hooks"]
Subagents["Subagent Workers"]
MCP["MCP Server"]
Plugin --> Skills
Plugin --> Hooks
Plugin --> Subagents
Plugin --> MCP
Skills --> Task1["Validate Config"]
Skills --> Task2["Deploy Resources"]
Skills --> Task3["Monitor Status"]
Hooks --> Pre["beforeTask Hook"]
Hooks --> Post["afterCompletion Hook"]
Subagents --> Security["Security Scanner"]
Subagents --> Perf["Performance Profiler"]
MCP --> Auth["Authentication"]
MCP --> API["API Integration"]
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef uiComponent fill:#2a1840,stroke:#c084fc,color:#f3e8ff
classDef dataStore fill:#3a2f0b,stroke:#fbbf24,color:#fef3c7
class Plugin framework
class Skills,Hooks,Subagents,MCP uiComponent
class Task1,Task2,Task3,Pre,Post,Security,Perf,Auth,API dataStore
The distinction between these layers is critical. Skills implement logic. Hooks inject timing. Subagents parallelize work. MCP servers abstract protocols. Mixing these concerns creates brittle plugins that break when the agent runtime evolves.

Creating Your First Plugin: Directory Structure and plugin.json
Plugin development starts with standardized file organization. The runtime expects specific files in specific locations. Deviating from this structure causes installation failures.
The minimal plugin directory looks like this:
my-plugin/
├── plugin.json // Manifest and metadata
├── skills/ // Task execution
│ ├── validate.ts
│ └── deploy.ts
├── hooks/ // Lifecycle interception
│ └── pre-deploy.ts
├── subagents/ // Parallel workers
│ └── monitor.ts
├── mcp-server/ // Protocol integration
│ └── aws-connector.ts
└── README.md // DocumentationThe plugin.json file defines capabilities and dependencies:
{
"name": "aws-deploy-toolkit",
"version": "1.2.0",
"description": "Complete AWS deployment automation for Claude Code",
"author": "your-org",
"license": "MIT",
"runtime": {
"claudeVersion": ">=3.0.0",
"nodeVersion": ">=18.0.0"
},
"capabilities": {
"skills": [
{
"id": "validate-config",
"entry": "skills/validate.ts",
"description": "Validates AWS infrastructure configurations"
},
{
"id": "deploy-resources",
"entry": "skills/deploy.ts",
"description": "Deploys validated resources to AWS"
}
],
"hooks": [
{
"type": "beforeTask",
"entry": "hooks/pre-deploy.ts"
}
],
"subagents": [
{
"id": "deployment-monitor",
"entry": "subagents/monitor.ts",
"concurrency": 2
}
],
"mcpServers": [
{
"id": "aws-connector",
"entry": "mcp-server/aws-connector.ts",
"protocol": "stdio"
}
]
},
"dependencies": {
"@aws-sdk/client-cloudformation": "^3.400.0",
"zod": "^3.22.0"
}
}The manifest drives installation. When developers run claude plugin install, the runtime reads plugin.json, validates dependencies, and registers capabilities. Missing or malformed metadata causes silent failures where some features activate but others don't.
Bundling Multiple Skills Into One Installable Package
Bundling transforms isolated capabilities into a cohesive toolkit. The pattern that works in production involves three steps: identify related operations, share configuration, and expose a unified interface.
Consider a database migration plugin. Three skills handle the workflow: analyze-schema, generate-migration, and apply-changes. Shipping those as separate installs creates version drift—teams run mismatched versions and migrations fail.
The bundled approach consolidates them:
// skills/analyze-schema.ts
export async function analyzeSchema(context: SkillContext) {
const config = context.plugin.getConfig();
const dbConnection = config.database;
// Schema analysis logic using shared config
const schema = await inspectDatabase(dbConnection);
return {
tables: schema.tables,
changes: detectChanges(schema)
};
}
// skills/generate-migration.ts
export async function generateMigration(context: SkillContext) {
const analysis = context.getPreviousResult('analyze-schema');
const config = context.plugin.getConfig();
// Generate migration using analysis from previous skill
const migration = buildMigration(analysis.changes, config.migrationTemplate);
return {
sql: migration.toSQL(),
affectedTables: migration.tables
};
}
// skills/apply-changes.ts
export async function applyChanges(context: SkillContext) {
const migration = context.getPreviousResult('generate-migration');
const config = context.plugin.getConfig();
// Apply using shared database connection
await executeMigration(migration.sql, config.database);
return {
applied: true,
affectedRows: migration.affectedTables.length
};
}The shared context.plugin object provides consistent configuration access. The getPreviousResult() method chains skill outputs without manual data passing. This matters because it eliminates the fragile state management that breaks multi-step workflows.
%% alt: Workflow showing bundled skills executing in sequence with shared context
flowchart TD
Start["User Request: Migrate Database"]
Install["Plugin Install"]
Config["Load Shared Config"]
Analyze["Skill: Analyze Schema"]
Generate["Skill: Generate Migration"]
Apply["Skill: Apply Changes"]
Context["Shared Context Object"]
Result["Migration Complete"]
Start --> Install
Install --> Config
Config --> Context
Context --> Analyze
Analyze -->|Schema Data| Generate
Generate -->|Migration SQL| Apply
Apply --> Result
Context -.->|Config Access| Analyze
Context -.->|Config Access| Generate
Context -.->|Config Access| Apply
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef dataStore fill:#3a2f0b,stroke:#fbbf24,color:#fef3c7
class Start,Install userAction
class Config,Context,Analyze,Generate,Apply framework
class Result dataStore
The implication here is immediate: bundled skills share state naturally while maintaining isolation. Each skill exports a pure function, but the plugin runtime handles the plumbing.
Adding Hooks and MCP Servers to Your Plugin Bundle
Hooks and MCP servers extend plugins beyond task execution. Hooks modify timing. MCP servers abstract external systems. Together, they transform a skill collection into a complete integration layer.
A deployment plugin demonstrates both. The hook validates environment readiness before any skill runs:
// hooks/pre-deploy.ts
export async function beforeTask(context: HookContext) {
const config = context.plugin.getConfig();
// Check AWS credentials
const awsCredentials = await verifyAWSAccess(config.awsProfile);
if (!awsCredentials.valid) {
throw new Error('AWS credentials not configured. Run `aws configure` first.');
}
// Verify target environment exists
const environment = await checkEnvironment(config.targetEnv);
if (environment.status !== 'healthy') {
throw new Error(`Environment ${config.targetEnv} is not healthy. Status: ${environment.status}`);
}
// Store validation results for skills to access
context.setSharedData('envValidation', {
credentials: awsCredentials,
environment: environment
});
}The MCP server handles AWS API interactions. Skills call the server instead of importing AWS SDKs directly:
// mcp-server/aws-connector.ts
import { MCPServer, Tool } from '@anthropic/mcp-sdk';
export const server = new MCPServer({
name: 'aws-deploy-connector',
version: '1.0.0'
});
server.addTool(new Tool({
name: 'deploy_cloudformation_stack',
description: 'Deploys a CloudFormation stack with automatic retry logic',
inputSchema: {
type: 'object',
properties: {
stackName: { type: 'string' },
templateBody: { type: 'string' },
parameters: { type: 'object' }
},
required: ['stackName', 'templateBody']
},
async execute(input) {
// Centralized deployment logic with error handling
const result = await deployStack({
name: input.stackName,
template: input.templateBody,
params: input.parameters
});
return {
stackId: result.stackId,
status: result.status
};
}
}));Skills invoke the MCP tool instead of calling AWS directly. This separation matters because MCP servers handle cross-cutting concerns—authentication, retries, rate limiting—once. Skills stay focused on business logic.
The hook-first validation prevents wasted API calls. The MCP abstraction prevents vendor lock-in. Together, they make the plugin resilient to infrastructure changes.

Plugin vs Skill vs Extension: When to Package What
The packaging decision determines maintenance burden and adoption friction. Engineers who choose the wrong unit waste months fixing installation issues.
%% alt: Comparison between plugin, skill, and extension packaging approaches
flowchart LR
subgraph PluginApproach["Plugin: Multi-capability bundle"]
P1["Install Once"]
P2["Multiple Skills"]
P3["Shared Config"]
P4["Hooks + MCP"]
P5["Version Lock"]
P1 --> P2
P2 --> P3
P3 --> P4
P4 --> P5
end
subgraph SkillApproach["Skill: Single task"]
S1["Standalone Install"]
S2["One Function"]
S3["No Dependencies"]
S4["Direct Execution"]
S1 --> S2
S2 --> S3
S3 --> S4
end
subgraph ExtensionApproach["Extension: Editor integration"]
E1["UI Modification"]
E2["Editor Events"]
E3["Visual Layer"]
E4["No Agent Logic"]
E1 --> E2
E2 --> E3
E3 --> E4
end
PluginApproach -.->|"Best for: Related workflows"| Decision["Packaging Decision"]
SkillApproach -.->|"Best for: Atomic utilities"| Decision
ExtensionApproach -.->|"Best for: IDE features"| Decision
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
class P1,P2,P3,P4,P5 framework
class S1,S2,S3,S4 userAction
class E1,E2,E3,E4 framework
Plugins suit multi-step workflows. If capabilities depend on each other—schema validation before migration generation—bundle them. The version lock prevents drift. The shared configuration eliminates redundant setup.
Skills suit atomic utilities. A JSON formatter doesn't need hooks or MCP servers. Shipping it as a plugin adds installation weight without value. Standalone skills install faster and compose better.
Extensions modify editor behavior, not agent logic. Syntax highlighting, code folding, and inline suggestions belong in extensions. They react to user actions in the editor, not agent task execution. Mixing extension code into plugins creates runtime confusion because the execution contexts differ.
The failure mode here is subtle but expensive. Teams package everything as plugins "for consistency," then wonder why lightweight utilities take seconds to activate. Or they ship complex workflows as standalone skills, then spend months debugging version conflicts.
The correct heuristic: if it needs coordination, bundle it as a plugin. If it stands alone, ship it as a skill. If it touches the editor UI, build an extension.
Publishing and Distributing Your Plugin to the Marketplace
Publication transforms a local development artifact into a community resource. The marketplace distribution flow involves four stages: packaging, validation, submission, and versioning.
%% alt: Plugin publication workflow from development to marketplace release
flowchart TD
Dev["Local Development"]
Build["Build Artifact"]
Validate["Validation Checks"]
Package["Create Distribution Package"]
Sign["Code Signing"]
Metadata["Marketplace Metadata"]
Submit["Submit to Marketplace"]
Review["Automated Review"]
Manual["Manual Approval"]
Publish["Published Version"]
Install["Users Install"]
Dev --> Build
Build --> Validate
Validate -->|Pass| Package
Validate -->|Fail| Fix["Fix Issues"]
Fix --> Build
Package --> Sign
Sign --> Metadata
Metadata --> Submit
Submit --> Review
Review -->|Pass| Manual
Review -->|Fail| Fix
Manual -->|Approve| Publish
Manual -->|Reject| Fix
Publish --> Install
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef dataStore fill:#3a2f0b,stroke:#fbbf24,color:#fef3c7
class Dev,Build userAction
class Validate,Package,Sign,Metadata,Submit,Review,Manual framework
class Publish,Install dataStore
class Fix stroke:#ef4444,fill:#450a0a,color:#fca5a5
Packaging requires a build step that bundles dependencies and compiles TypeScript. The standard approach uses a build script in package.json:
{
"scripts": {
"build": "tsc && node scripts/bundle.js",
"validate": "claude plugin validate .",
"publish": "claude plugin publish --marketplace official"
}
}The validation step catches common errors: missing required fields in plugin.json, skills that reference undefined dependencies, hooks with incorrect signatures. Running npm run validate before submission prevents rejection loops.
Code signing proves plugin authenticity. The marketplace requires signed artifacts for security. Generate a signing key through the developer portal, then add it to your build process:
// scripts/bundle.js
import { signPlugin } from '@claude/plugin-tools';
const signed = await signPlugin({
pluginPath: './dist',
keyFile: process.env.PLUGIN_SIGNING_KEY,
outputPath: './release/plugin.cpkg'
});Marketplace metadata determines discoverability. The submission form requests categories, tags, screenshots, and example usage. Plugins with clear descriptions and visual documentation get 10x more installs than minimal listings.
Versioning follows semantic versioning. Breaking changes to skill interfaces require a major version bump. Adding new skills or hooks without breaking existing ones qualifies as a minor release. Bug fixes increment the patch version.
The marketplace handles update notifications automatically. When you publish version 2.0.0, users on 1.x receive an upgrade prompt. Critical security patches can trigger forced updates if marked appropriately in release notes.
Real-World Plugin Examples and Best Practices
Production plugins reveal patterns that academic examples miss. The stripe-integration plugin demonstrates complete workflow bundling. It packages payment processing skills, webhook handling hooks, a subscription management subagent, and an MCP server for Stripe API access.
The architecture avoids common mistakes. Skills don't call Stripe APIs directly—they invoke MCP tools. The beforeTask hook validates API keys before any payment operation. The subagent monitors subscription renewals in parallel with the main agent's work. This separation of concerns makes the plugin testable and maintainable.
The code-review-suite plugin shows the value of subagent parallelization. Security scanning, performance profiling, and style checking run concurrently instead of sequentially, as covered in our analysis of 2-million-token context windows. The main agent orchestrates results without waiting for each check to complete.
Best practices from these examples:
- Keep skills under 200 lines. Extract complex logic into shared utilities.
- Use TypeScript for type safety. The plugin runtime provides strong typing for context objects.
- Version lock all dependencies in
plugin.json. Loose version ranges cause installation failures when breaking changes ship. - Write integration tests that load the plugin in a test runtime. Unit tests miss configuration issues.
- Document configuration options in README with working examples. Most support requests stem from misconfigured plugins.
The failure patterns repeat across failed plugins. Tight coupling between skills breaks when runtimes upgrade. Missing error boundaries cause one skill failure to crash the entire plugin. Undocumented dependencies create installation loops where users can't resolve missing packages.
Engineers who avoid these patterns ship plugins that install cleanly and run reliably. The maintenance burden drops because the architecture anticipates change. Teams building autonomous PR workflows rely on this plugin stability—the agent can't review code if the review plugin crashes on import.
That covers the essential patterns for Claude Code plugin packaging. Bundle related capabilities together, separate concerns across layers, and validate before publishing. Apply these in production and the difference will be immediate: installations succeed, workflows compose cleanly, and maintenance costs drop. The plugin architecture matters because it's the foundation that determines whether your agent toolkit scales or collapses under its own complexity.