← Back to daily report

plugins-reference.md

Changed on 2026-10-02 21:39:24 EST

+1 lines added
-0 lines removed
Visual Diff
> ## Documentation Index¶
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt¶
> Use this file to discover all available pages before exploring further.¶

# Plugin manifest reference¶

> Complete reference for plugin.json: every field with its type and default, accepted path forms, and the userConfig and environment variable schemas.¶

A plugin manifest is the `plugin.json` file in a plugin's `.claude-plugin/` directory. It carries the plugin's metadata and the [`userConfig`](#user-configuration) values that Claude Code prompts the user for. It also declares any component that you define inline or keep outside its [default location](#standard-layout).¶

This reference is for plugin creators, and for marketplace owners who put component fields in a marketplace entry.¶

<Note>¶
These cases are covered on other pages:¶

* **Learning to build a plugin**: start with [Create a plugin](/docs/en/plugins/create)¶
* **What each component does at runtime**: see [Plugin components](/docs/en/plugins/components)¶
</Note>¶

Start at the section that matches what you're looking up:¶

* A field: the [Fields table](#fields) gives each field's type, whether it's required, its default, and what it accepts. [Path rules](#path-rules) covers the `./` prefix and containment for every component path¶
* A `userConfig` option or a `channels` entry: the [User configuration](#user-configuration) and [Channels](#channels) schemas¶
* `${CLAUDE_PLUGIN_ROOT}` or another variable a plugin can reference: [Environment variables](#environment-variables)¶
* Where each component's files go: [Standard layout](#standard-layout)¶
* A message from `claude plugin validate`: the [troubleshooting page](/docs/en/plugins/troubleshooting) lists each message with its fix and links to the relevant sections on this page¶

## Manifest file¶

The manifest is optional. Without it, Claude Code loads the components it finds in the [standard layout](#standard-layout). The plugin name then comes from the marketplace entry, or from the directory name when you load the plugin with `--plugin-dir`.¶

Write a manifest when you want metadata, a component outside its default directory, `userConfig`, or an inline component definition.¶

Save the manifest at `.claude-plugin/plugin.json` under the plugin root. Put every other plugin file at the plugin root, not inside `.claude-plugin/`. That includes `skills/`, `commands/`, and `hooks/`.¶

The following example sets most of the keys in the [Fields table](#fields). It passes validation in a plugin directory that contains each referenced path.¶

```json theme={null}¶
{¶
"name": "deploy-tools",¶
"displayName": "Deploy Tools",¶
"version": "1.2.0",¶
"description": "Deployment commands, a review agent, and a status monitor",¶
"author": {¶
"name": "Example Team",¶
"email": "dev@example.com",¶
"url": "https://example.com"¶
},¶
"homepage": "https://example.com/docs/deploy-tools",¶
"repository": "https://github.com/example/deploy-tools",¶
"license": "MIT",¶
"keywords": ["deployment", "ci"],¶
"defaultEnabled": true,¶
"dependencies": ["secrets-vault"],¶
"metadata": { "catalogId": "cat-123" },¶
"skills": ["./extra-skills/"],¶
"commands": {¶
"status": {¶
"source": "./commands/status.md",¶
"description": "Show the current deployment status"¶
},¶
"about": {¶
"content": "Explain what the deploy-tools plugin provides.",¶
"description": "Describe this plugin"¶
}¶
},¶
"agents": ["./agents/reviewer.md"],¶
"hooks": "./config/extra-hooks.json",¶
"mcpServers": {¶
"deploy-api": {¶
"command": "node",¶
"args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]¶
}¶
},¶
"lspServers": "./.lsp.json",¶
"outputStyles": "./styles/",¶
"experimental": {¶
"themes": "./themes/",¶
"monitors": "./config/monitors.json"¶
},¶
"userConfig": {¶
"api_token": {¶
"type": "string",¶
"title": "API token",¶
"description": "Token for the deployment API",¶
"sensitive": true¶
}¶
}¶
}¶
```¶

### Unrecognized fields¶

An unrecognized top-level key is stripped, and an unrecognized key inside a `userConfig` option, `channels` entry, `lspServers` config, or `monitors` entry is rejected:¶

* **Top-level fields**: the field is stripped and the plugin loads. `claude plugin validate` reports each unrecognized top-level field as a warning¶
* **Strict objects**: `userConfig` options, `channels` entries, `lspServers` configs, and `monitors` entries are strict. An unknown key inside one is an error, and the plugin doesn't load¶

### Validate the manifest¶

`claude plugin validate` is the authoritative check for a manifest. Run it from your shell against the plugin directory:¶

```bash theme={null}¶
claude plugin validate ./my-plugin¶
```¶

The command reports one of these results:¶

* **`Validation passed`**: the manifest loads¶
* **`Validation passed with warnings`**: the manifest loads, but the validator found something to fix, such as an unknown top-level field that Claude Code strips, a `name` that isn't kebab-case, or a missing `version`, `description`, or `author`. Pass `--strict` to turn warnings into failures in CI¶
* **`Validation failed`**: the manifest has a type mismatch, a path that is missing or escapes the plugin root, or an unknown key inside a `userConfig` option, `channels` entry, `lspServers` config, or `monitors` entry. Claude Code reports the same problem when it loads the plugin¶

The command also checks each MCP server entry the plugin declares in `.mcp.json`, in a `.json` file that [`mcpServers`](#mcpservers) names, or inline in `plugin.json`. These MCP checks require Claude Code v2.1.281 or later and include:¶

* **Errors**: an entry Claude Code would drop when it loads the plugin, a `${user_config.KEY}` reference to an option the manifest doesn't declare, and a remote `url` that isn't a valid absolute URL¶
* **Warnings**: an `http://` or `ws://` URL to a non-loopback host, and a header value that looks like a literal credential¶

## Fields¶

The table lists the top-level keys in `plugin.json`. `name` is the only required key. Where a field name is a link, the linked section has its full rules.¶

For component keys such as `commands` and `hooks`, [Component path forms](#component-path-forms) shows each accepted shape with an example, and every path follows the [path rules](#path-rules) for the `./` prefix, extensions, and containment.¶

| Field | Type | Description |¶
| :- | :- | :- |¶
| `$schema` | String | JSON Schema URL for editor autocomplete. Claude Code ignores it at load time |¶
| [`name`](#name) | String | Plugin identifier, required. Use kebab-case. Every component is namespaced under it |¶
| [`displayName`](#displayname) | String | Name shown in UI in place of `name` |¶
| [`version`](#version) | String | Version string. Setting it keeps users on that version until you change it |¶
| `description` | String | Short explanation of what the plugin provides |¶
| `author` | Object | `name`, which is required, plus optional `email` and `url` |¶
| `homepage` | String | Documentation URL. Must parse as a URL, or the plugin fails to load |¶
| `repository` | String | Source repository URL. Not validated |¶
| `license` | String | SPDX identifier such as `MIT` or `Apache-2.0` |¶
| `keywords` | Array of strings | Discovery tags |¶
| [`metadata`](#metadata) | Object | Free-form object for your own data. Claude Code doesn't read it |¶
| [`icon`](#directory-listing-fields) | String | Icon for the plugin's listing in Anthropic's directory. Claude Code doesn't read it |¶
| [`documentationUrl`](#directory-listing-fields) | String | Documentation link for the plugin's listing in Anthropic's directory. Claude Code doesn't read it |¶
| [`supportUrl`](#directory-listing-fields) | String | Support link for the plugin's listing in Anthropic's directory. Claude Code doesn't read it |¶
| [`privacyPolicyUrl`](#directory-listing-fields) | String | Privacy policy link for the plugin's listing in Anthropic's directory. Claude Code doesn't read it |¶
| [`termsOfServiceUrl`](#directory-listing-fields) | String | Terms of service link for the plugin's listing in Anthropic's directory. Claude Code doesn't read it |¶
| [`defaultEnabled`](#defaultenabled) | Boolean | Whether the plugin starts enabled when the user hasn't set it. Defaults to `true` |¶
| [`dependencies`](#dependencies) | Array of strings or objects | Plugins that must be enabled for this one to work |¶
| [`settings`](#settings) | Object | Settings Claude Code applies while the plugin is enabled. Only `agent` and `subagentStatusLine` take effect |¶
| [`userConfig`](#user-configuration) | Object | Values Claude Code prompts the user for when the plugin is enabled |¶
| `types` | Path | A `.d.ts` file that declares the `$.state` values and `$` nouns of a [mod](/docs/en/plugins/mods/reference#files) |¶
| [`channels`](#channels) | Array of objects | Message channels the plugin provides, each bound to one of its MCP servers |¶
| `skills` | Path, or array of paths | Directories to scan for skills, each a directory of `<name>/SKILL.md` folders or one folder holding `SKILL.md` directly. `"."` names the plugin root. Adds to the default `skills/` scan |¶
| [`commands`](#commands) | Path, array of paths, or object | Flat `.md` command files, directories of them, or an object map of command name to `source` or `content`. Replaces the default `commands/` scan |¶
| `agents` | Path, or array of paths | Agent `.md` files. Directories aren't accepted. Replaces the default `agents/` scan |¶
| [`hooks`](#hooks) | Path, object, or array of either | `.json` hook files or inline hook config. Loaded together with `hooks/hooks.json` |¶
| [`mcpServers`](#mcpservers) | Path, object, or array of either | `.json` MCP config files, `.mcpb` or `.dxt` bundles, or inline server configs keyed by name. Loaded together with `.mcp.json`; a server name declared later replaces an earlier one |¶
| [`lspServers`](#lspservers) | Path, object, or array of either | `.json` LSP config files or inline server configs keyed by name. Loaded together with `.lsp.json` |¶
| `outputStyles` | Path, or array of paths | Output style files or directories. Replaces the default `output-styles/` scan |¶
| `workflows` | Path, or array of paths | [Workflow](/docs/en/workflows#distribute-a-workflow-in-a-plugin) `.js` files or directories. Replaces the default `workflows/` scan |¶
| `experimental` | Object | Container for `themes`, `monitors`, and `evals`, whose manifest shape may still change |¶
| `experimental.themes` | Path, or array of paths | Theme files or directories. Replaces the default `themes/` scan. A top-level `themes` key still loads, with a `claude plugin validate` warning |¶
| [`experimental.monitors`](#monitors) | Path, or inline array | A `.json` file holding the monitors array, or the array itself. Defaults to `monitors/monitors.json`. A top-level `monitors` key still loads, with a `claude plugin validate` warning. Monitors run only in interactive sessions, and not on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry |¶
| `experimental.evals` | Path, or array of paths | Directory that holds the plugin's [eval cases](/docs/en/plugin-evals#use-a-different-eval-directory) when it isn't the default `evals/`. `claude plugin eval --eval-dir` overrides it |¶

In the Type column, a path is a string relative to the plugin root, such as `"./custom/commands"`.¶

### `name`¶

The plugin identifier. It must be non-empty, with no spaces, `@`, `:`, path separators, control characters, or bidirectional-formatting characters; use kebab-case.¶

Claude Code namespaces every component under it, so an agent `reviewer` in plugin `deploy-tools` appears as `deploy-tools:reviewer`.¶

`claude plugin validate` also checks that the name doesn't pass as one of Anthropic's own plugins. The check ignores case and treats any run of separators as one:¶

| Name | Result |¶
| :- | :- |¶
| Starts with `claude-`, `anthropic-`, `anthropics-`, or `cc-plugin-` | Error |¶
| Is `claude`, `anthropic`, `anthropics`, `claude-code`, or `claude-mods` | Error |¶
| Puts `official` beside `claude` or `anthropic`, such as `official-claude-tools` | Error |¶
| Has `claude`, `anthropic`, or `anthropics` as a whole word anywhere else, such as `mcp-for-claude` | Warning |¶

The error reads `Plugin name "<name>" is reserved: it passes as one of Anthropic's own`, and the warning reads `Plugin name "<name>" reads as one of Anthropic's own`. `claude plugin init` and `claude plugin tag` refuse a name that draws the error. Only these commands check the name. Claude Code still installs and loads a plugin whose name they refuse.¶

### `displayName`¶

The name shown in UI in place of `name`. It may contain spaces and any casing, and it isn't used for namespacing or lookup.¶

For a marketplace-installed plugin, a `displayName` on the [marketplace entry](/docs/en/plugins/marketplace-reference#plugin-entries) takes precedence over this value.¶

### `version`¶

A version string, not checked against semver. Setting it pins the plugin to that version until you change it; see [Versions and updates](/docs/en/plugins/loading#versions-and-updates). A plugin with a [`command` source](/docs/en/plugins/marketplace-reference), a plugin from a [marketplace hosted on claude.ai](/docs/en/plugins/install#add-from-claude-ai), and a plugin [loaded in place](/docs/en/plugins/loading#find-plugins-on-disk) from a marketplace added as a local directory aren't pinned by this field.¶

### `metadata`¶

A free-form object for your own data, such as catalog or entitlement fields. Claude Code doesn't read it. Requires Claude Code v2.1.222 or later.¶

### Directory listing fields¶

Anthropic's directory reads the `icon`, `documentationUrl`, `supportUrl`, `privacyPolicyUrl`, and `termsOfServiceUrl` fields from `plugin.json` for your plugin's listing when you [submit the plugin](/docs/en/plugins/publish#submit-to-anthropics-directory). Claude Code ignores them at load time. Set them only in `plugin.json`. In a [marketplace entry](#marketplace-entries-and-the-manifest), `claude plugin validate` reports each one as an unknown field.¶

Set `icon` to the path of an image file inside the plugin, such as `./logo.png`, and each of the four URL fields to an `https://` URL.¶

`claude plugin validate` accepts these fields without a warning on Claude Code v2.1.281 or later. Earlier versions print an `Unknown field` warning for each one, so a `--strict` run fails on those versions.¶

### `defaultEnabled`¶

Whether the plugin starts enabled when the user hasn't set it in [`enabledPlugins`](/docs/en/settings-reference#enabledplugins). Defaults to `true`. A plugin that an enabled plugin depends on starts enabled regardless. The same field in the marketplace entry overrides this one.¶

Once a user's `enabledPlugins` entry is written, it persists across plugin updates, so changing `defaultEnabled` in a later release doesn't change the setting for an existing user.¶

### `dependencies`¶

Plugins that must be enabled for this one to work. Each entry is `"name"`, `"name@marketplace"`, or `{ "name": "...", "marketplace": "...", "version": "..." }`. Bare names resolve against this plugin's own marketplace. See [dependency constraints](/docs/en/plugins/dependencies).¶

### `settings`¶

Settings Claude Code applies while the plugin is enabled. Only `agent` and `subagentStatusLine` take effect; other keys are dropped at load. A `settings.json` at the plugin root takes precedence over this key. See [Default settings](/docs/en/plugins/components#default-settings).¶

## Component path forms¶

Every component key accepts a path relative to the plugin root. `hooks`, `mcpServers`, `lspServers`, and `experimental.monitors` also accept inline configuration, `commands` also accepts an object map, and `mcpServers` also accepts MCP bundle paths and URLs. The examples that follow show each accepted shape once. For what each component does at runtime, see [Plugin components](/docs/en/plugins/components).¶

### Path-only fields¶

`agents`, `skills`, `outputStyles`, `workflows`, and `experimental.themes` take one path or an array of paths. `agents` entries must be `.md` files, and `skills` entries must be directories. The other three accept a directory or a file.¶

```json theme={null}¶
{¶
"agents": ["./custom-agents/reviewer.md", "./custom-agents/tester.md"],¶
"skills": ["./extra-skills/", "."],¶
"outputStyles": "./styles/"¶
}¶
```¶

### `commands`¶

`commands` takes a path, an array of paths, or an object map. A path names a flat `.md` command file or a directory. In the object map, each key becomes the command name after the plugin prefix. For example, `"about"` in plugin `deploy-tools` runs as `/deploy-tools:about`.¶

Each value sets exactly one of `source` or `content`, and an entry that sets both or neither fails validation. The other fields in this table are optional:¶

| Field | Type | Description |¶
| :- | :- | :- |¶
| `source` | string | Path to the command's Markdown file, relative to the plugin root |¶
| `content` | string | Inline Markdown for the command body, instead of `source` |¶
| `description` | string | Description shown for the command |¶
| `argumentHint` | string | Argument hint shown after the command name, such as `[file]` |¶
| `model` | string | Default model for the command |¶
| `allowedTools` | array of strings | Tools the command may use without prompting |¶

This map declares one command from a file and one from inline content:¶

```json theme={null}¶
{¶
"commands": {¶
"status": { "source": "./commands/status.md", "argumentHint": "[env]" },¶
"about": { "content": "Explain what this plugin provides." }¶
}¶
}¶
```¶

### `hooks`¶

`hooks` takes a `.json` file path, an inline hooks object in the same shape as [`hooks` in `settings.json`](/docs/en/hooks#configuration), or an array mixing both. For hook events and handler fields, see the [hooks reference](/docs/en/hooks#hook-events).¶

A hooks file wraps the event map in a top-level `"hooks"` key, the shape [`hooks/hooks.json`](/docs/en/plugins/components#hooks) uses. A file that contains only the event map, without that wrapper, fails to load. An inline object is the event map itself, with no wrapper.¶

Claude Code merges whatever you declare with `hooks/hooks.json` when that file exists. This array loads one hooks file and declares one inline `PostToolUse` hook:¶

```json theme={null}¶
{¶
"hooks": [¶
"./config/extra-hooks.json",¶
{¶
"PostToolUse": [¶
{¶
"matcher": "Write|Edit",¶
"hooks": [¶
{ "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.sh" }¶
]¶
}¶
]¶
}¶
]¶
}¶
```¶

The file that array names carries the `"hooks"` wrapper around its own event map:¶

```json config/extra-hooks.json theme={null}¶
{¶
"hooks": {¶
"PreToolUse": [¶
{¶
"matcher": "Bash",¶
"hooks": [¶
{ "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/check-command.sh" }¶
]¶
}¶
]¶
}¶
}¶
```¶

### `mcpServers`¶

`mcpServers` takes a `.json` file path, an MCP bundle path or URL, an inline map, or an array mixing them. For server config fields, see [plugin-provided MCP servers](/docs/en/mcp#plugin-provided-mcp-servers).¶

Claude Code loads `.mcp.json` at the plugin root first, then each declared shape in order. A server name declared later replaces an earlier one.¶

An `mcpServers` value takes one of these shapes:¶

| Shape | Example value | What Claude Code does |¶
| :- | :- | :- |¶
| `.json` file path | `"./mcp/servers.json"` | Reads the file as an `mcpServers` map |¶
| MCP bundle path | `"./bundle.mcpb"` | Extracts the `.mcpb` or `.dxt` bundle into `.mcpb-cache/` under the plugin root and reads its server config |¶
| MCP bundle URL | `"https://example.com/server.mcpb"` | Downloads the bundle into `.mcpb-cache/`, then reads it |¶
| Inline map | `{ "deploy-api": { "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"] } }` | Uses the map as server configs keyed by name |¶

A bundle path or URL must end in `.mcpb` or `.dxt`. Any other extension fails validation.¶

### `lspServers`¶

`lspServers` takes a `.json` file path, an inline map of server name to config, or an array of either.¶

Claude Code loads `.lsp.json` at the plugin root first, then each declared config in order. A server name declared later replaces an earlier one.¶

Each server config is a strict object with these fields. An unknown key fails validation.¶

| Field | Required | Description |¶
| :- | :- | :- |¶
| `command` | Yes | Language server binary. No spaces unless the value starts with `/`; put arguments in `args` |¶
| `extensionToLanguage` | Yes | Map of file extension to LSP language ID, at least one entry. Keys start with a dot, such as `".go"` |¶
| `args` | No | Arguments passed to the server |¶
| `transport` | No | Communication transport: `stdio` (default) or `socket`. Claude Code accepts `socket` but runs every server over stdio, so the stdout protocol rules apply to all servers |¶
| `env` | No | Environment variables for the server process |¶
| `initializationOptions` | No | Options sent in the initialize request |¶
| `settings` | No | Settings sent by `workspace/didChangeConfiguration` |¶
| `workspaceFolder` | No | Workspace folder path for the server |¶
| `startupTimeout` | No | Milliseconds to wait for startup, a positive integer |¶
| `shutdownTimeout` | No | Milliseconds to wait for a graceful shutdown, a positive integer. When the timeout elapses, Claude Code terminates the server process. When unset, no timeout applies |¶
| `requestTimeout` | No | Milliseconds to wait for the server to answer a request, a positive integer. Defaults to `60000`, so a request the server never answers fails after 60 seconds. Requires v2.1.288 or later |¶
| `restartOnCrash` | No | Whether to restart the server after it crashes. Defaults to `true`. Set to `false` to leave a crashed server stopped instead of restarting it |¶
| `maxRestarts` | No | Restart attempts before giving up, zero or more |¶
| `diagnostics` | No | Whether to push diagnostics into context after edits. Defaults to `true` |¶

This inline config runs `gopls` for `.go` files:¶

```json theme={null}¶
{¶
"lspServers": {¶
"go": {¶
"command": "gopls",¶
"args": ["serve"],¶
"extensionToLanguage": { ".go": "go" }¶
}¶
}¶
}¶
```¶

For the language servers Anthropic publishes as plugins and how the servers behave at runtime, see [Code intelligence](/docs/en/plugins/code-intelligence).¶

### `monitors`¶

`experimental.monitors` takes a `.json` file path or the inline array. When you omit the key, Claude Code loads `monitors/monitors.json` if it exists.¶

Each entry is a strict object with these fields.¶

| Field | Required | Description |¶
| :- | :- | :- |¶
| `name` | Yes | Identifier unique within the plugin |¶
| `command` | Yes | Shell command Claude Code runs as a persistent background process in the session working directory |¶
| `description` | Yes | Short summary shown in the task panel and notification summaries |¶
| `when` | No | With `"always"`, the default, the monitor starts at session start and on plugin reload. With `"on-skill-invoke:<skill>"`, it starts the first time that skill runs |¶

This inline array declares one monitor that starts the first time the `deploy` skill runs:¶

```json theme={null}¶
{¶
"experimental": {¶
"monitors": [¶
{¶
"name": "deploy-status",¶
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/poll-deploy.sh",¶
"description": "Deployment status changes",¶
"when": "on-skill-invoke:deploy"¶
}¶
]¶
}¶
}¶
```¶

A monitor `command` can't reference `${user_config.*}`. See [Fields that run through a shell](#fields-that-run-through-a-shell).¶

## Path rules¶

Every component path in a manifest is relative to the plugin root and must start with `./`. A path such as `commands/foo.md` fails validation. `skills` and `mcpServers` each accept one form outside that rule:¶

* **`skills`**: also accepts `"."`. Both `"."` and `"./"` denote the plugin root. Before v2.1.221, `"."` failed manifest validation, so use `"./"` when the plugin must load on earlier versions¶
* **`mcpServers`**: also accepts an `https://` bundle URL¶

`experimental.evals` isn't a component path, so the rules in this section don't cover it, and `claude plugin eval` checks the value when it runs instead. It names a directory below the plugin root, such as `"quality/evals"`, with or without the `./` prefix. With an array, only the first entry is used. For what the value accepts and what happens with an unusable one, see [Use a different eval directory](/docs/en/plugin-evals#use-a-different-eval-directory).¶

### Containment and existence¶

Every component path must resolve inside the plugin root and must exist. `claude plugin validate` checks the paths under every component key:¶

* **Containment**: a path that resolves outside the plugin root doesn't load, and the `/plugin` **Errors** tab shows `<component> path escapes plugin directory: <path>`. A path containing `..` is the usual case, and `claude plugin validate` reports the error `Path contains ".." which could be a path traversal attempt`¶
* **Existence**: a path that doesn't exist doesn't load, and the `/plugin` **Errors** tab shows `<component> path not found: <path>`. `claude plugin validate` reports the error `Path not found`¶

For `outputStyles`, `lspServers`, `monitors`, and `themes` paths, the `claude plugin validate` check requires Claude Code v2.1.283 or later.¶

### How each key combines with its default location¶

Each component key either replaces its default location, adds to it, or merges with it:¶

* **Replaces the default**: `commands`, `agents`, `outputStyles`, `workflows`, `experimental.themes`, `experimental.monitors`. When you set `commands`, the default `commands/` directory isn't scanned. To keep the default and add more, list it explicitly: `"commands": ["./commands/", "./extras/"]`¶
* **Adds to the default**: `skills`. The `skills/` directory is still scanned, and the listed directories load alongside it¶
* **Merges**: `hooks`, `mcpServers`, `lspServers`. The default file loads first, and what the manifest declares merges into it, as described under [Component path forms](#component-path-forms)¶

If a plugin has a default folder such as `commands/` and also sets the manifest key that replaces it, Claude Code loads the manifest paths and not the folder. `claude plugin list` and the `/plugin` interface then show the warning `Default <folder>/ folder is ignored because the manifest sets "<key>"`.¶

To avoid the warning, set the key to a path inside that folder: `"commands": ["./commands/deploy.md"]` names a file in the default folder and produces no warning.¶

## User configuration¶

`userConfig` declares values Claude Code prompts the user for when the plugin is enabled, so users don't edit `settings.json` themselves.¶

Keys are identifiers made of letters, digits, and underscores, and can't start with a digit.¶

Each value is a strict object with these fields. An unknown key fails validation.¶

| Field | Required | Description |¶
| :- | :- | :- |¶
| `type` | Yes | One of `string`, `number`, `boolean`, `directory`, or `file` |¶
| `title` | Yes | Label shown in the configuration dialog |¶
| `description` | Yes | Help text shown beneath the field |¶
| `required` | No | If `true`, the configuration dialog doesn't accept an empty value |¶
| `default` | No | Value used when the user provides nothing: a string, number, boolean, or array of strings |¶
| `options` | No | For `string`, the values the field accepts, shown as a picker in `/config`. See [Limit a field to fixed options](#limit-a-field-to-fixed-options). Requires Claude Code v2.1.271 or later |¶
| `multiple` | No | For `string`, allows an array of strings |¶
| `sensitive` | No | If `true`, masks input and stores the value in secure storage instead of `settings.json` |¶
| `min` / `max` | No | Bounds for `number` |¶

Each option of each enabled plugin also appears as a row in the `/config` panel, except `sensitive` options and `multiple` lists. The `/config` rows require Claude Code v2.1.269 or later.¶

This `userConfig` declares an endpoint and a masked token:¶

```json theme={null}¶
{¶
"userConfig": {¶
"api_endpoint": {¶
"type": "string",¶
"title": "API endpoint",¶
"description": "Your team's API endpoint"¶
},¶
"api_token": {¶
"type": "string",¶
"title": "API token",¶
"description": "API authentication token",¶
"sensitive": true¶
}¶
}¶
}¶
```¶

### Limit a field to fixed options¶

Set `options` on a `userConfig` field to make users pick its value from a fixed list.¶

To limit a `tone` field to three options, list them in `options` and set `default` to one of them:¶

```json theme={null}¶
{¶
"userConfig": {¶
"tone": {¶
"type": "string",¶
"title": "Tone",¶
"description": "Voice for generated replies",¶
"options": ["neutral", "warm", "formal"],¶
"default": "neutral"¶
}¶
}¶
}¶
```¶

If you declare `options` on any field, users on Claude Code versions before v2.1.271 can't load the plugin.¶

`options` applies to a `string` field that isn't `multiple` or `sensitive`. Set `default` to one of the listed values, or set `required: true` so the user must pick one. Each option is a plain label of 1 to 64 characters, and `claude plugin validate`, which you run in your shell, reports anything else it rejects. A plugin whose `options` break these rules fails to load.¶

### Where values are stored¶

Non-sensitive values are saved under [`pluginConfigs`](/docs/en/settings-reference#pluginconfigs) in the user's `settings.json`. Sensitive values go to the platform's secure credential store instead. The [settings page](/docs/en/settings-reference#pluginconfigs) lists which settings files `pluginConfigs` is read from.¶

### Reference a saved value¶

Reference a saved value where the plugin needs it, in one of two forms:¶

* **`${user_config.KEY}`**: substituted in MCP server config, LSP server config, [exec-form](/docs/en/hooks#exec-form-and-shell-form) hook `args`, and skill and agent content. In skill and agent content, only non-sensitive values are substituted, and a sensitive value there becomes a placeholder¶
* **`CLAUDE_PLUGIN_OPTION_<KEY>`**: exported to hook processes for every option, with `<KEY>` uppercased. A shell-form hook reads `$CLAUDE_PLUGIN_OPTION_API_TOKEN` for `api_token`¶

### Fields that run through a shell¶

Shell-form hook commands, monitor commands, and MCP [`headersHelper`](/docs/en/mcp#use-dynamic-headers-for-custom-authentication) reject `${user_config.*}`. A component that references it in one of these fields fails with an [error](/docs/en/errors#plugin-command-references-user-config) instead of running, because the field's value is passed to a shell that would re-parse the substituted value.¶

The table shows how the value can reach each of these fields instead.¶

| Field | How the value can reach it |¶
| :- | :- |¶
| Shell-form hook commands | Use [exec form](/docs/en/hooks#exec-form-and-shell-form) with `args`, or read `CLAUDE_PLUGIN_OPTION_<KEY>` from the hook's environment |¶
| Monitor commands | Not through Claude Code. Monitor processes don't receive `CLAUDE_PLUGIN_OPTION_<KEY>`, so the monitor script has to obtain the value on its own |¶
| MCP `headersHelper` | Not through Claude Code. The helper's environment carries `CLAUDE_PLUGIN_ROOT`, `CLAUDE_CODE_MCP_SERVER_NAME`, and `CLAUDE_CODE_MCP_SERVER_URL` but no option values, so the helper script has to obtain the value on its own |¶

## Channels¶

`channels` declares the message channels a plugin provides, such as a bridge to a chat app. When you declare one, Claude Code can prompt for the channel's configuration when the plugin is enabled. For how the server injects messages, see the [channels reference](/docs/en/channels-reference#package-as-a-plugin).¶

Each entry is a strict object bound to one of the plugin's MCP servers, with these fields:¶

| Field | Required | Description |¶
| :- | :- | :- |¶
| `server` | Yes | Key of the MCP server in this plugin's `mcpServers` that the channel binds to |¶
| `displayName` | No | Name shown in the configuration dialog title. Defaults to the server name |¶
| `userConfig` | No | Options to prompt for, in the same shape as [top-level `userConfig`](#user-configuration). Saved values substitute into `${user_config.KEY}` references in the server's `env` |¶

This manifest binds a channel to the plugin's `telegram` MCP server and prompts for a bot token that substitutes into the server's `env`:¶

```json theme={null}¶
{¶
"mcpServers": {¶
"telegram": {¶
"command": "node",¶
"args": ["${CLAUDE_PLUGIN_ROOT}/server.js"],¶
"env": { "BOT_TOKEN": "${user_config.bot_token}" }¶
}¶
},¶
"channels": [¶
{¶
"server": "telegram",¶
"displayName": "Telegram",¶
"userConfig": {¶
"bot_token": {¶
"type": "string",¶
"title": "Bot token",¶
"description": "Telegram bot token",¶
"sensitive": true¶
}¶
}¶
}¶
]¶
}¶
```¶

## Environment variables¶

Claude Code provides three path variables to plugin components. Reference them as `${NAME}` in the fields listed under [Where each variable resolves](#where-each-variable-resolves), and read them as environment variables in the processes that receive them.¶

| Variable | Resolves to | Use it for |¶
| :- | :- | :- |¶
| `${CLAUDE_PLUGIN_ROOT}` | Absolute path of the plugin's installed version | Scripts, binaries, and config files bundled with the plugin |¶
| `${CLAUDE_PLUGIN_DATA}` | `~/.claude/plugins/data/<id>/`, created on first reference and kept across plugin updates. `<id>` is the plugin identifier with every character other than a letter, digit, `_`, or `-` replaced by `-` | Installed dependencies such as `node_modules`, generated code, and caches |¶
| `${CLAUDE_PROJECT_DIR}` | The project root | Project-local scripts and config files |¶

`${CLAUDE_PLUGIN_ROOT}` changes when the plugin updates, so don't write state there. For where the root moves and when the old directory is cleaned up, see the [loading page](/docs/en/plugins/loading).¶

By default, Claude Code deletes the `${CLAUDE_PLUGIN_DATA}` directory when you uninstall the plugin from the last place it's installed. For `--keep-data` and the other cases where it stays, see [plugin uninstall](/docs/en/plugins/cli-reference#plugin-uninstall).¶

### Where each variable resolves¶

In each plugin component, `${...}` references resolve inline in specific fields, and some components also receive the variables in their process environment:¶

| Plugin component | Fields where `${...}` resolves | Exported to the process |¶
| :- | :- | :- |¶
| Hook commands | Anywhere in `command` and `args` | `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`, `CLAUDE_PROJECT_DIR`, and `CLAUDE_PLUGIN_OPTION_<KEY>` |¶
| Monitor commands | Anywhere in `command` | Not exported |¶
| MCP `stdio` servers | `command`, `args`, `env` | `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA` |¶
| MCP `http`, `sse`, `ws` servers | `url`, `headers`, `headersHelper` | Not applicable |¶
| LSP servers | `command`, `args`, `env`, `workspaceFolder` | `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`, `CLAUDE_PROJECT_DIR` |¶
| Skill, command, and agent content | Anywhere in the Markdown body | Not applicable |¶

The variables aren't present in the environment of commands Claude runs through the Bash tool, in the main session or in a subagent. In skill, command, and agent content, write the `${...}` reference in the Markdown body instead, and Claude Code substitutes the path inline when it loads the content.¶

### Quoting and path separators¶

Keep each substituted path a single argument:¶

* **Hook commands**: use [exec form](/docs/en/hooks#exec-form-and-shell-form) with `args` so each path is one argument with no quoting¶
* **Shell-form hooks and monitor commands**: wrap the variable in double quotes so a path with spaces stays one word¶

If you leave one of these variables outside quotes in a shell-form command in a hooks file, `claude plugin validate` warns about it unless the hook sets [`shell`](/docs/en/hooks#command-hook-fields) to `"powershell"`.¶

This shell-form hook runs a script bundled with the plugin:¶

```json theme={null}¶
{¶
"hooks": {¶
"PostToolUse": [¶
{¶
"hooks": [¶
{¶
"type": "command",¶
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/process.sh"¶
}¶
]¶
}¶
]¶
}¶
}¶
```¶

On Windows, the substituted paths use forward slashes so a shell doesn't read backslashes as escapes.¶

## Standard layout¶

Each component type has a default location under the plugin root, used when the manifest doesn't point elsewhere.¶

| Component | Default location | Contents |¶
| :- | :- | :- |¶
| Manifest | `.claude-plugin/plugin.json` | Plugin metadata and configuration. Optional |¶
| Skills | `skills/` | One `<name>/SKILL.md` per skill. A plugin with `SKILL.md` at its root, no `skills/`, and no `skills` key loads as a single skill |¶
| Commands | `commands/` | Flat Markdown command files. Prefer `skills/` for new plugins |¶
| Agents | `agents/` | Agent Markdown files. Subfolders are part of the [agent name](/docs/en/plugins/components#agents) |¶
| Hooks | `hooks/hooks.json` | Hook configuration |¶
| MCP servers | `.mcp.json` | MCP server definitions |¶
| LSP servers | `.lsp.json` | LSP server configurations |¶
| Output styles | `output-styles/` | Output style Markdown files |¶
| Workflows | `workflows/` | Workflow `.js` files |¶
| Themes | `themes/` | Theme JSON files |¶
| Monitors | `monitors/monitors.json` | The monitors array |¶
| Executables | `bin/` | Files here are on the Bash tool's `PATH` while the plugin is enabled, so Claude runs them as bare commands. claude.ai and Cowork don't install a plugin that has this directory, including one you [distribute through claude.ai organization settings](https://claude.com/docs/plugins/org-sync#keep-executables-out-of-the-top-level-bin-directory) |¶
| Settings | `settings.json` | `agent` and `subagentStatusLine` defaults applied while the plugin is enabled |¶

A plugin that uses every default location, plus a `scripts/` folder that its hooks call, is laid out like this:¶

```text theme={null}¶
deploy-tools/¶
├── .claude-plugin/¶
│ └── plugin.json¶
├── skills/¶
│ └── deploy/¶
│ └── SKILL.md¶
├── commands/¶
│ └── status.md¶
├── agents/¶
│ └── reviewer.md¶
├── hooks/¶
│ └── hooks.json¶
├── monitors/¶
│ └── monitors.json¶
├── output-styles/¶
│ └── terse.md¶
├── themes/¶
│ └── dracula.json¶
├── workflows/¶
│ └── release-audit.js¶
├── bin/¶
│ └── deploy-tool¶
├── scripts/¶
│ └── format.sh¶
├── settings.json¶
├── .mcp.json¶
└── .lsp.json¶
```¶

To click through this layout and read what each file does, open the [plugin explorer](/docs/en/plugins/components#explore-the-plugin-directory).¶

A `CLAUDE.md` at the plugin root isn't loaded as context, and `claude plugin validate` warns when it finds one. To include instructions that load into Claude's context, put them in a skill.¶

## Marketplace entries and the manifest¶

A [marketplace entry](/docs/en/plugins/marketplace-reference) accepts [its own fields](/docs/en/plugins/marketplace-reference#plugin-entries), including `strict`, and every field on this page except the [directory listing fields](#directory-listing-fields).¶

The `strict` field decides whether the entry may add components to a plugin that has its own `plugin.json`. It defaults to `true`.¶

### How entry fields combine with `plugin.json`¶

The entry either serves as the manifest, adds components to it, or conflicts with it:¶

* **No `plugin.json`**: the entry is the manifest, regardless of `strict`. Entry `hooks` loads only in the inline object form. For a file path or array there, the `/plugin` **Errors** tab shows a `not yet supported in a marketplace entry` error¶
* **`plugin.json` present, `strict` unset or `true`**: Claude Code loads the manifest and appends the entry's `commands`, `agents`, `skills`, `outputStyles`, and `themes` to it. For `hooks`, the entry's matchers for an event replace the manifest's matchers for that same event, and events only the manifest declares keep theirs¶
* **`plugin.json` present, `strict: false`**: an entry that declares any of `commands`, `agents`, `skills`, `hooks`, `outputStyles`, or `themes` is a conflict, and the plugin fails to load with `Plugin <name> has conflicting manifests`¶

When a [marketplace entry whose `source` is the marketplace root](/docs/en/plugins/marketplace-reference) lists specific `skills` subdirectories, only those subdirectories load, and the plugin's default `skills/` directory isn't scanned. A `skills` key in the manifest instead [adds to the default](#how-each-key-combines-with-its-default-location).¶

### Metadata precedence¶

Some metadata fields have a fixed precedence regardless of `strict`:¶

* **`defaultEnabled` and display fields**: the entry's `defaultEnabled` and its [display fields](/docs/en/plugins/marketplace-reference#entry-and-plugin-json) such as `displayName` override the manifest's¶
* **`version`**: the manifest's `version` overrides the entry's¶
* **`name`**: when the entry lists the plugin under a different `name` than the manifest, `enabledPlugins` uses the entry name, and components are namespaced under the manifest name¶

For the full precedence table, see [Strict mode](/docs/en/plugins/marketplace-reference).¶

## Next steps¶

* [Add components to a plugin](/docs/en/plugins/components): what each component does at runtime, with an example that validates¶
* [Marketplace reference](/docs/en/plugins/marketplace-reference): the entry fields a marketplace can set for your plugin¶
* [Plugin commands reference](/docs/en/plugins/cli-reference#plugin-validate): `claude plugin validate` flags and output¶
* [Troubleshoot plugins](/docs/en/plugins/troubleshooting#claude-plugin-validate-reports-errors): each validation message with its fix¶

Unified Diff

--- a/plugins-reference.md
+++ b/plugins-reference.md
@@ -335,6 +335,7 @@
 | `workspaceFolder` | No | Workspace folder path for the server |
 | `startupTimeout` | No | Milliseconds to wait for startup, a positive integer |
 | `shutdownTimeout` | No | Milliseconds to wait for a graceful shutdown, a positive integer. When the timeout elapses, Claude Code terminates the server process. When unset, no timeout applies |
+| `requestTimeout` | No | Milliseconds to wait for the server to answer a request, a positive integer. Defaults to `60000`, so a request the server never answers fails after 60 seconds. Requires v2.1.288 or later |
 | `restartOnCrash` | No | Whether to restart the server after it crashes. Defaults to `true`. Set to `false` to leave a crashed server stopped instead of restarting it |
 | `maxRestarts` | No | Restart attempts before giving up, zero or more |
 | `diagnostics` | No | Whether to push diagnostics into context after edits. Defaults to `true` |