# How to build a Claude Code plugin and share it through a marketplace

Canonical: https://clawdocx.com/docs/claude-code-plugin-marketplace
Author: Theo Marsh
Difficulty: INTERMEDIATE
Published: 2026-02-24
Updated: 2026-10-20

> Build a Claude Code plugin from one directory, check it with claude plugin validate, then list it in a marketplace.json so your team installs it by name.

A Claude Code plugin is one directory, and you can build a working one in about five minutes. Make a folder, put a skill in `skills/<name>/SKILL.md`, check it with `claude plugin validate`, and load it with `claude --plugin-dir`. Publishing is a separate and later step: a marketplace is just a `.claude-plugin/marketplace.json` file listing your plugins and where to fetch each one, so do not reach for one until the plugin already works on your own machine.

Everything below was run against Claude Code 2.1.294 on 8 October 2026, and the command output quoted is what the CLI actually printed.

## What is the smallest plugin that works?

A directory with one skill in it. The plugins overview describes a plugin as "a directory of skills, agents, hooks, MCP servers, or other components that Claude Code installs and loads as one unit", and the create page is explicit that of those components "none is required".

Here is a complete plugin. It ships a single skill that drafts release notes from the commits on a branch:

```bash
mkdir -p release-notes-tools/.claude-plugin
mkdir -p release-notes-tools/skills/draft-notes
```

`release-notes-tools/.claude-plugin/plugin.json`:

```json
{
  "name": "release-notes-tools",
  "description": "Draft release notes from the commits on the current branch",
  "version": "1.0.0",
  "author": {
    "name": "Your Name"
  },
  "license": "MIT",
  "keywords": ["release", "changelog"]
}
```

`release-notes-tools/skills/draft-notes/SKILL.md`:

```markdown
---
name: draft-notes
description: Draft release notes from the commits on the current branch. Use when the user asks for release notes, a changelog entry, or a summary of what changed since the last tag.
---

Draft release notes for the current branch.

1. Run `git describe --tags --abbrev=0` to find the last tag. If there is no tag, use the first commit.
2. Run `git log <last-tag>..HEAD --no-merges --format=%s` to list the commit subjects.
3. Group the subjects under `Added`, `Changed` and `Fixed`, dropping anything that only touches tests or formatting.
4. Write the result to `RELEASE-NOTES.md` and show the user a diff before saving.

Ask before overwriting a `RELEASE-NOTES.md` that already exists.
```

That is the whole plugin. Check it and run it:

```bash
claude plugin validate ./release-notes-tools
claude --plugin-dir ./release-notes-tools
```

Validation printed the manifest path it read and then `Validation passed`, exiting 0. In the session the skill runs as `/release-notes-tools:draft-notes`, because every plugin skill is namespaced under the plugin's name. That namespacing is the point of a plugin rather than a loose skill: two plugins can each ship a `draft-notes` skill without colliding.

One placement rule decides whether any of this loads. The create page states it as a warning: "Only `plugin.json` goes inside `.claude-plugin/`. Components saved there don't load." So `skills/`, `agents/`, `hooks/` and `.mcp.json` all sit at the plugin root, beside that folder and never inside it. If your plugin loads but its skills are missing, this is the first thing to check.

For what each component type does once it loads, and when a plugin is the wrong answer in the first place, [skills vs subagents vs plugins vs MCP servers](/blog/claude-code-skills-vs-subagents-vs-plugins-vs-mcp) draws the four distinctions. To write the skill the plugin ships, see [building custom skills](/docs/building-custom-skills).

## Do you need a manifest, and what has to be in it?

No, and only `name`. The manifest reference says it outright: "The manifest is optional. Without it, Claude Code loads the components it finds in the standard layout. The plugin name then comes from the marketplace entry, or from the directory name when you load the plugin with `--plugin-dir`." In the fields table, "`name` is the only required key."

We checked both ends of that. A copy of the plugin above with its `.claude-plugin/` folder deleted entirely validated clean, and the output line changed from `Validating plugin manifest:` to `Validating components in:`, which is how you can tell at a glance which mode you are in. A manifest carrying nothing but `name` also passed, with three warnings:

