Module 28 · 45 min

What a Plugin Is

You can open a plugin folder, name each of its five surfaces and say in one sentence what each one changes, and run the validator on it.

If you expected a plugin to be something you compile, install, or run, forget that, a plugin is a folder. There is no program inside it to start, no step that turns it into something else, and nothing to install in the sense that Node was installed in module 26. Claude Code reads the folder, works out what is in it from where the files sit, and behaves differently afterwards. The folder is the thing.

What it changes is the circle from module 27: the model, the tools it can ask for, and what happens in between. Five kinds of file can live in the folder, and each one is a lever on a different part of that circle.

Five kinds of file, five different levers

SurfaceWhat sits in the folderWhat it changes
Skillskills/<name>/SKILL.mdGives the model a capability it can reach for on its own, or that you invoke by typing /plugin-name:skill-name
Agentagents/<name>.mdDefines a second agent, with its own instructions and its own list of tools, for one kind of job
Hookhooks/hooks.jsonRuns a program of yours at a fixed moment, such as before a tool runs, whatever the model wanted
MCP server.mcp.jsonConnects an outside program that supplies extra tools, over MCP, the Model Context Protocol
Commandcommands/<name>.mdAn older, flat-file form of a skill, kept working; the docs point new work at skills/ instead

Four of those five are named on the SDK plugins page, which lists skills, agents, hooks and MCP servers as what a plugin can include. The fifth appears in the directory layout on the same page, with the note that commands/ holds skills as flat Markdown files and that new plugins should use skills/ instead.

Where each of the five has to sit

One more file appears in the tree below, and it is the odd one out. A manifest is a small file whose job is to describe the thing it sits in, the way a name card describes the person wearing it. A plugin’s manifest is plugin.json , and it is the only file that goes inside the .claude-plugin/ directory. The plugins page records that it is optional: leave it out and Claude Code works out the components from the layout instead.

my-first-plugin/
├─ .claude-plugin/
├─ skills/
│ └─ hello/
├─ agents/
├─ hooks/
.claude-plugin/plugin.json

The manifest: the plugin's name card. Optional.

{
"name": "my-first-plugin",
"description": "A greeting plugin to learn the basics",
"version": "1.0.0",
"author": { "name": "Your Name" }
}
Read more

Optional, and worth writing anyway: the name in it is what appears in front of a skill name when you invoke one, as in /my-first-plugin:hello. Nothing else belongs in this directory.

The layout the plugins page prints, trimmed to the five surfaces. Read plugin.json first.

Each of those five has a page of its own elsewhere on this site, and you do not need them yet: module 05 for skills, module 08 for agents, module 09 for hooks, module 10 for MCP, module 04 for the flat-file commands, and module 12 for the manifest in full. This track meets all five again as it builds them, with module 38 writing the hooks and module 40 turning the chat app into the plugin.

Notice where the five live: at the top level of the plugin folder, side by side. Only plugin.json goes inside .claude-plugin/. The plugins page states that as a common mistake to avoid, in those words, which is a strong hint about how often it is made.

Make the smallest one and check it

You need Claude Code itself for this, which module 00 installs in a few lines. Then make two directories and one file, following the plugins page quickstart:

mkdir my-first-plugin
mkdir my-first-plugin/.claude-plugin

Put the manifest from the tree above into my-first-plugin/.claude-plugin/plugin.json, then check it:

claude plugin validate ./my-first-plugin

The documentation says that when validation passes, Claude Code prints ✔ Validation passed, or ✔ Validation passed with warnings when there are warnings, and that warnings do not fail validation unless you add --strict. That sentence is quoted from the plugins reference as it stood on 2026-08-27; it was not run against a binary for this page, so treat the tick mark as the shape of the answer rather than as a screenshot.

Now add the skill. Make my-first-plugin/skills/hello/SKILL.md with the two-line frontmatter from the tree, and start Claude Code with the folder loaded:

claude --plugin-dir ./my-first-plugin

Type / and the skill appears in the list as /my-first-plugin:hello. A folder, a name card, and one markdown file, and the assistant can do something it could not do this morning.

Build

The same four rungs as every page on this track.

  1. Run it. The two directories, the manifest, claude plugin validate, then the skill and —plugin-dir. Confirm the skill in the / list.
  2. Read one file. Read your own SKILL.md and say which line the model uses to decide whether to reach for the skill, and which lines it reads only afterwards.
  3. Change one line. Change the description to something narrower, restart, and look at the / list again. The text you see there is the text the model is choosing from.
  4. Build. Add a second skill directory beside the first, with its own one-line description, and do not touch the manifest. Restart and confirm both are listed. The plugins page is explicit that components are discovered from the directory layout, so a manifest edit per skill is work nobody has to do.
The mistake most people make first

.claude-plugin/ is the folder with the plugin’s name in it, so it reads like the plugin’s home, and the tidy instinct is to put the rest of the plugin in there beside the manifest: the skill written to my-first-plugin/.claude-plugin/skills/hello/SKILL.md rather than to my-first-plugin/skills/hello/SKILL.md.

Every file is well formed. The JSON parses and the frontmatter is right, so everything you would think to check comes back clean, because nothing you wrote is wrong. You start Claude Code with --plugin-dir, type /, and your skill is not in the list. There is no message telling you a directory was in the wrong place, because from Claude Code’s point of view there is no such directory: it looked at the plugin root, found no skills/, and concluded the plugin has no skills.

The plugins page names this one directly: “Don’t put commands/, agents/, skills/, or hooks/ inside the .claude-plugin/ directory. Only plugin.json goes inside .claude-plugin/. All other directories must be at the plugin root level.” Move the folder up one level and it appears. The habit worth forming from this is the check, not the rule: after wiring any surface into a plugin, look for it in the / list before you believe it is there.

Does this travel?

The contents travel further than the container. A survey of ten other harnesses made for this course on 2026-08-27 found the industry converging on Claude Code’s own file layout rather than on a neutral standard: Copilot CLI reads .claude/skills/, .claude/agents/, .claude/commands/, CLAUDE.md and Claude-format hook JSON unmodified, and Cline, Windsurf and OpenCode read the skills directory too. MCP travels best of all, because the protocol and the server are identical everywhere and only the file holding the configuration changes. What does not travel is the wrapper: .claude-plugin/plugin.json, —plugin-dir and the validator are Claude Code’s, and no other harness installs a plugin by that name.

Check yourself

  1. A plugin folder has a SKILL.md, an agents/ directory and a hooks/hooks.json, and no plugin.json at all. Is it a plugin? What does it lose by not having one?
  2. Three of the five surfaces are text the model reads and weighs. One is a program that runs whatever the model wanted. Name each group, say which surface is left over and why it belongs to neither, and say what that difference decides about where a rule you need kept has to live.
  3. You add a skill, restart, and it is not in the / list. Name three things that could be true, and the order you would check them in.