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
| Surface | What sits in the folder | What it changes |
|---|---|---|
| Skill | skills/<name>/SKILL.md | Gives the model a capability it can reach for on its own, or that you invoke by typing /plugin-name:skill-name |
| Agent | agents/<name>.md | Defines a second agent, with its own instructions and its own list of tools, for one kind of job |
| Hook | hooks/hooks.json | Runs a program of yours at a fixed moment, such as before a tool runs, whatever the model wanted |
| MCP server | .mcp.json | Connects an outside program that supplies extra tools, over MCP, the Model Context Protocol |
| Command | commands/<name>.md | An 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.
.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.
skills/hello/SKILL.md One skill. The directory name is the skill name.
---
description: Greet the user with a friendly message
---
Greet the user warmly and ask how you can help them today. Read more
The body is instructions the model reads once the skill is in play. The description is how it decides whether to reach for it at all, which is why that is the one field worth agonising over.
agents/reviewer.md One agent definition: its own instructions, its own tools.
---
description: Reads a change and reports what looks wrong
tools: Read, Grep, Glob
---
Read the change. Report at most three problems, each with the file and line. Read more
One file per agent, under agents/. The description tells the main agent when to hand work over; the body is that agent's standing instructions; tools is the list it is allowed to use, and leaving it out gives it everything the session can reach.
hooks/hooks.json Programs of yours, wired to moments in the session.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "./scripts/check.sh" }]
}
]
}
} Read more
A hook is the one surface here that is not text for the model to weigh. It is a program that runs and whose answer is obeyed. The matcher says which tool calls it applies to, and the command is the program.
.mcp.json Outside programs that supply extra tools.
{
"mcpServers": {
"notes": { "command": "node", "args": ["./servers/notes.mjs"] }
}
} Read more
MCP is the Model Context Protocol, an open standard for a program that offers tools to an agent. The server named here runs on your machine and its tools show up in the session under a name that says where they came from.
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.
The same four rungs as every page on this track.
- Run it. The two directories, the manifest,
claude plugin validate, then the skill and—plugin-dir. Confirm the skill in the/list. - Read one file. Read your own
SKILL.mdand say which line the model uses to decide whether to reach for the skill, and which lines it reads only afterwards. - 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. - 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.
.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.
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
- A plugin folder has a
SKILL.md, anagents/directory and ahooks/hooks.json, and noplugin.jsonat all. Is it a plugin? What does it lose by not having one? - 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.
- 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.