Follow Discord
Sweep 27 Oct 2026 · 19:04Z Build v2.1.312 531 read Stable v2.1.308 Latest v2.1.312 Next v2.1.312 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-code

Add components to a plugin changedplugins/components

Nearest release: v2.1.308, published 3 hours after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 22 Oct 2026 00:39 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 23 Oct 2026 19:07 UTC.

Upstream edited
Recorded here
Lines+75added
Lines−75removed
From line 2 where the diff opens
First seen 25 Sep 2026 this site's first read of the page
Recorded edits18to this page, all time

The whole hunk

from line 2, old and new numbered
/
lines
from line 2
22 
33> Add skills, hooks, MCP servers, and every other component type to a Claude Code plugin, with an example that validates for each.
44 
5A Claude Code plugin is built from components, such as skills, agents, hooks, and MCP servers. Each component has a default folder in the plugin, an optional manifest key in `.claude-plugin/plugin.json` that replaces or adds to that folder, and a name the user sees. For each key's full field table, see the [manifest reference](/docs/en/plugins/manifest-reference.md#fields).
5A Claude Code plugin is built from components, such as skills, agents, hooks, and MCP servers. Each component has a default folder in the plugin, an optional manifest key in `.claude-plugin/plugin.json` that replaces or adds to that folder, and a name the user sees. For each key's full field table, see the [manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference.md#fields).
66 
77Use this page to add a component to a plugin that already loads.
88 
9After you add a component, run `/reload-plugins` in a running session or start a new one so Claude Code loads it. To check the component's file before loading it, run [`claude plugin validate .`](/docs/en/plugins/cli-reference#plugin-validate) in your shell from the plugin directory.
9After you add a component, run `/reload-plugins` in a running session or start a new one so Claude Code loads it. To check the component's file before loading it, run [`claude plugin validate .`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-validate) in your shell from the plugin directory.
1010 
1111> [!NOTE]
1212>
1313> These cases are covered on other pages:
1414>
15> * **Building your first plugin**: start with [Create a plugin](/docs/en/plugins/create.md)
16> * **Installing someone else's plugin**: see [Install plugins](/docs/en/plugins/install.md)
15> * **Building your first plugin**: start with [Create a plugin](https://code.claude.com/docs/en/plugins/create.md)
16> * **Installing someone else's plugin**: see [Install plugins](https://code.claude.com/docs/en/plugins/install.md)
1717> * **Your plugin's users are on claude.ai or in Cowork**: a different set of components loads there. See [Plugin structure and testing](https://claude.com/docs/plugins/build) and the [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app)
1818 
1919## Explore the plugin directory
from line 42
4242 
4343Selected piece
4444 
45The [manifest](/docs/en/plugins/manifest-reference.md) is the `plugin.json` file in a plugin's `.claude-plugin/` directory. It contains the plugin's metadata and the `userConfig` values that Claude Code prompts the user for. Claude Code loads a plugin without one. Inside the file, only `name` is required. In this one, `description` is the text users see for the plugin in `/plugin`, and `version` keeps users on that version until you change it:
45The [manifest](https://code.claude.com/docs/en/plugins/manifest-reference.md) is the `plugin.json` file in a plugin's `.claude-plugin/` directory. It contains the plugin's metadata and the `userConfig` values that Claude Code prompts the user for. Claude Code loads a plugin without one. Inside the file, only `name` is required. In this one, `description` is the text users see for the plugin in `/plugin`, and `version` keeps users on that version until you change it:
4646 
4747```json theme={null}
4848{
from line 52
5252}
5353```
5454 
55A [skill](/docs/en/skills.md) is a `SKILL.md` file. Save each skill in its own directory under `skills/`. Claude reads every skill's `description`, and when what the user asks for matches it, such as asking Claude to review a pull request here, Claude loads the skill's instructions and follows them. The user can also run it directly as `/my-plugin:review`:
55A [skill](https://code.claude.com/docs/en/skills.md) is a `SKILL.md` file. Save each skill in its own directory under `skills/`. Claude reads every skill's `description`, and when what the user asks for matches it, such as asking Claude to review a pull request here, Claude loads the skill's instructions and follows them. The user can also run it directly as `/my-plugin:review`:
5656 
5757```markdown theme={null}
5858---
from line 72
7272Summarize what this repository does in three sentences.
7373```
7474 
75A [subagent](/docs/en/sub-agents.md) is a separate assistant, with its own instructions and its own context window, that Claude can delegate a task to and get a result back from. Each Markdown file under `agents/` defines one: the frontmatter names it and says when to use it, and the body is its system prompt. This one is named `my-plugin:security-reviewer`, and the user can invoke it with `@agent-my-plugin:security-reviewer`:
75A [subagent](https://code.claude.com/docs/en/sub-agents.md) is a separate assistant, with its own instructions and its own context window, that Claude can delegate a task to and get a result back from. Each Markdown file under `agents/` defines one: the frontmatter names it and says when to use it, and the body is its system prompt. This one is named `my-plugin:security-reviewer`, and the user can invoke it with `@agent-my-plugin:security-reviewer`:
7676 
7777```markdown theme={null}
7878---
from line 84
8484You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.
8585```
8686 
87A [hook](/docs/en/hooks-guide.md) runs something automatically at a point in Claude Code's lifecycle, such as after every file edit: a shell command, an HTTP request, an MCP tool call, a prompt to a model, or a subagent. Save the plugin's hooks in `hooks/hooks.json` at the plugin root. This one runs the plugin's `scripts/format.sh` after Claude writes or edits a file:
87A [hook](https://code.claude.com/docs/en/hooks-guide.md) runs something automatically at a point in Claude Code's lifecycle, such as after every file edit: a shell command, an HTTP request, an MCP tool call, a prompt to a model, or a subagent. Save the plugin's hooks in `hooks/hooks.json` at the plugin root. This one runs the plugin's `scripts/format.sh` after Claude writes or edits a file:
8888 
8989```json theme={null}
9090{
from line 116
116116]
117117```
118118 
119A plugin can include [output styles](/docs/en/output-styles.md), which change how Claude formats and phrases its replies. Save each output style as `output-styles/<name>.md`. This one appears in `/output-style` as `my-plugin:terse`:
119A plugin can include [output styles](https://code.claude.com/docs/en/output-styles.md), which change how Claude formats and phrases its replies. Save each output style as `output-styles/<name>.md`. This one appears in `/output-style` as `my-plugin:terse`:
120120 
121121```markdown theme={null}
122122---
from line 128
128128Keep every reply short. Skip preambles and summaries.
129129```
130130 
131A plugin can include [color themes](/docs/en/terminal-config.md#create-a-custom-theme) for the Claude Code interface. Save each theme as `themes/<slug>.json`. This one appears in `/theme` as `Dracula`, marked as from `my-plugin`:
131A plugin can include [color themes](https://code.claude.com/docs/en/terminal-config.md#create-a-custom-theme) for the Claude Code interface. Save each theme as `themes/<slug>.json`. This one appears in `/theme` as `Dracula`, marked as from `my-plugin`:
132132 
133133```json theme={null}
134134{
from line 141
141141}
142142```
143143 
144The `workflows/` folder holds [workflow](/docs/en/workflows.md) `.js` files: a `meta` block, then a script body that orchestrates several subagents. This one runs as `/my-plugin:audit-routes`:
144The `workflows/` folder holds [workflow](https://code.claude.com/docs/en/workflows.md) `.js` files: a `meta` block, then a script body that orchestrates several subagents. This one runs as `/my-plugin:audit-routes`:
145145 
146146```javascript theme={null}
147147export const meta = {
from line 174
174174npx prettier --write .
175175```
176176 
177A `settings.json` at the plugin root holds [settings](/docs/en/settings-reference.md) that apply while the plugin is enabled, so a plugin can change how the session behaves and not only add components. Only two keys take effect from a plugin, [`agent`](/docs/en/settings-reference#agent) and [`subagentStatusLine`](/docs/en/settings-reference#subagentstatusline); every other key is dropped. See [Default settings](#default-settings).
177A `settings.json` at the plugin root holds [settings](https://code.claude.com/docs/en/settings-reference.md) that apply while the plugin is enabled, so a plugin can change how the session behaves and not only add components. Only two keys take effect from a plugin, [`agent`](https://code.claude.com/docs/en/settings-reference#agent) and [`subagentStatusLine`](https://code.claude.com/docs/en/settings-reference#subagentstatusline); every other key is dropped. See [Default settings](#default-settings).
178178 
179179This one sets `agent`, which runs the session's main thread as the plugin's own `security-reviewer` agent, so that agent's system prompt, tool restrictions, and model apply to the whole session:
180180 
from line 184
184184}
185185```
186186 
187An [MCP server](/docs/en/mcp.md) gives Claude tools from an external system. Declare it in `.mcp.json` at the plugin root. This one starts a local server from a script inside the plugin, and appears in `/mcp` as `plugin:my-plugin:db`:
187An [MCP server](https://code.claude.com/docs/en/mcp.md) gives Claude tools from an external system. Declare it in `.mcp.json` at the plugin root. This one starts a local server from a script inside the plugin, and appears in `/mcp` as `plugin:my-plugin:db`:
188188 
189189```json theme={null}
190190{
from line 197
197197}
198198```
199199 
200An LSP server gives Claude [diagnostics and code navigation](/docs/en/plugins/code-intelligence.md) for a language. Declare the server in `.lsp.json` at the plugin root. This one connects the Go language server for `.go` files:
200An LSP server gives Claude [diagnostics and code navigation](https://code.claude.com/docs/en/plugins/code-intelligence.md) for a language. Declare the server in `.lsp.json` at the plugin root. This one connects the Go language server for `.go` files:
201201 
202202```json theme={null}
203203{
from line 217
217217 
218218### Skills
219219 
220A [skill](/docs/en/skills.md) is a `SKILL.md` file that Claude can load when its description matches the task. The user can also run it as a command. Save each skill in its own directory under `skills/`:
220A [skill](https://code.claude.com/docs/en/skills.md) is a `SKILL.md` file that Claude can load when its description matches the task. The user can also run it as a command. Save each skill in its own directory under `skills/`:
221221 
222222```text theme={null}
223223my-plugin/
from line 240
240240 
241241After you load the plugin, `/my-plugin:review` runs the skill. The command name and who can invoke it follow these rules:
242242 
243* **Command name**: `/<plugin>:<directory>`, so `skills/review/SKILL.md` in `my-plugin` is `/my-plugin:review`. If you set `name` in the frontmatter, it replaces the last segment and the plugin prefix stays. See [how a skill gets its command name](/docs/en/skills.md#how-a-skill-gets-its-command-name)
244* **Who invokes it**: Claude, the user, or both, controlled by frontmatter. See [Control who invokes a skill](/docs/en/skills.md#control-who-invokes-a-skill)
243* **Command name**: `/<plugin>:<directory>`, so `skills/review/SKILL.md` in `my-plugin` is `/my-plugin:review`. If you set `name` in the frontmatter, it replaces the last segment and the plugin prefix stays. See [how a skill gets its command name](https://code.claude.com/docs/en/skills.md#how-a-skill-gets-its-command-name)
244* **Who invokes it**: Claude, the user, or both, controlled by frontmatter. See [Control who invokes a skill](https://code.claude.com/docs/en/skills.md#control-who-invokes-a-skill)
245245 
246246You can also place skills outside the default `skills/` directory:
247247 
248248* **Additional directories**: list them in the `skills` manifest key. They add to the default `skills/` scan rather than replacing it, unlike `commands` and `agents`
249* **A single skill at the plugin root**: with no `skills/` directory and no `skills` manifest key, a `SKILL.md` at the plugin root loads as one skill. Set `name` in its frontmatter, because otherwise a marketplace install names the skill after its [cache directory](/docs/en/plugins/loading.md#find-plugins-on-disk) rather than your plugin
249* **A single skill at the plugin root**: with no `skills/` directory and no `skills` manifest key, a `SKILL.md` at the plugin root loads as one skill. Set `name` in its frontmatter, because otherwise a marketplace install names the skill after its [cache directory](https://code.claude.com/docs/en/plugins/loading.md#find-plugins-on-disk) rather than your plugin
250250 
251251To include instructions in a plugin, write them as a skill. Claude Code doesn't load a `CLAUDE.md` at the plugin root, and `claude plugin validate` warns `CLAUDE.md at the plugin root is not loaded as project context`.
252252 
253If a rule must hold every time, such as [blocking edits to protected files](/docs/en/hooks-guide.md#block-edits-to-protected-files), add it to the plugin as a [hook](#hooks) rather than a skill. To choose between the two, see the Hook vs Skill tab under [Compare similar features](/docs/en/features-overview.md#compare-similar-features).
253If a rule must hold every time, such as [blocking edits to protected files](https://code.claude.com/docs/en/hooks-guide.md#block-edits-to-protected-files), add it to the plugin as a [hook](#hooks) rather than a skill. To choose between the two, see the Hook vs Skill tab under [Compare similar features](https://code.claude.com/docs/en/features-overview.md#compare-similar-features).
254254 
255For frontmatter fields and supporting files, see [Skills](/docs/en/skills.md).
255For frontmatter fields and supporting files, see [Skills](https://code.claude.com/docs/en/skills.md).
256256 
257257### Commands
258258 
from line 286
286286 
287287Load the plugin and run `/my-plugin:about` in the session to confirm it loaded.
288288 
289For the full key syntax, see [`commands`](/docs/en/plugins/manifest-reference#commands).
289For the full key syntax, see [`commands`](https://code.claude.com/docs/en/plugins/manifest-reference#commands).
290290 
291291### Agents
292292 
293A [subagent](/docs/en/sub-agents.md) is a separate assistant, with its own instructions and context window, that Claude can delegate a task to. Each Markdown file under `agents/` defines one:
293A [subagent](https://code.claude.com/docs/en/sub-agents.md) is a separate assistant, with its own instructions and context window, that Claude can delegate a task to. Each Markdown file under `agents/` defines one:
294294 
295295```markdown agents/security-reviewer.md theme={null}
296296---
from line 302
302302You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.
303303```
304304 
305This agent is named `my-plugin:security-reviewer`, and the user can [invoke it explicitly](/docs/en/sub-agents.md#invoke-subagents-explicitly) with `@agent-my-plugin:security-reviewer`. The name form is `<plugin>:<name>`, where `<name>` comes from the frontmatter `name` field, or from the file name when that field is missing.
305This agent is named `my-plugin:security-reviewer`, and the user can [invoke it explicitly](https://code.claude.com/docs/en/sub-agents.md#invoke-subagents-explicitly) with `@agent-my-plugin:security-reviewer`. The name form is `<plugin>:<name>`, where `<name>` comes from the frontmatter `name` field, or from the file name when that field is missing.
306306 
307307The `agents` manifest key replaces the `agents/` scan.
308308 
309309#### Organize agents in subfolders
310310 
311You can put plugin agent files in subfolders of `agents/`. Claude Code [loads them recursively](/docs/en/sub-agents.md#choose-the-subagent-scope) and joins the plugin name, each subfolder name, and the file name with colons to form the agent's scoped name. For example, `agents/review/security.md` in a plugin named `my-plugin` loads as `my-plugin:review:security`. Two settings change that name:
311You can put plugin agent files in subfolders of `agents/`. Claude Code [loads them recursively](https://code.claude.com/docs/en/sub-agents.md#choose-the-subagent-scope) and joins the plugin name, each subfolder name, and the file name with colons to form the agent's scoped name. For example, `agents/review/security.md` in a plugin named `my-plugin` loads as `my-plugin:review:security`. Two settings change that name:
312312 
313313* Frontmatter `name`: it replaces only the file name, so `name: audit` in `agents/review/security.md` loads as `my-plugin:review:audit`
314* Manifest [`agents`](/docs/en/plugins/manifest-reference#fields) field: a file you list there loads without subfolder names, so `"agents": "./custom/review/security.md"` loads as `my-plugin:security`
314* Manifest [`agents`](https://code.claude.com/docs/en/plugins/manifest-reference#fields) field: a file you list there loads without subfolder names, so `"agents": "./custom/review/security.md"` loads as `my-plugin:security`
315315 
316316#### Frontmatter fields in plugin agents
317317 
318318A plugin agent's frontmatter follows these rules:
319319 
320* **Supported fields**: `name`, `description`, `model`, `effort`, `maxTurns`, `autoCompactWindow`, `tools`, `disallowedTools`, `skills`, `memory`, `background`, `omitClaudeMd`, `isolation`, `color`, and the `cacheTtl` key of `experimental`. The only valid `isolation` value is `"worktree"`. See [supported frontmatter fields](/docs/en/sub-agents.md#supported-frontmatter-fields) for what each one does
320* **Supported fields**: `name`, `description`, `model`, `effort`, `maxTurns`, `autoCompactWindow`, `tools`, `disallowedTools`, `skills`, `memory`, `background`, `omitClaudeMd`, `isolation`, `color`, and the `cacheTtl` key of `experimental`. The only valid `isolation` value is `"worktree"`. See [supported frontmatter fields](https://code.claude.com/docs/en/sub-agents.md#supported-frontmatter-fields) for what each one does
321321* **Ignored fields**: `permissionMode`, `hooks`, `mcpServers`, and `initialPrompt`. An agent file can't add hooks or MCP servers on its own, so add those as plugin [hooks](#hooks) and [MCP servers](#mcp-servers) instead
322322* **A `name` longer than 256 characters**: Claude Code ignores it and names the agent after its file, as it does when the field is missing
323* **Frontmatter that doesn't parse**: the agent still loads with every field ignored. It's named after the file, and its description reads `Agent from my-plugin plugin`. Run [`claude plugin validate`](/docs/en/plugins/cli-reference#plugin-validate) in your shell to find these files
323* **Frontmatter that doesn't parse**: the agent still loads with every field ignored. It's named after the file, and its description reads `Agent from my-plugin plugin`. Run [`claude plugin validate`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-validate) in your shell to find these files
324324 
325For what each field does and the precedence rules, see [Subagents](/docs/en/sub-agents.md#supported-frontmatter-fields).
325For what each field does and the precedence rules, see [Subagents](https://code.claude.com/docs/en/sub-agents.md#supported-frontmatter-fields).
326326 
327327### Hooks
328328 
329A [hook](/docs/en/hooks-guide.md) runs something automatically at a point in Claude Code's lifecycle, such as after every file edit: a shell command, an HTTP request, an MCP tool call, a prompt to a model, or a subagent. Save the plugin's hooks in `hooks/hooks.json` at the plugin root, under a top-level `"hooks"` key, in the same shape as the `hooks` object in `settings.json`. That lets you copy an existing settings hook in unchanged.
329A [hook](https://code.claude.com/docs/en/hooks-guide.md) runs something automatically at a point in Claude Code's lifecycle, such as after every file edit: a shell command, an HTTP request, an MCP tool call, a prompt to a model, or a subagent. Save the plugin's hooks in `hooks/hooks.json` at the plugin root, under a top-level `"hooks"` key, in the same shape as the `hooks` object in `settings.json`. That lets you copy an existing settings hook in unchanged.
330330 
331331This hook runs a bundled script after every `Write` or `Edit`:
332332 
from line 350
350350 
351351Save the script at `scripts/format.sh` and make it executable.
352352 
353Load the plugin and ask Claude to edit a file. A `PostToolUse` hook that exits 0 shows nothing in the transcript, so confirm it ran with [debug logging](/docs/en/hooks.md#debug-hooks) or by what the script itself changes.
353Load the plugin and ask Claude to edit a file. A `PostToolUse` hook that exits 0 shows nothing in the transcript, so confirm it ran with [debug logging](https://code.claude.com/docs/en/hooks.md#debug-hooks) or by what the script itself changes.
354354 
355Hooks in `hooks/hooks.json` and in the `hooks` manifest key both load. For every event and its payload, see [Hook events](/docs/en/hooks.md#hook-events).
355Hooks in `hooks/hooks.json` and in the `hooks` manifest key both load. For every event and its payload, see [Hook events](https://code.claude.com/docs/en/hooks.md#hook-events).
356356 
357When another enabled plugin has the same name, one of the two registers its `hooks/hooks.json` hooks and the other's are left out. See [Hooks when two enabled plugins share a name](/docs/en/plugins/loading.md#hooks-when-two-enabled-plugins-share-a-name) for which one, and for the note in `/plugin` that tells you.
357When another enabled plugin has the same name, one of the two registers its `hooks/hooks.json` hooks and the other's are left out. See [Hooks when two enabled plugins share a name](https://code.claude.com/docs/en/plugins/loading.md#hooks-when-two-enabled-plugins-share-a-name) for which one, and for the note in `/plugin` that tells you.
358358 
359To write hooks as JavaScript functions that run inside Claude Code and can draw in its interface, list a module file under a `modules` key in the same `hooks/hooks.json`. A plugin with one is a mod. See [Create a mod](/docs/en/plugins/mods/create.md).
359To write hooks as JavaScript functions that run inside Claude Code and can draw in its interface, list a module file under a `modules` key in the same `hooks/hooks.json`. A plugin with one is a mod. See [Create a mod](https://code.claude.com/docs/en/plugins/mods/create.md).
360360 
361361#### When plugin hooks fire
362362 
363363A plugin's hooks don't wait for one of the plugin's skills or commands to be used. Claude Code registers them when a session loads the plugin, and they fire on their events from then on. To limit when a hook runs, narrow its `matcher`.
364364 
365If a hook never fires, see [hooks that don't fire](/docs/en/plugins/troubleshooting.md#failed-to-load-hooks-from-and-hooks-that-dont-fire).
365If a hook never fires, see [hooks that don't fire](https://code.claude.com/docs/en/plugins/troubleshooting.md#failed-to-load-hooks-from-and-hooks-that-dont-fire).
366366 
367367#### Environment, quoting, and matching MCP tools
368368 
from line 369
369369The hook's environment, the quoting of `${CLAUDE_PLUGIN_ROOT}`, and matchers for the plugin's own MCP tools work as follows:
370370 
371371* **Environment**: every hook process receives `CLAUDE_PLUGIN_ROOT` and `CLAUDE_PLUGIN_DATA` in its environment, plus `CLAUDE_PLUGIN_OPTION_<KEY>` for each [user configuration](#user-configuration) value, so your script can read them from there
372* **Quoting**: when `command` has no `args`, it runs through a shell, so wrap the `${CLAUDE_PLUGIN_ROOT}` path in double quotes, as the `hooks/hooks.json` example under [Hooks](#hooks) does, to keep the expanded path one shell word. When you pass `args` instead, each element is passed as one argument with no shell and needs no quoting. See [exec form and shell form](/docs/en/hooks.md#exec-form-and-shell-form)
373* **Matching the plugin's own MCP tools**: a tool from an [MCP server this plugin declares](#mcp-servers) is named `mcp__plugin_<plugin>_<server>__<tool>`, so write that full name in the matcher. A matcher on the server name alone never fires. See [Match MCP tools](/docs/en/hooks.md#match-mcp-tools)
372* **Quoting**: when `command` has no `args`, it runs through a shell, so wrap the `${CLAUDE_PLUGIN_ROOT}` path in double quotes, as the `hooks/hooks.json` example under [Hooks](#hooks) does, to keep the expanded path one shell word. When you pass `args` instead, each element is passed as one argument with no shell and needs no quoting. See [exec form and shell form](https://code.claude.com/docs/en/hooks.md#exec-form-and-shell-form)
373* **Matching the plugin's own MCP tools**: a tool from an [MCP server this plugin declares](#mcp-servers) is named `mcp__plugin_<plugin>_<server>__<tool>`, so write that full name in the matcher. A matcher on the server name alone never fires. See [Match MCP tools](https://code.claude.com/docs/en/hooks.md#match-mcp-tools)
374374 
375375### MCP servers
376376 
377An MCP server gives Claude tools from an external system. Declare it in `.mcp.json` at the plugin root, in the same shape as a [project `.mcp.json`](/docs/en/mcp#project-scope). This `.mcp.json` declares one server named `db`:
377An MCP server gives Claude tools from an external system. Declare it in `.mcp.json` at the plugin root, in the same shape as a [project `.mcp.json`](https://code.claude.com/docs/en/mcp#project-scope). This `.mcp.json` declares one server named `db`:
378378 
379379```json .mcp.json theme={null}
380380{
from line 393
393393 
394394`claude plugin validate` checks `.mcp.json` and reports a server entry that Claude Code would drop at load time as an error. Requires Claude Code v2.1.281 or later.
395395 
396For where a bad entry shows up at load time, see [MCP servers that don't start](/docs/en/plugins/troubleshooting.md#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start).
396For where a bad entry shows up at load time, see [MCP servers that don't start](https://code.claude.com/docs/en/plugins/troubleshooting.md#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start).
397397 
398398The `mcpServers` manifest key takes an inline server map, a path to a JSON file, or an array of those. When a manifest server has the same name as one in `.mcp.json`, the manifest server replaces it.
399399 
from line 405
405405 
406406The server's names, variable substitution, and reload behavior follow these rules:
407407 
408* **Server name**: `plugin:<plugin>:<server>`, so the `db` server in `my-plugin` is `plugin:my-plugin:db` in `/mcp`. Use the same form to name the server in an [`mcp_tool` hook](/docs/en/hooks#mcp-tool-hook-fields)
409* **Tool names**: `mcp__plugin_<plugin>_<server>__<tool>`, so a `query` tool on that `db` server is `mcp__plugin_my-plugin_db__query`. That is the name to use in [permission rules](/docs/en/permissions.md) and [hook matchers](#hooks)
408* **Server name**: `plugin:<plugin>:<server>`, so the `db` server in `my-plugin` is `plugin:my-plugin:db` in `/mcp`. Use the same form to name the server in an [`mcp_tool` hook](https://code.claude.com/docs/en/hooks#mcp-tool-hook-fields)
409* **Tool names**: `mcp__plugin_<plugin>_<server>__<tool>`, so a `query` tool on that `db` server is `mcp__plugin_my-plugin_db__query`. That is the name to use in [permission rules](https://code.claude.com/docs/en/permissions.md) and [hook matchers](#hooks)
410410* **Substitution**: `${CLAUDE_PLUGIN_ROOT}` and the other [path variables](#path-variables-and-persistent-data) are substituted in `command`, `args`, and `env`. No quoting is needed in `args`, because each element is passed as one argument
411* **Reload**: when the user runs `/reload-plugins` and [the reload applies](/docs/en/plugins/cli-reference.md#reloads-that-change-mcp-tools), a server whose configuration is unchanged keeps its connection. A server whose configuration changed reconnects, and one you removed disconnects
411* **Reload**: when the user runs `/reload-plugins` and [the reload applies](https://code.claude.com/docs/en/plugins/cli-reference.md#reloads-that-change-mcp-tools), a server whose configuration is unchanged keeps its connection. A server whose configuration changed reconnects, and one you removed disconnects
412412 
413413#### Include a packaged MCPB server
414414 
from line 428
428428Users supply the values in one of two ways:
429429 
430430* **In `/plugin`**: select the plugin on the **Installed** tab and choose **Configure**
431* **At install, from the shell**: pass [`--config <server>.<key>=<value>`](/docs/en/plugins/cli-reference#plugin-install) to `claude plugin install`. Requires Claude Code v2.1.285 or later, and works only for a bundle packaged inside the plugin.
431* **At install, from the shell**: pass [`--config <server>.<key>=<value>`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) to `claude plugin install`. Requires Claude Code v2.1.285 or later, and works only for a bundle packaged inside the plugin.
432432 
433For transports and authentication, see [MCP](/docs/en/mcp.md#plugin-provided-mcp-servers).
433For transports and authentication, see [MCP](https://code.claude.com/docs/en/mcp.md#plugin-provided-mcp-servers).
434434 
435435### LSP servers
436436 
437An LSP server gives Claude diagnostics and code navigation for a language. If an [official code intelligence plugin](/docs/en/plugins/code-intelligence.md) already covers your language, install that instead of writing one. Otherwise declare the server in `.lsp.json` at the plugin root:
437An LSP server gives Claude diagnostics and code navigation for a language. If an [official code intelligence plugin](https://code.claude.com/docs/en/plugins/code-intelligence.md) already covers your language, install that instead of writing one. Otherwise declare the server in `.lsp.json` at the plugin root:
438438 
439439```json .lsp.json theme={null}
440440{
from line 459
459459 
460460The `lspServers` manifest key takes the same map inline, a path to a JSON file, or an array of those, and its servers add to the ones in `.lsp.json`. When a manifest server has the same name as one in `.lsp.json`, the manifest server replaces it.
461461 
462For `transport`, timeouts, restarts, and the other fields, see [`lspServers`](/docs/en/plugins/manifest-reference#lspservers).
462For `transport`, timeouts, restarts, and the other fields, see [`lspServers`](https://code.claude.com/docs/en/plugins/manifest-reference#lspservers).
463463 
464464Send log output to stderr, not stdout. Claude Code reads a server's stdout as protocol messages only, and accepts message headers up to 64 KiB and a message body up to 32 MiB.
465465 
from line 467
467467 
468468### Executables
469469 
470Files in `bin/` at the plugin root are on the `PATH` of the Bash tool's shell while the plugin is enabled, so Claude can run them as bare commands. A command that Claude runs with the [PowerShell tool](/docs/en/tools-reference.md#powershell-tool) outside the [sandbox](/docs/en/sandboxing.md) finds them on its `PATH` too, and a sandboxed PowerShell command doesn't. Add an executable script:
470Files in `bin/` at the plugin root are on the `PATH` of the Bash tool's shell while the plugin is enabled, so Claude can run them as bare commands. A command that Claude runs with the [PowerShell tool](https://code.claude.com/docs/en/tools-reference.md#powershell-tool) outside the [sandbox](https://code.claude.com/docs/en/sandboxing.md) finds them on its `PATH` too, and a sandboxed PowerShell command doesn't. Add an executable script:
471471 
472472```bash bin/hello-plugin theme={null}
473473#!/bin/bash
from line 494
494494 
495495Load the plugin and start a session. Claude then answers in the main conversation with the `security-reviewer` agent's system prompt and model.
496496 
497For everything the key controls, see the [`agent` setting](/docs/en/settings-reference#agent).
497For everything the key controls, see the [`agent` setting](https://code.claude.com/docs/en/settings-reference#agent).
498498 
499499When the same key is set in more than one place, these rules decide which value applies:
500500 
from line 502
502502* **User settings over plugin defaults**: across settings sources, plugin defaults are the lowest layer, so a user's own `agent` in `~/.claude/settings.json` overrides yours
503503* **Two plugins set the same key**: the value from the plugin loaded last applies, and `claude --debug` logs `overrides setting`
504504 
505For the `subagentStatusLine` shape, see [subagent status lines](/docs/en/statusline.md#subagent-status-lines).
505For the `subagentStatusLine` shape, see [subagent status lines](https://code.claude.com/docs/en/statusline.md#subagent-status-lines).
506506 
507507### Themes and output styles
508508 
from line 510
510510 
511511| Component | Save as | Format | Appears in | Manifest key |
512512| :- | :- | :- | :- | :- |
513| Theme | `themes/<slug>.json` | The [custom theme file](/docs/en/terminal-config.md#create-a-custom-theme) format users write in `~/.claude/themes/` | `/theme`, under the file's `name` | `experimental.themes` |
514| Output style | `output-styles/<name>.md` | The [custom output style](/docs/en/output-styles.md#create-a-custom-output-style) format, with `name` and `description` frontmatter | `/output-style`, as `<plugin>:<name>` | `outputStyles` |
513| Theme | `themes/<slug>.json` | The [custom theme file](https://code.claude.com/docs/en/terminal-config.md#create-a-custom-theme) format users write in `~/.claude/themes/` | `/theme`, under the file's `name` | `experimental.themes` |
514| Output style | `output-styles/<name>.md` | The [custom output style](https://code.claude.com/docs/en/output-styles.md#create-a-custom-output-style) format, with `name` and `description` frontmatter | `/output-style`, as `<plugin>:<name>` | `outputStyles` |
515515 
516516Plugin themes are read-only, so when a user edits one in `/theme`, the edit is saved as a copy in their own themes directory.
517517 
from line 530
530530 
531531### Channels
532532 
533A [channel](/docs/en/channels.md) lets an outside system such as a chat app send messages into a session. In a plugin, a channel is one of the MCP servers plus a `channels` entry that binds to it and can prompt for its own configuration. This manifest binds a channel to a `telegram` server and asks for a bot token:
533A [channel](https://code.claude.com/docs/en/channels.md) lets an outside system such as a chat app send messages into a session. In a plugin, a channel is one of the MCP servers plus a `channels` entry that binds to it and can prompt for its own configuration. This manifest binds a channel to a `telegram` server and asks for a bot token:
534534 
535535```json .claude-plugin/plugin.json theme={null}
536536{
from line 560
560560 
561561`server` must match a key in `mcpServers`. The per-channel `userConfig` takes the same shape as the [top-level `userConfig` key](#user-configuration).
562562 
563For what the server must implement and how users enable a channel plugin, see [Package as a plugin](/docs/en/channels-reference.md#package-as-a-plugin) in the channels reference. For the field table, see [`channels`](/docs/en/plugins/manifest-reference#channels).
563For what the server must implement and how users enable a channel plugin, see [Package as a plugin](https://code.claude.com/docs/en/channels-reference.md#package-as-a-plugin) in the channels reference. For the field table, see [`channels`](https://code.claude.com/docs/en/plugins/manifest-reference#channels).
564564 
565565### Monitors
566566 
from line 576
576576]
577577```
578578 
579The command runs in a shell, in the session's current working directory. It runs with your full user permissions and outside the [sandbox](/docs/en/sandboxing.md).
579The command runs in a shell, in the session's current working directory. It runs with your full user permissions and outside the [sandbox](https://code.claude.com/docs/en/sandboxing.md).
580580 
581581A monitor's command is limited in where it starts and what it can reference:
582582 
583* **Interactive sessions only**: plugin monitors start in an interactive session and never in non-interactive mode with the `-p` flag. They also don't start in sessions where the API provider or telemetry settings make the [Monitor tool](/docs/en/tools-reference.md#monitor-tool) unavailable
583* **Interactive sessions only**: plugin monitors start in an interactive session and never in non-interactive mode with the `-p` flag. They also don't start in sessions where the API provider or telemetry settings make the [Monitor tool](https://code.claude.com/docs/en/tools-reference.md#monitor-tool) unavailable
584584* **No user configuration**: `command` gets the [path variables](#path-variables-and-persistent-data) and `${ENV_VAR}` from the environment, but never `${user_config.*}`. A monitor that references one doesn't start, and monitor processes don't receive `CLAUDE_PLUGIN_OPTION_<KEY>` either
585585* **Disabling mid-session**: if you disable a plugin mid-session, Claude Code doesn't stop monitors that are already running. They stop when the session ends
586586 
587587The `experimental.monitors` manifest key takes the same array inline or a path to a JSON file, and is read instead of `monitors/monitors.json`.
588588 
589For the `when` trigger and the other fields, see [`monitors`](/docs/en/plugins/manifest-reference#monitors).
589For the `when` trigger and the other fields, see [`monitors`](https://code.claude.com/docs/en/plugins/manifest-reference#monitors).
590590 
591591## Ask the user for configuration values
592592 
from line 625
625625 
626626To open the same dialog at any time, the user runs `/plugin configure <plugin>@<marketplace>`.
627627 
628The VS Code extension's [Manage plugins dialog](/docs/en/vs-code.md#install-plugins) asks for unset options as a form after an install, and a gear icon on the plugin's row opens the form again with every option.
628The VS Code extension's [Manage plugins dialog](https://code.claude.com/docs/en/vs-code.md#install-plugins) asks for unset options as a form after an install, and a gear icon on the plugin's row opens the form again with every option.
629629 
630The `claude plugin install` shell command never prompts for `userConfig` values. To set values from the shell, pass each one as `--config KEY=VALUE` when you install, or pipe a JSON object to [`claude plugin configure --values-stdin`](/docs/en/plugins/cli-reference#plugin-configure) afterward.
630The `claude plugin install` shell command never prompts for `userConfig` values. To set values from the shell, pass each one as `--config KEY=VALUE` when you install, or pipe a JSON object to [`claude plugin configure --values-stdin`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) afterward.
631631 
632When options remain unset, `claude plugin install` prints a `userConfig options not yet set` line. For the line's exact text, see [The `userConfig` dialog never appears](/docs/en/plugins/troubleshooting#the-userconfig-dialog-never-appears).
632When options remain unset, `claude plugin install` prints a `userConfig options not yet set` line. For the line's exact text, see [The `userConfig` dialog never appears](https://code.claude.com/docs/en/plugins/troubleshooting#the-userconfig-dialog-never-appears).
633633 
634For the option fields, where each value is stored, how a component references a saved value, and which fields reject `${user_config.*}`, see [User configuration](/docs/en/plugins/manifest-reference.md#user-configuration).
634For the option fields, where each value is stored, how a component references a saved value, and which fields reject `${user_config.*}`, see [User configuration](https://code.claude.com/docs/en/plugins/manifest-reference.md#user-configuration).
635635 
636636## Reference plugin paths and store data
637637 
638638You don't know where your plugin will be installed, so refer to its files and data through these variables rather than fixed paths. They're substituted in skill, command, and agent content, in hook and monitor commands, and in MCP and LSP server configurations. They're also exported to hook, MCP, and LSP processes:
639639 
640* **`${CLAUDE_PLUGIN_ROOT}`**: the plugin's install directory. Each version has its own [cache directory](/docs/en/plugins/loading.md#find-plugins-on-disk), so the path changes when the plugin updates. Don't write state there
640* **`${CLAUDE_PLUGIN_ROOT}`**: the plugin's install directory. Each version has its own [cache directory](https://code.claude.com/docs/en/plugins/loading.md#find-plugins-on-disk), so the path changes when the plugin updates. Don't write state there
641641* **`${CLAUDE_PLUGIN_DATA}`**: a directory that survives updates, for `node_modules`, virtual environments, and caches. It resolves to `~/.claude/plugins/data/<id>/` and is created when first referenced
642642* **`${CLAUDE_PROJECT_DIR}`**: the project root, the same value hooks receive
643643 
from line 647
647647 
648648### Install dependencies into the data directory
649649 
650For a marketplace-installed plugin, Claude Code installs eligible [Node.js package dependencies](/docs/en/plugins/loading.md#node-js-package-dependencies) automatically when it caches the plugin, so you may not need to install them yourself. When you do, this `SessionStart` hook installs `node_modules` into `${CLAUDE_PLUGIN_DATA}` on first run and again after an update changes `package.json`:
650For a marketplace-installed plugin, Claude Code installs eligible [Node.js package dependencies](https://code.claude.com/docs/en/plugins/loading.md#node-js-package-dependencies) automatically when it caches the plugin, so you may not need to install them yourself. When you do, this `SessionStart` hook installs `node_modules` into `${CLAUDE_PLUGIN_DATA}` on first run and again after an update changes `package.json`:
651651 
652652```json hooks/hooks.json theme={null}
653653{
from line 666
666666}
667667```
668668 
669After the first session, `~/.claude/plugins/data/<id>/node_modules` exists. An MCP server can then set `NODE_PATH` to `${CLAUDE_PLUGIN_DATA}/node_modules` in its `env`. For which fields substitute which variable, see [Environment variables](/docs/en/plugins/manifest-reference.md#environment-variables).
669After the first session, `~/.claude/plugins/data/<id>/node_modules` exists. An MCP server can then set `NODE_PATH` to `${CLAUDE_PLUGIN_DATA}/node_modules` in its `env`. For which fields substitute which variable, see [Environment variables](https://code.claude.com/docs/en/plugins/manifest-reference.md#environment-variables).
670670 
671671## Next steps
672672 
673* [Plugin manifest reference](/docs/en/plugins/manifest-reference.md): `plugin.json` fields, path rules, and the standard layout
674* [Test plugins with evals](/docs/en/plugin-evals.md): check that the components you added change Claude's behavior the way you intend
675* [Publish and distribute a plugin](/docs/en/plugins/publish.md): version the plugin and put it in a marketplace
676* [Troubleshoot plugins](/docs/en/plugins/troubleshooting.md): what to do when a component doesn't load or a hook doesn't fire
673* [Plugin manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference.md): `plugin.json` fields, path rules, and the standard layout
674* [Test plugins with evals](https://code.claude.com/docs/en/plugin-evals.md): check that the components you added change Claude's behavior the way you intend
675* [Publish and distribute a plugin](https://code.claude.com/docs/en/plugins/publish.md): version the plugin and put it in a marketplace
676* [Troubleshoot plugins](https://code.claude.com/docs/en/plugins/troubleshooting.md): what to do when a component doesn't load or a hook doesn't fire
677677 
Feedback