How to create and share a Claude Code plugin
A Claude Code plugin is a directory containing skills, agents, hooks, MCP servers, or LSP
configurations, optionally described by a .claude-plugin/plugin.json manifest. Test it locally with
--plugin-dir, then distribute it through a marketplace so other people can install and update it as
one versioned unit.
The trigger for creating one is specific: a second repository needs the same setup. Until then,
standalone .claude/ configuration iterates faster and costs nothing to change.
Standalone or plugin
Standalone (.claude/) |
Plugin | |
|---|---|---|
| Skill names | /hello |
/plugin-name:hello |
| Availability | One project | Any project that installs it |
| Distribution | Copy files manually | /plugin install from a marketplace |
| Versioning | None | Explicit, with controlled updates |
| Best for | Personal workflows, experiments | Sharing with teammates or the community |
Start standalone, convert when you are ready to share. The conversion is mechanical, and doing it early mostly buys you a namespace prefix you did not need yet.
If you are still deciding what the thing should be at all — Claude skills vs. connectors vs. plugins separates the packaging question from the capability question.
Build the plugin
mkdir -p my-first-plugin/.claude-plugin
The manifest at .claude-plugin/plugin.json defines the plugin's identity:
{
"name": "my-first-plugin",
"description": "A greeting plugin to learn the basics",
"version": "1.0.0",
"author": { "name": "Your Name" }
}
| Field | Purpose |
|---|---|
name |
Unique identifier and skill namespace. Skills are prefixed with it: /my-first-plugin:hello. |
description |
Shown in the plugin manager when browsing or installing. |
version |
Optional. When set, users only receive updates after you bump it. |
author |
Optional, useful for attribution. |
Then add a skill. Skills live in skills/, one directory each, containing a SKILL.md:
mkdir -p my-first-plugin/skills/hello
---
description: Greet the user with a personalized message
---
Greet the user named "$ARGUMENTS" warmly and ask how you can help them today.
Get the directory layout right
This is the single most common plugin bug, and it produces a plugin that loads with nothing in it.
Only plugin.json goes inside .claude-plugin/. Everything else sits at the plugin root.
| Directory | Purpose |
|---|---|
.claude-plugin/ |
Contains plugin.json — optional when components use default locations |
skills/ |
Skills as <name>/SKILL.md directories |
commands/ |
Skills as flat markdown files — use skills/ for new plugins |
agents/ |
Custom subagent definitions |
hooks/ |
Event handlers in hooks.json |
.mcp.json |
MCP server configurations |
.lsp.json |
LSP server configurations for code intelligence |
monitors/ |
Background monitor configurations |
bin/ |
Executables added to the Bash tool's PATH while the plugin is enabled |
settings.json |
Default settings applied when the plugin is enabled |
The plugin root is the individual plugin's own directory — never ~/.claude/. A plugin that ships
exactly one skill can place SKILL.md directly at the plugin root, but use the skills/ layout for
anything that might grow.
Test it locally
claude --plugin-dir ./my-first-plugin
Then invoke the skill by its namespaced name:
/my-first-plugin:hello Alex
As you make changes, /reload-plugins picks them up without restarting — it reloads plugins, skills,
agents, hooks, plugin MCP servers, and plugin LSP servers.
A few things --plugin-dir supports that are easy to miss:
- It accepts a
.ziparchive of the plugin directory. - Repeat the flag to load several plugins at once.
- Point it at a folder of plugins and each immediate subfolder with a manifest loads separately. Claude Code skips everything else in that folder without reporting an error, including plugins that have no manifest — which is the usual explanation for "my plugin silently didn't load."
- When a
--plugin-dirplugin shares a name with an installed marketplace plugin, the local copy wins for that session, so you can test changes without uninstalling.
Test each component deliberately: run every skill, check agents appear in /context under Custom
Agents, and trigger the event each hook matches to confirm its effect. Claude Code records which hooks
matched, their exit codes, and their output in the debug log.
For faster iteration without the flag, claude plugin init my-tool scaffolds a plugin in
~/.claude/skills/my-tool/ that loads automatically on the next session as my-tool@skills-dir, with
no marketplace or install step.
Convert existing configuration
If you already have .claude/ configuration, migration is a copy plus one format change:
mkdir -p my-plugin/.claude-plugin
# write plugin.json, then:
cp -r .claude/commands my-plugin/
cp -r .claude/agents my-plugin/
cp -r .claude/skills my-plugin/
Hooks are the exception. They move from settings.json into my-plugin/hooks/hooks.json, where the
inner format is the same — copy the hooks object across:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npm run lint:fix" }]
}
]
}
}
Then remove the originals from .claude/. Project and user .claude/agents/ definitions override
same-named plugin agents, so the plugin version does not take effect until the originals are gone.
Skills behave differently: the original /name and the namespaced /plugin-name:name both remain
available, so a stale copy quietly shadows nothing and confuses everyone.
Ship a marketplace
Distribution goes through a marketplace: a .claude-plugin/marketplace.json at a repository root.
{
"name": "company-tools",
"owner": { "name": "DevTools Team", "email": "devtools@example.com" },
"plugins": [
{
"name": "code-formatter",
"source": "./plugins/formatter",
"description": "Automatic code formatting on save",
"version": "2.1.0"
},
{
"name": "deployment-tools",
"source": { "source": "github", "repo": "company/deploy-plugin", "ref": "v2.0.0" },
"description": "Deployment automation tools"
}
]
}
Each entry needs a name and a source. Sources can be relative paths, GitHub repositories, git
repositories, git subdirectories, npm packages, or zip archives.
Three things to get right:
Marketplace names are public and singular. Users type them: /plugin install my-tool@company-tools.
Each user can register only one marketplace per name — adding a second with the same name replaces the
first. To publish several plugins under one name, list them all in a single marketplace.json.
A set of names is reserved for official Anthropic use, including claude-plugins-official,
claude-community, and agent-skills, along with names that impersonate them. Reserved names are
re-checked on every load, not only when a marketplace is added.
Relative paths resolve against the marketplace root, the directory containing .claude-plugin/.
They do not resolve at all if users add your marketplace via a direct URL to the marketplace.json
file, because only that file gets downloaded. For URL-based distribution, use any other source type.
Pin what you depend on. A GitHub source takes an optional ref for a branch or tag and a sha for
an exact commit. For anything a team relies on, pin it.
Validate before you share
claude plugin validate ./my-plugin
Validation prints ✔ Validation passed, or passes with warnings. Add --strict to treat warnings as
errors. Run it from your marketplace directory too — claude plugin validate . — and install a test
plugin from the local path before pointing anyone else at it.
Validation checks structure. It does not tell you whether the plugin changes what Claude actually
does. For that, claude plugin eval runs the plugin against a set of test prompts, several times each
with and without the plugin loaded, so you can see what it contributes and catch regressions when you
change it or a new model ships. That is the check worth adding to CI, because a plugin that stops
triggering is indistinguishable from a plugin that works until someone measures it.
Distribution options
A public GitHub repository is the default: users add it with /plugin marketplace add owner/repo.
A private repository keeps a marketplace internal to your team, using your existing git authentication.
Organisation settings distribute through claude.ai for Team and Enterprise organisations. One
constraint applies there: a plugin distributed this way cannot include a top-level bin/ directory.
The community marketplace is claude-community, where third-party submissions land after review.
Submit through the in-app form on claude.ai (Team or Enterprise organisations with directory management
access) or the Console form (for individual authors). Run claude plugin validate locally first — the
review pipeline runs the same check, plus automated safety screening.
Approved plugins are pinned to a specific commit SHA in the community catalog, with CI bumping the pin as you push. The public catalog syncs nightly, so there is a delay between approval and installability.
The curated claude-plugins-official marketplace is separate. Anthropic decides what goes in it at its
discretion; there is no application process, and the submission form does not add plugins to it.
Version and update deliberately
When version is set in plugin.json, users receive updates only when you bump it. That makes the
field a release gate rather than metadata: changing a hook that runs on every file edit without bumping
the version means nobody gets the fix.
Before each release:
- Bump
versioninplugin.json, and in the marketplace entry if it declares one. - Update the
README.md— installation, usage, and what each component does. - Re-run
claude plugin validate, and the eval suite if you have one. - Have someone else install it from the marketplace, not from
--plugin-dir.
To rename or remove a plugin without stranding existing users, the marketplace renames field maps a
former name to its current name, or to null if the plugin is gone.
What you are asking people to trust
A plugin can carry hooks that run shell commands on lifecycle events, MCP servers that reach external
services, executables added to the Bash tool's PATH, and agents with their own permission modes.
Installing one is a trust decision of the same kind as adding a dependency — which is why the
vetting checklist applies to plugins at
least as much as to individual skills.
Publishing one puts you on the other side of that decision. Two things make it easier for people to say yes: a README that states plainly what each hook fires on and what each MCP server can reach, and a repository where the diff between versions is readable. Neither is hard. Both are frequently absent.
Next step: Package a proven Claude workflow as a plugin, then list its reusable skills on LLM Mart so the people who would benefit from it can find it.
Sources
Comments (0)
Sign in to join the conversation.
No comments yet.