```
Found 3 warnings:

  > version: No version specified. Consider adding a version following semver (e.g., "1.0.0")
  > description: No description provided. Adding a description helps users understand what your plugin does
  > author: No author information provided. Consider adding author details for plugin attribution

Validation passed with warnings
```

So "optional" means optional for loading, not optional for shipping. Write a manifest when you want metadata, a component outside its default directory, `userConfig` values Claude Code prompts the user for, or an inline component definition.

Two fields are worth deciding deliberately rather than copying:

- **`name` is permanent.** Users install, enable and configure your plugin by `name@marketplace`, and the publish page warns that after a rename "users who already installed it lose the plugin, because their install is recorded under the old name". Set `displayName` for the label people see, and change that instead.
- **`version` is a commitment.** The docs say that if you set it and then push commits without changing it, `claude plugin update` prints that the plugin "is already at the latest version" and users keep the old copy. Either increment it on every release, or omit it in a git-hosted marketplace so Claude Code uses the commit SHA instead.

The manifest also refuses names that pass as Anthropic's own. We confirmed this with a manifest named `claude-helper`, which fails with an error rather than a warning:

```
> name: Plugin name "claude-helper" is reserved: it passes as one of Anthropic's own.
```

(The real output prefixes each result line with a tick or a cross glyph, which renders differently depending on your terminal, so the lines above are quoted without it.)

Per the reference, that error covers any name starting with `claude-`, `anthropic-`, `anthropics-` or `cc-plugin-`, the bare names `claude`, `anthropic`, `anthropics`, `claude-code` and `claude-mods`, and anything putting `official` beside `claude` or `anthropic`. Having `claude` as a whole word elsewhere, such as `mcp-for-claude`, is only a warning.

## What does claude plugin validate actually catch?

Less than its name suggests, and knowing the gap is what saves you a broken release. The command reads only files inside the directory you point it at, so anything that depends on fetching a remote source or on a path existing elsewhere is invisible to it.

We ran each row below against Claude Code 2.1.294:

| What we tried | `claude plugin validate` | Where it actually surfaces |
|---|---|---|
| Valid plugin, full manifest | Passes, exit 0 | Nothing to surface |
| No manifest at all | Passes, exit 0, reports `Validating components in:` | Nothing to surface |
| Manifest with only `name` | Passes with 3 warnings, exit 0 | `--strict` turns them into a failure, exit 1 |
| Plugin name reserved to Anthropic | Fails, exit 1 | Caught before you ship |
| Marketplace `source` containing `..` | Fails, exit 1 | Caught before you ship |
| Marketplace `source` naming a missing directory | **Passes, exit 0** | `claude plugin install`, as `Source path does not exist` |
| Entry `name` different from the manifest `name` | **Passes, exit 0** | Your users, when the install id does not resolve |

The last two rows are the ones to plan around. The create-marketplace page says most failed installs from a new marketplace come from exactly these two mistakes, and neither is a validation error, so the only thing that catches them is installing from your own marketplace before you hand it to anyone.

In CI, run it with `--strict`:

```bash
claude plugin validate --strict ./release-notes-tools
```

That turns warnings such as a missing `version` or an unknown manifest field into exit code 1. Drop `--strict` if you deliberately chose to omit `version`.

## How do you turn the plugin into a marketplace?

Add one JSON file and point Claude Code at the directory. A marketplace is a directory or repository holding `.claude-plugin/marketplace.json`, which the overview describes as "a catalog, not a hosted store".

The file requires `name`, `owner` and a `plugins` array, and each entry requires `name` and `source`:

```json
{
  "name": "acme-plugins",
  "description": "Plugins the Acme platform team maintains",
  "owner": {
    "name": "Acme Platform Team",
    "url": "https://github.com/acme"
  },
  "plugins": [
    {
      "name": "release-notes-tools",
      "source": "./plugins/release-notes-tools",
      "description": "Draft release notes from the commits on the current branch"
    }
  ]
}
```

With the plugin copied to `acme-plugins/plugins/release-notes-tools/`, that file validated clean. Then register it and install from it:

```bash
claude plugin marketplace add ./acme-plugins
claude plugin install release-notes-tools@acme-plugins
claude plugin list
```

Two rules about `source` cause most of the trouble:

