Claude Code Plugins: Create & Distribute Extensions
By Learnia Team
Claude Code Plugins: Create & Distribute Extensions
This article is written in English. Our training modules are available in French.
Plugins extend Claude Code beyond its built-in capabilities. They add new tools, integrations, and functionality—packaged for easy installation and sharing. Whether you need specialized development tools or want to share your innovations with the community, plugins make it possible.
What Are Plugins?
Plugins are self-contained packages that add features to Claude Code:
- →New tools: Additional commands Claude can use
- →Integrations: Connections to services and APIs
- →Workflows: Pre-built automation patterns
- →UI extensions: Custom visualizations and interfaces
Example: Without a Plugin
> Analyze this image for accessibility issues
I don't have image analysis capabilities in this context.
Example: With Vision Plugin
> Analyze this image for accessibility issues
[Vision Plugin: Analyzing image...]
Accessibility Issues Found:
1. Low contrast ratio (2.1:1) on header text
2. Missing alt text suggestion: "Product showcase banner"
3. Color-only information in the chart (needs patterns)
Recommendations:
- Increase text contrast to 4.5:1
- Add descriptive alt text
- Add patterns to distinguish chart segments
Plugin Architecture
Plugin Stack (from top to bottom):
- →Claude Code (main application)
- →Plugins Layer → Vision, DB, AWS, Custom plugins
- →Plugin API Layer → Standardized interface
- →Core Claude Code → Base functionality
Plugins interact with Claude Code through:
- →Tool Registration: Declare available tools
- →Hook Integration: Subscribe to lifecycle events
- →MCP Extensions: Add new MCP servers
- →Configuration: Settings and preferences
Managing Plugins
List Installed Plugins
> /plugin list
Installed Plugins:
vision@1.2.0 - Image and vision analysis
db-explorer@2.0.1 - Database exploration tools
aws-toolkit@1.5.0 - AWS service integration
Marketplace: https://plugins.claude.ai
Install a Plugin
> /plugin install vision
Installing vision@latest...
✓ Downloaded vision@1.2.0
✓ Verified signature
✓ Registered 3 new tools:
- analyze_image
- extract_text
- describe_scene
Plugin installed successfully.
From a specific source:
# From npm
claude plugin install @anthropic/vision-plugin
# From GitHub
claude plugin install github:username/my-plugin
# From local path
claude plugin install ./my-local-plugin
Update Plugins
> /plugin update vision
Updating vision...
1.2.0 → 1.3.0
Changes:
- Added video frame analysis
- Improved text extraction accuracy
- Bug fixes
✓ Updated successfully
Update all plugins:
> /plugin update --all
Remove Plugins
> /plugin remove vision
Remove vision plugin? This will unregister its tools.
[y/n]: y
✓ Plugin removed
Plugin Configuration
View Plugin Settings
> /plugin config vision
Vision Plugin Settings:
model: gpt-4-vision (default)
max_image_size: 4MB
output_format: markdown
cache_results: true
[e] Edit settings [r] Reset to defaults [Esc] Exit
Configure Plugin
> /plugin config vision set output_format json
Updated: output_format = json
Plugin Settings File
// ~/.claude/plugins/vision/config.json
{
"model": "gpt-4-vision",
"max_image_size": "4MB",
"output_format": "json",
"cache_results": true,
"api_key": "${VISION_API_KEY}"
}
Creating Plugins
Plugin Structure
A plugin folder contains:
- →package.json - Plugin metadata
- →index.js - Main entry point
- →tools/my-tool.js - Tool definitions
- →tools/another-tool.js - Additional tools
- →hooks/on-start.js - Lifecycle hooks
- →config.schema.json - Configuration schema
- →README.md - Documentation
package.json
{
"name": "@myorg/my-plugin",
"version": "1.0.0",
"description": "My custom Claude Code plugin",
"claudeCode": {
"type": "plugin",
"displayName": "My Plugin",
"icon": "puzzle",
"minVersion": "1.0.0"
},
"main": "index.js",
"keywords": ["claude-code-plugin"],
"author": "Your Name",
"license": "MIT"
}
Main Entry Point
// index.js
export default {
name: "my-plugin",
version: "1.0.0",
// Tools this plugin provides
tools: [
require("./tools/my-tool"),
require("./tools/another-tool")
],
// Lifecycle hooks
hooks: {
onLoad: async (context) => {
console.log("Plugin loaded");
},
onUnload: async (context) => {
console.log("Plugin unloaded");
}
},
// Configuration schema
configSchema: require("./config.schema.json")
};
Defining Tools
// tools/my-tool.js
export default {
name: "analyze_complexity",
description: "Analyze code complexity metrics",
inputSchema: {
type: "object",
properties: {
filePath: {
type: "string",
description: "Path to file to analyze"
},
metrics: {
type: "array",
items: { type: "string" },
description: "Metrics to calculate",
default: ["cyclomatic", "cognitive", "halstead"]
}
},
required: ["filePath"]
},
async execute(input, context) {
const { filePath, metrics } = input;
// Read file using context
const content = await context.readFile(filePath);
// Calculate metrics
const results = calculateMetrics(content, metrics);
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
}
};
function calculateMetrics(content, metrics) {
// Implementation...
return {
cyclomatic: 5,
cognitive: 8,
halstead: { difficulty: 12.5 }
};
}
Plugin API
Context Object
Plugins receive a context object with Claude Code APIs:
async execute(input, context) {
// File operations
const content = await context.readFile(path);
await context.writeFile(path, content);
// Run commands
const result = await context.runCommand("npm test");
// Access configuration
const config = context.getConfig();
// Logging
context.log.info("Processing...");
context.log.error("Something went wrong");
// User interaction
const answer = await context.prompt("Continue? [y/n]");
// Progress reporting
context.progress.start("Analyzing...");
context.progress.update(50, "Halfway done");
context.progress.complete("Done!");
}
Tool Response Format
return {
content: [
{
type: "text",
text: "Analysis complete"
},
{
type: "code",
language: "json",
text: JSON.stringify(data)
}
],
metadata: {
cached: true,
duration: 1234
}
};
Error Handling
async execute(input, context) {
try {
const result = await riskyOperation();
return { content: [{ type: "text", text: result }] };
} catch (error) {
return {
error: {
code: "OPERATION_FAILED",
message: error.message,
recoverable: true
}
};
}
}
Plugin Types
Tool Plugins
Add new capabilities:
// Image analysis plugin
export default {
name: "vision-tools",
tools: [
{
name: "analyze_image",
description: "Analyze image contents",
async execute({ imagePath }, context) {
const image = await context.readFile(imagePath, "base64");
const analysis = await visionAPI.analyze(image);
return { content: [{ type: "text", text: analysis }] };
}
}
]
};
Integration Plugins
Connect to external services:
// Jira integration plugin
export default {
name: "jira-integration",
tools: [
{
name: "get_jira_issue",
description: "Fetch Jira issue details",
async execute({ issueKey }, context) {
const config = context.getConfig();
const jira = new JiraClient(config.apiKey);
const issue = await jira.getIssue(issueKey);
return { content: [{ type: "text", text: formatIssue(issue) }] };
}
},
{
name: "create_jira_issue",
description: "Create new Jira issue",
async execute({ summary, description, type }, context) {
// Implementation...
}
}
]
};
Workflow Plugins
Provide complex multi-step operations:
// Release automation plugin
export default {
name: "release-automation",
tools: [
{
name: "prepare_release",
description: "Prepare a new release",
async execute({ version, changelog }, context) {
// 1. Update version
await context.runCommand(`npm version ${version}`);
// 2. Generate changelog
const changes = await generateChangelog();
await context.writeFile("CHANGELOG.md", changes);
// 3. Create commit
await context.runCommand("git add -A");
await context.runCommand(`git commit -m "Release ${version}"`);
// 4. Create tag
await context.runCommand(`git tag v${version}`);
return {
content: [{
type: "text",
text: `Release ${version} prepared. Run 'git push --tags' to publish.`
}]
};
}
}
]
};
Hook Plugins
React to Claude Code events:
// Analytics plugin
export default {
name: "analytics",
hooks: {
onToolUse: async (event, context) => {
await analytics.track("tool_used", {
tool: event.toolName,
duration: event.duration
});
},
onSessionStart: async (context) => {
await analytics.track("session_started");
},
onSessionEnd: async (context) => {
await analytics.track("session_ended", {
duration: context.sessionDuration
});
}
}
};
Publishing Plugins
Prepare for Publication
- →Test thoroughly:
npm test
claude plugin validate ./my-plugin
- →Add documentation:
<!-- README.md -->
# My Plugin
## Installation
\`\`\`
claude plugin install @myorg/my-plugin
\`\`\`
## Tools
### analyze_complexity
Analyzes code complexity metrics.
**Parameters:**
- `filePath` (required): Path to analyze
- `metrics` (optional): Array of metrics
**Example:**
\`\`\`
> Analyze complexity of src/utils.ts
\`\`\`
- →Create config schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"threshold": {
"type": "number",
"default": 10,
"description": "Complexity threshold for warnings"
}
}
}
Publish to npm
npm publish --access public
Publish to Plugin Marketplace
claude plugin publish
Publishing @myorg/my-plugin@1.0.0...
✓ Validated plugin structure
✓ Scanned for security issues
✓ Generated documentation
✓ Submitted for review
Your plugin will be reviewed within 24-48 hours.
Track status: https://plugins.claude.ai/submissions/abc123
Private Distribution
For internal plugins:
# Install from private npm
claude plugin install @internal/my-plugin --registry https://npm.internal.com
# Install from private GitHub
claude plugin install github:org/private-plugin --token $GITHUB_TOKEN
Plugin Security
Permissions
Plugins declare required permissions:
{
"claudeCode": {
"permissions": {
"filesystem": {
"read": ["**/*.ts", "**/*.js"],
"write": ["reports/**"]
},
"network": ["api.example.com"],
"commands": ["npm:*"]
}
}
}
Users approve permissions on install:
Installing my-plugin...
This plugin requests:
✓ Read TypeScript/JavaScript files
✓ Write to reports/ directory
✓ Network access to api.example.com
✓ Run npm commands
Install with these permissions? [y/n]
Sandboxing
Plugins run in isolated environments:
- →Limited filesystem access
- →Controlled network access
- →No direct system calls
- →Memory limits
Verification
Marketplace plugins are verified:
- →Code review
- →Security scanning
- →Signature verification
> /plugin info vision
Vision Plugin
Publisher: Anthropic (verified ✓)
Downloads: 45,230
Rating: 4.8/5
Security: Scanned ✓
Signature: Valid ✓
Popular Plugin Categories
Development Tools
| Plugin | Description |
|---|---|
| Code complexity metrics |
| Dependency visualization |
| Performance analysis |
| Test coverage reporting |
Integrations
| Plugin | Description |
|---|---|
| Jira issue management |
| Linear integration |
| Figma design access |
| Monitoring data |
AI/ML
| Plugin | Description |
|---|---|
| Image analysis |
| Semantic search |
| Code translation |
| Large file summaries |
DevOps
| Plugin | Description |
|---|---|
| Kubernetes tools |
| IaC support |
| Container management |
| CI/CD monitoring |
Plugin Development Best Practices
1. Single Responsibility
Each tool should do one thing well:
// Good: Focused tools
{
name: "get_user",
// Only fetches user
}
{
name: "update_user",
// Only updates user
}
// Bad: Kitchen sink tool
{
name: "manage_user",
// Does too many things
}
2. Clear Descriptions
Help Claude understand when to use tools:
{
name: "analyze_bundle_size",
description: "Analyze JavaScript bundle size and identify large dependencies. " +
"Use this when optimizing build output or investigating slow page loads. " +
"Returns a breakdown of bundle contents with size in bytes."
}
3. Robust Error Handling
async execute(input, context) {
if (!input.filePath) {
return {
error: {
code: "MISSING_PARAMETER",
message: "filePath is required"
}
};
}
try {
const result = await analyze(input.filePath);
return { content: [{ type: "text", text: result }] };
} catch (error) {
if (error.code === "ENOENT") {
return {
error: {
code: "FILE_NOT_FOUND",
message: `File not found: ${input.filePath}`
}
};
}
throw error; // Re-throw unexpected errors
}
}
4. Efficient Resource Use
// Cache expensive operations
const cache = new Map();
async execute(input, context) {
const cacheKey = JSON.stringify(input);
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const result = await expensiveOperation(input);
cache.set(cacheKey, result);
return result;
}
5. Version Compatibility
{
"claudeCode": {
"minVersion": "1.0.0",
"maxVersion": "2.x"
}
}
Integration with Other Features
Plugins + MCP
Plugins can register as MCP servers:
export default {
name: "my-plugin",
mcp: {
serverName: "my-mcp-server",
tools: [/* ... */]
}
};
See Model Context Protocol (MCP) for Claude Code: Complete Guide.
Plugins + Hooks
Plugins can define hooks:
export default {
hooks: {
PreToolUse: async (event, context) => {
// Validate before any tool runs
}
}
};
See Claude Code Hooks: Automate Your Development Workflow.
Plugins + Custom Commands
Commands can use plugin tools:
<!-- .claude/commands/analyze.md -->
Use the complexity-analyzer plugin to analyze this codebase
and generate a report.
See Custom Slash Commands in Claude Code: Build Your Own.
Key Takeaways
- →
Plugins extend capabilities: Add tools, integrations, and workflows.
- →
Easy installation:
handles everything./plugin install - →
Security first: Plugins declare permissions and run sandboxed.
- →
Share with community: Publish to npm or the marketplace.
- →
Build custom solutions: Create plugins for your team's needs.
Build Production-Ready Extensions
Plugins are production code. Learn the principles of building reliable, maintainable software.
In our Module 4 — Code Generation, you'll learn:
- →Writing maintainable code
- →Testing strategies
- →Error handling patterns
- →Documentation best practices
Module 4 — Chaining & Routing
Build multi-step prompt workflows with conditional logic.