installs.me
< cd ~/blog

·4 min read#plugins#explainer

Claude Code Plugins, Explained: Skills, Commands, Hooks, MCP

Every Claude Code plugin is answering one question four different ways: who decides when this thing runs? Get that straight and the whole plugin system stops feeling like four unrelated features bolted together.

The docs list skills, commands, hooks, and MCP servers as the four plugin surfaces. What they never say out loud is that these are four trigger models, not four content types. The same knowledge, say, "how our team writes migration scripts," could live in any of the four. The surface you pick determines when it fires and who pulls the trigger.

The mental model: four triggers

Commands fire when the human decides. A command is a Markdown file in your plugin's commands/ directory. The user types /deploy-checklist and the file's contents get injected into the conversation as instructions. Nothing happens until someone types the slash. Zero ambiguity, zero context cost until invoked.

Skills fire when the model decides. A skill is a directory containing a SKILL.md with YAML frontmatter, at minimum name and description, plus an optional references/ directory for supporting files. Claude Code loads only the frontmatter descriptions into context at session start. When the model judges that a task matches a description, it pulls in the full SKILL.md on demand. This is the crucial property: the description is the trigger. A vague description means a skill that never fires, or fires on everything. Write descriptions like routing rules ("Use when the user asks about X, Y, or pastes a Z URL"), not like marketing copy.

Hooks fire when an event happens. Nobody decides. Hooks are shell commands bound to lifecycle events: PreToolUse, PostToolUse, Stop, SessionStart, UserPromptSubmit, and friends. They run deterministically, outside the model's judgment. If you configure a PreToolUse hook on Bash, it runs before every Bash call, whether the model likes it or not. Hooks can block actions, inject context, or run formatters. This is the only surface where "the model forgot" is impossible.

MCP servers fire when the model calls a tool, but they exist to extend reach. A plugin can ship a .mcp.json declaring MCP servers, typically stdio servers the plugin spawns locally. Each server exposes tools the model can call: query a database, hit an internal API, read a calendar. Skills teach the model how to think; MCP servers give it hands it didn't have.

Where they live in a plugin

A plugin is a directory with a .claude-plugin/plugin.json manifest, plus whichever surfaces you use:

my-plugin/
  .claude-plugin/
    plugin.json
  skills/
    code-style/
      SKILL.md
      references/
  commands/
    deploy-checklist.md
  hooks/
    hooks.json
  .mcp.json

Distribution goes through a marketplace, which is just a marketplace.json listing plugins and their sources. One trap worth knowing if you host your own: plugin sources served over a URL must be git-backed (the git-subdir source type). Pointing a marketplace entry at a relative HTTP path does not error, it silently fails at install time. If your /plugin install mysteriously does nothing, check this first. Verified on Claude Code CLI 2.1.x.

Installing is two commands:

/plugin marketplace add <url-or-repo>
/plugin install <plugin-name>@<marketplace-name>

The decision guide

Ask two questions: who should trigger it, and does it need to be guaranteed?

You want...Reach forWhy
A workflow the user runs on purposeCommandExplicit invocation, no false triggers
Knowledge applied when relevantSkillModel routes by description, loads on demand
Something that must happen every timeHookDeterministic, model can't skip it
Access to external systems or dataMCP serverNew tools, not new instructions

Some sharper cuts from actually shipping plugins:

If you're tempted to write "ALWAYS do X" in a skill, you want a hook. Skills are probabilistic. The model reads the description, weighs it against the task, and sometimes decides your skill doesn't apply. For style preferences that's fine. For "never commit without running tests" it's a bug waiting to happen. Hooks are the enforcement layer; skills are the advice layer.

If the user has to remember it exists, make it a command. A skill for "generate our weekly report format" only fires if the user phrases the request in a way that matches the description. A /weekly-report command is discoverable in the command list and fires exactly when typed. Commands are also the right home for anything with arguments the user should supply consciously.

If the payload is data rather than instructions, it's MCP. People stuff API documentation into skills when what they actually want is the API itself. A skill saying "here's how to query our CRM" makes the model write curl commands from memory of your docs. An MCP server exposing a search_contacts tool makes it call a typed interface. The second one doesn't hallucinate endpoints.

Surfaces compose, and the good plugins use several. A persona plugin, for instance, ships a skill with the person's frameworks and voice in references/, commands for their signature workflows, and MCP servers into their live data. The skill fires when the conversation drifts near their expertise, the commands run their named plays on demand, and the MCP tools keep the answers grounded in current reality instead of a snapshot.

The four surfaces look like a feature list. They're actually a delegation spectrum: command (human triggers), skill (model triggers), hook (event triggers), MCP (capability, model calls). Pick by trigger, not by content, and the plugin does what you meant instead of what you wrote.

Install a person

installs.me turns your files, calendar and calls into a Claude Code plugin that thinks like you. Anyone installs it with two commands:

/plugin marketplace add https://installs.me/lautaro
/plugin install lautaro@lautaro-installs