- **Relative paths start from the marketplace root**, which is the directory containing `.claude-plugin/`, not from `marketplace.json` itself. So the entry above reads `./plugins/release-notes-tools` and not `./../plugins/...`. A path with `..` is a validation error, and the message we got names the fix: `Plugin source paths are resolved relative to the marketplace root (the directory containing .claude-plugin/), not relative to marketplace.json.`
- **The entry `name` and the manifest `name` must match.** The entry name is the install id and the key written into the user's settings; the manifest name is the prefix on the plugin's skills. When they differ and someone installs by the manifest name, Claude Code reports that the plugin was not found in the marketplace.

For a plugin that lives outside the marketplace directory, `source` becomes an object instead of a path. The `github` form is `{ "source": "github", "repo": "your-org/your-plugin" }`, and `git-subdir` takes a `url` and a `path` for a plugin inside a monorepo. The reference also documents `url`, `archive`, `npm` and `command` sources.

To publish from the plugin's own repository rather than a separate one, keep `marketplace.json` beside `plugin.json` in `.claude-plugin/` and give the single entry a `source` of `"./"`.

## Which way should you distribute it?

By who needs to install it, not by how finished the plugin feels. The docs set out three routes, and only one of them involves a submission:

| Route | Who can install | What you need | Automatic updates |
|---|---|---|---|
| No marketplace | The people you send the folder or a `.zip` to | The plugin's folder | None. They load the copy you sent |
| Your own marketplace | Anyone who can clone the repository, including a private one | A git host with `.claude-plugin/marketplace.json` | Off by default, per marketplace |
| Anthropic's directory | People who add it on claude.ai or in Cowork, and it reaches their Claude Code through account sync | A GitHub repository and a paid claude.ai plan to submit from | Yes, once the version you push is published |

Start at the top of that table and move down only when you need to. Sending a directory needs no marketplace and no review, and a recipient loads it with `claude --plugin-dir ./release-notes-tools` for one session, with or without a manifest, or by moving the directory, with its `.claude-plugin/plugin.json`, under `~/.claude/skills/` so it loads in every session. Only the `--plugin-dir` route works without a manifest: Claude Code loads a folder under `~/.claude/skills/` only when it holds `.claude-plugin/plugin.json`, and one without it loads nothing and reports no error.

Your own marketplace is published the moment the file is in the repository, with no form to fill in. Your users then run two commands:

```bash
claude plugin marketplace add your-org/your-marketplace
claude plugin install release-notes-tools@your-marketplace
```

Since Claude Code 2.1.292 those collapse into one, which is the change that matters most here if you write install instructions for other people:

```bash
claude plugin install release-notes-tools --marketplace your-org/your-marketplace
```

The changelog entry for 2.1.292, dated 6 October 2026, describes it as adding "`--marketplace <source>` to `claude plugin install`: adds the marketplace if needed, under the same policy checks as `claude plugin marketplace add`, then installs the plugin from it". The in-session form, `/plugin install <name> --marketplace <source>`, requires 2.1.275 or later.

Anthropic's directory is the only route with a gate on it. Submission goes through the developer portal at claude.ai/directory/manage, requires a paid claude.ai plan, and each version goes through review before it is published. The publish page is also clear that a clean local validation is not a pass: "The portal applies additional directory rules that the CLI doesn't check." Note too that Anthropic's official marketplace, `claude-plugins-official`, does not take submissions through that portal at all. The directory is a different thing again from [Claude Marketplace](/blog/claude-marketplace-launch), the website Anthropic opened in September 2026 for browsing plugins, connectors and partners.

## What breaks first, and how do you tell?

Four failures account for most of the time lost, and each has its own diagnostic.

**Components inside `.claude-plugin/`.** The plugin loads and its skills are missing. Move `skills/` to the plugin root and run `/reload-plugins`.

**`--plugin-dir` pointed at a marketplace root.** Nothing loads and there is no error, because Claude Code does not read `marketplace.json` for that flag. Point it at one plugin's own directory, the one holding `.claude-plugin/plugin.json` and the component folders.

**A component path in the manifest that does not exist.** Every component path must start with `./`, resolve inside the plugin root and exist. The `/plugin` panel's Errors tab reports `<component> path not found: <path>`, and validation reports `Path not found`.

**Edits that do not appear.** Plugins load at session start. Run `/reload-plugins` in the session to apply changes you made on disk, then confirm with `claude plugin list` or the Installed tab of `/plugin`, which lists the components Claude Code actually found.

When a plugin loads cleanly but does not change how Claude behaves, that is a different problem and has its own tool: `claude plugin eval` runs your test cases with and without the plugin and scores the difference.

## What should you check before installing someone else's plugin?

Read it first. A plugin can ship skills, hooks, MCP servers and executables, and the overview is blunt about the consequence: "what the plugin runs, it runs as you." A marketplace's tier does not change that. Claude Code accepts Anthropic's official and community marketplace names only for marketplaces sourced from `github.com/anthropics/` repositories, and the docs note that every other marketplace, including one your own employer publishes, is third-party.

Before you install anything from a marketplace you do not control, read every `SKILL.md`, every file under `hooks/`, every script those hooks call, and anything in `bin/`, which the manifest reference says is placed on the Bash tool's `PATH` while the plugin is enabled. Our [skill security audit](/docs/skill-security-audit) is the checklist we use for the skill half of that. Nothing on ClawDocx is an endorsement of a third-party plugin or skill, including any named in a source we cite.

One last thing worth knowing before you install rather than after: an enabled plugin is part of every session, not only the ones where you use it. Each invocable skill, agent and command keeps its name and description in Claude's context on every turn, which costs tokens and context window even in sessions where nothing from the plugin runs. Plugins in Anthropic's official marketplace show a context cost estimate in the Marketplaces tab of `/plugin`, and `claude plugin disable` stops a plugin without uninstalling it.

If what you actually wanted was to change Claude Code's behaviour rather than package components for other people, [mods](/blog/claude-code-mods) are the component type to read about next.

## Frequently asked questions

**Do you need a plugin.json to build a Claude Code plugin?**

No. The manifest reference says in as many words that the manifest is optional, and without it Claude Code loads whatever components it finds in the standard layout, naming the plugin after its directory when you load it with --plugin-dir. Write one when you want metadata, a component outside its default folder, or userConfig. When you do write one, name is the only required field.

**How do you test a Claude Code plugin without publishing it?**

Run claude plugin validate ./your-plugin from your shell to check the manifest and every skill, agent and command file, then start a session with claude --plugin-dir ./your-plugin. The plugin loads for that session only and nothing is written to your settings. Run /reload-plugins in the session to pick up edits you make on disk.

**What is the difference between a plugin marketplace and Claude Marketplace?**

A plugin marketplace is a directory or repository with a .claude-plugin/marketplace.json file that lists plugins and where to fetch each one, which you register with claude plugin marketplace add. Claude Marketplace is Anthropic's website at claude.com/marketplace for browsing plugins, connectors and partners. The docs state plainly that it is not a marketplace you add with /plugin marketplace add.

**Why does my plugin install fail when claude plugin validate passed?**

Validation only reads files inside the directory you point it at, so it cannot see a remote source or a path that does not exist yet. We confirmed two cases that pass validation and fail later: a relative source naming a directory that is not there fails at install with Source path does not exist, and a marketplace entry name that differs from the name in the plugin's own plugin.json passes validation but breaks the install id.

**How do people get a new version of your plugin?**

If your plugin.json sets a version, increment it and push, and users receive it when they run claude plugin update or when auto-update is on for your marketplace. Auto-update is off by default. If you push commits without changing the version, the docs say claude plugin update reports the plugin is already at the latest version and users keep the old copy.

## Sources

- [Claude Code docs: Plugins overview](https://code.claude.com/docs/en/plugins)
- [Claude Code docs: Create a Claude Code plugin](https://code.claude.com/docs/en/plugins/create)
- [Claude Code docs: Plugin manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference)
- [Claude Code docs: Create a marketplace](https://code.claude.com/docs/en/plugins/create-marketplace)
- [Claude Code docs: Publish and distribute a plugin](https://code.claude.com/docs/en/plugins/publish)
- [Claude Code docs: Add components to a plugin](https://code.claude.com/docs/en/plugins/components)
- [Claude Code changelog](https://code.claude.com/docs/en/changelog)