Plugin pre-submission checklist changedplugins/pre-submission-checklist
Nearest release: v2.1.284, published 16 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 28 Sep 2026 00:41 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 28 Sep 2026 22:07 UTC.
Upstream edited
Recorded here
Lines+49added
Lines−49removed
From line
79
where the diff opens
First seen
25 Sep 2026
this site's first read of the page
Recorded edits5to this page, all time
The whole hunk
from line 79, old and new numbered
/
from line 79
7979
8080The repository and folder layout checks cover the plugin's location in the repository and what the repository as a whole contains.
8181
82| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
83| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
84| Submit a folder that contains `.claude-plugin/plugin.json` | Blocks, except that a folder with no `plugin.json` anywhere and at least one `skills/<name>/SKILL.md` passes with a note and is listed for Claude Code only | |
85| Submit one plugin at a time. In a [marketplace repository](https://code.claude.com/docs/en/plugins/create-marketplace) with several plugins, validate and submit each plugin folder on its own. | Blocks | **Pick one plugin first**, when you select **Submit for review** |
86| Keep every file that a hook, an MCP server command, or a script uses inside the plugin folder, and point every component path in `plugin.json` inside it | Blocks for a `plugin.json` path that points outside the plugin folder | |
87| Commit regular files and folders for everything the plugin loads, not symbolic links, Git submodules, or Git LFS pointer files | Blocks where the plugin loads the entry. Warning elsewhere. | |
88| Remove `.DS_Store`, `Thumbs.db`, `desktop.ini`, and `__MACOSX` entries from the plugin folder | Blocks | In validation, a message that begins "This is a macOS or Windows system file". After you submit, **Files in the repository the scanner won’t accept**. |
89| Use file and folder names that are valid on both Windows and macOS: no colon, no trailing dot or space, no Windows device name such as `con.md` or `prn`, and no two names that differ only by capitalization | Validation stops | **Couldn’t validate that repository** |
90| Name each folder on the path to the plugin with letters, digits, dots, hyphens, and underscores only, and enter the plugin path with the same capitalization as the repository | Validation stops | **Couldn’t validate that repository** |
91| Keep `export-ignore` and `export-subst` out of every `.gitattributes` file. Keep `filter`, Git LFS included, and other attributes that rewrite file contents out of `.gitattributes` files at the repository root, above the plugin folder, and inside it. | Validation stops | **Couldn’t validate that repository** |
92| Keep the repository under 50 MiB as GitHub archives it and under 256 MiB unpacked, with fewer than 10,000 files and folders, and keep every file in the plugin folder under 5 MiB | Validation stops | **Repository too large to validate** |
82| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
83| - | - | - |
84| Submit a folder that contains `.claude-plugin/plugin.json` | Blocks, except that a folder with no `plugin.json` anywhere and at least one `skills/<name>/SKILL.md` passes with a note and is listed for Claude Code only | |
85| Submit one plugin at a time. In a [marketplace repository](https://code.claude.com/docs/en/plugins/create-marketplace) with several plugins, validate and submit each plugin folder on its own. | Blocks | **Pick one plugin first**, when you select **Submit for review** |
86| Keep every file that a hook, an MCP server command, or a script uses inside the plugin folder, and point every component path in `plugin.json` inside it | Blocks for a `plugin.json` path that points outside the plugin folder | |
87| Commit regular files and folders for everything the plugin loads, not symbolic links, Git submodules, or Git LFS pointer files | Blocks where the plugin loads the entry. Warning elsewhere. | |
88| Remove `.DS_Store`, `Thumbs.db`, `desktop.ini`, and `__MACOSX` entries from the plugin folder | Blocks | In validation, a message that begins "This is a macOS or Windows system file". After you submit, **Files in the repository the scanner won’t accept**. |
89| Use file and folder names that are valid on both Windows and macOS: no colon, no trailing dot or space, no Windows device name such as `con.md` or `prn`, and no two names that differ only by capitalization | Validation stops | **Couldn’t validate that repository** |
90| Name each folder on the path to the plugin with letters, digits, dots, hyphens, and underscores only, and enter the plugin path with the same capitalization as the repository | Validation stops | **Couldn’t validate that repository** |
91| Keep `export-ignore` and `export-subst` out of every `.gitattributes` file. Keep `filter`, Git LFS included, and other attributes that rewrite file contents out of `.gitattributes` files at the repository root, above the plugin folder, and inside it. | Validation stops | **Couldn’t validate that repository** |
92| Keep the repository under 50 MiB as GitHub archives it and under 256 MiB unpacked, with fewer than 10,000 files and folders, and keep every file in the plugin folder under 5 MiB | Validation stops | **Repository too large to validate** |
9393
9494The file-name, plugin-path, and `.gitattributes` checks all produce **Couldn’t validate that repository**. The error doesn't say which cause applies, so check each of them. [Files in the plugin folder](#files-in-the-plugin-folder) has tighter file limits that hold a version for a reviewer.
9595
from line 97
9797
9898`plugin.json` is the plugin's manifest. Beyond the syntax and schema errors that `claude plugin validate` catches, the directory runs the checks in this table. Settle the name before you submit, and raise `version` with every release, as [version management](https://code.claude.com/docs/en/plugins/loading#versions-and-updates) describes.
9999
100| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
101| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
102| Use a `name` made of lowercase letters, digits, and hyphens, up to 64 characters, that starts and ends with a letter or digit | Blocks for non-ASCII characters. Warning for any other name that breaks the pattern, such as uppercase letters. | **Non-ASCII identifier** when blocked |
103| Build the name around your own distinctive product or project name: not a reserved word such as `claude`, `anthropic`, `official`, `plugin`, `mcp`, or `test` as the whole name, not a [marketplace name reserved for Anthropic](https://code.claude.com/docs/en/plugins/marketplace-reference#reserved-names), and nothing that presents the plugin as official | Blocks. Held for a reviewer for a name made only of generic words, such as `test-plugin`. | **Name is taken** when blocked. **Name may be confused with an existing listing** when held. |
104| Choose a name that no other organization's plugin uses. A name that differs only in capitalization or punctuation counts as the same name. | Blocks for the same name. Held for a reviewer for a look-alike. | **Name is taken** when blocked. **Name may be confused with an existing listing** when held. |
105| Choose a name, `displayName`, and `author.name` that can't be mistaken for an existing plugin, publisher, connector, or well-known brand that isn't yours | Held for a reviewer | **Name matches a known brand**, **Name may be confused with an existing listing**, or **Publisher name may be confused with another** for `author.name` |
106| In a fork, give the plugin a name of its own. Forks are allowed. | Held for a reviewer | **Fork uses the upstream project’s name** |
107| Write `displayName` and `author.name` in one writing system, without look-alike letters or invisible characters | Blocks | |
108| Spell the keys that declare components, such as `hooks` and `mcpServers`, exactly as the [plugins reference](https://code.claude.com/docs/en/plugins/manifest-reference) does, and keep them out of the `experimental` object | Blocks | |
109| Set `description`, `author`, and `version` | Warning | |
100| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
101| - | - | - |
102| Use a `name` made of lowercase letters, digits, and hyphens, up to 64 characters, that starts and ends with a letter or digit | Blocks for non-ASCII characters. Warning for any other name that breaks the pattern, such as uppercase letters. | **Non-ASCII identifier** when blocked |
103| Build the name around your own distinctive product or project name: not a reserved word such as `claude`, `anthropic`, `official`, `plugin`, `mcp`, or `test` as the whole name, not a [marketplace name reserved for Anthropic](https://code.claude.com/docs/en/plugins/marketplace-reference#reserved-names), and nothing that presents the plugin as official | Blocks. Held for a reviewer for a name made only of generic words, such as `test-plugin`. | **Name is taken** when blocked. **Name may be confused with an existing listing** when held. |
104| Choose a name that no other organization's plugin uses. A name that differs only in capitalization or punctuation counts as the same name. | Blocks for the same name. Held for a reviewer for a look-alike. | **Name is taken** when blocked. **Name may be confused with an existing listing** when held. |
105| Choose a name, `displayName`, and `author.name` that can't be mistaken for an existing plugin, publisher, connector, or well-known brand that isn't yours | Held for a reviewer | **Name matches a known brand**, **Name may be confused with an existing listing**, or **Publisher name may be confused with another** for `author.name` |
106| In a fork, give the plugin a name of its own. Forks are allowed. | Held for a reviewer | **Fork uses the upstream project’s name** |
107| Write `displayName` and `author.name` in one writing system, without look-alike letters or invisible characters | Blocks | |
108| Spell the keys that declare components, such as `hooks` and `mcpServers`, exactly as the [plugins reference](https://code.claude.com/docs/en/plugins/manifest-reference) does, and keep them out of the `experimental` object | Blocks | |
109| Set `description`, `author`, and `version` | Warning | |
110110
111111### README and license
112112
113113The directory shows your README as the listing's description and requires a license before it lists the plugin.
114114
115| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
116| --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------------- |
117| Put a README of at least 40 words in the plugin folder, preferably named `README.md`. Words inside code blocks don't count. | Blocks | **README missing**, **README too short** |
118| Add a `LICENSE` file to the plugin folder, or set `license` in `plugin.json` | Blocks | **License missing** |
115| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
116| - | - | - |
117| Put a README of at least 40 words in the plugin folder, preferably named `README.md`. Words inside code blocks don't count. | Blocks | **README missing**, **README too short** |
118| Add a `LICENSE` file to the plugin folder, or set `license` in `plugin.json` | Blocks | **License missing** |
119119
120120### Files in the plugin folder
121121
122122The file checks apply to every file in the plugin folder, including images and documents.
123123
124| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
125| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------- |
126| Keep every file that isn't an image or font under 256 KiB | Held for a reviewer | **Files or downloads the validator couldn’t inspect** |
127| Keep the plugin to 512 files or fewer | Held for a reviewer | **Files or downloads the validator couldn’t inspect** |
128| Include only text files, SVG included, complete PNG, JPEG, GIF, and WebP images, and font files. Any other binary file, such as an `.ico`, `.pdf`, or `.zip` file or a compiled executable, is held. | Held for a reviewer | **Files or downloads the validator couldn’t inspect** |
129| To show a bundled image in the README, use Markdown image syntax. Don't refer to bundled images or fonts from commands, hooks, or scripts, or write their paths in backticks or a code block. | Held for a reviewer | |
130| Declare each MCP server with `command` and `args` or with `url`, not a `.mcpb` or `.dxt` bundle | Held for a reviewer. Blocks for a bundle fetched from a URL. | **Bundled MCP server not inspected** when held |
124| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
125| - | - | - |
126| Keep every file that isn't an image or font under 256 KiB | Held for a reviewer | **Files or downloads the validator couldn’t inspect** |
127| Keep the plugin to 512 files or fewer | Held for a reviewer | **Files or downloads the validator couldn’t inspect** |
128| Include only text files, SVG included, complete PNG, JPEG, GIF, and WebP images, and font files. Any other binary file, such as an `.ico`, `.pdf`, or `.zip` file or a compiled executable, is held. | Held for a reviewer | **Files or downloads the validator couldn’t inspect** |
129| To show a bundled image in the README, use Markdown image syntax. Don't refer to bundled images or fonts from commands, hooks, or scripts, or write their paths in backticks or a code block. | Held for a reviewer | |
130| Declare each MCP server with `command` and `args` or with `url`, not a `.mcpb` or `.dxt` bundle | Held for a reviewer. Blocks for a bundle fetched from a URL. | **Bundled MCP server not inspected** when held |
131131
132132### Review what the plugin runs and connects to
133133
134134A package launcher is a command that downloads a package and runs it: `npx`, `bunx`, `pnpm dlx`, `yarn dlx`, `uvx`, `pipx run`, and `uv run` all count. `${CLAUDE_PLUGIN_ROOT}` is the variable that Claude Code sets to the plugin's installation directory.
135135
136| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
137| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------- |
138| Pin every package that a launcher runs to an exact version, such as `npx <package>@1.2.3` or `uvx <package>==1.2.3`, not a range or `@latest`. Run `uv run` with `--locked` or `--frozen`. | Blocks | **Unpinned npx launcher**, **Unpinned uvx launcher** |
139| In a plugin that uses a launcher or runs a package install, don't include a package-manager configuration file that sets a registry, index, proxy, or other package source, such as `.npmrc`, `bunfig.toml`, or `uv.toml` | Blocks with a launcher. Held for a reviewer with a package install. | **Install may use a custom registry or package source** when held |
140| Keep real credentials out of every file, documentation and examples included. Ask for each value through a `userConfig` entry in `plugin.json` with `sensitive: true`, and refer to it as `${user_config.KEY}`. | Blocks | **Secret in MCP headers** for a credential in an MCP server's headers |
141| Don't read a credential that is already set in the user's environment, such as `$GITHUB_TOKEN`, and send it to a server, even in a README example. Ask for it through `userConfig` instead. | Held for a reviewer. Blocks for an HTTP hook that sends the credential. | **Uses a credential from the user’s machine** when held |
142| Make `.mcp.json` valid JSON in which every server entry matches the schema in the [MCP documentation](https://code.claude.com/docs/en/mcp) | Blocks | **.mcp.json can’t be parsed** for invalid JSON |
143| Give each remote MCP server a `type` of `http`, `sse`, or `ws` and a `url` that is an absolute `https://` or `wss://` URL, a `${user_config.KEY}` reference, or `""` when the plugin has no fixed endpoint | Blocks | **MCP server URL is not https** for a URL with another scheme |
144| Start each local MCP server by running a file in the plugin with plain arguments, such as `node ${CLAUDE_PLUGIN_ROOT}/server.js`, not through a shell, an inline program such as `-c`, or a package-manager script such as `npm run` | Held for a reviewer | **MCP server command wasn’t read** |
145| In the command of a hook or an MCP server, write each path in full from `${CLAUDE_PLUGIN_ROOT}`, with no other variable, command substitution, wildcard, or inline program such as `python3 -c` | Blocks when the plugin folder is a subfolder of the repository | |
146| Keep launchers and package installs out of each script that a hook or an MCP server runs. When the plugin folder is a subfolder of the repository, also keep shell variables other than `${CLAUDE_PLUGIN_ROOT}`, command substitutions, and calls to other files in the plugin out of those scripts. | Held for a reviewer | **Scripts the validator couldn’t follow** |
136| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
137| - | - | - |
138| Pin every package that a launcher runs to an exact version, such as `npx <package>@1.2.3` or `uvx <package>==1.2.3`, not a range or `@latest`. Run `uv run` with `--locked` or `--frozen`. | Blocks | **Unpinned npx launcher**, **Unpinned uvx launcher** |
139| In a plugin that uses a launcher or runs a package install, don't include a package-manager configuration file that sets a registry, index, proxy, or other package source, such as `.npmrc`, `bunfig.toml`, or `uv.toml` | Blocks with a launcher. Held for a reviewer with a package install. | **Install may use a custom registry or package source** when held |
140| Keep real credentials out of every file, documentation and examples included. Ask for each value through a `userConfig` entry in `plugin.json` with `sensitive: true`, and refer to it as `${user_config.KEY}`. | Blocks | **Secret in MCP headers** for a credential in an MCP server's headers |
141| Don't read a credential that is already set in the user's environment, such as `$GITHUB_TOKEN`, and send it to a server, even in a README example. Ask for it through `userConfig` instead. | Held for a reviewer. Blocks for an HTTP hook that sends the credential. | **Uses a credential from the user’s machine** when held |
142| Make `.mcp.json` valid JSON in which every server entry matches the schema in the [MCP documentation](https://code.claude.com/docs/en/mcp) | Blocks | **.mcp.json can’t be parsed** for invalid JSON |
143| Give each remote MCP server a `type` of `http`, `sse`, or `ws` and a `url` that is an absolute `https://` or `wss://` URL, a `${user_config.KEY}` reference, or `""` when the plugin has no fixed endpoint | Blocks | **MCP server URL is not https** for a URL with another scheme |
144| Start each local MCP server by running a file in the plugin with plain arguments, such as `node ${CLAUDE_PLUGIN_ROOT}/server.js`, not through a shell, an inline program such as `-c`, or a package-manager script such as `npm run` | Held for a reviewer | **MCP server command wasn’t read** |
145| In the command of a hook or an MCP server, write each path in full from `${CLAUDE_PLUGIN_ROOT}`, with no other variable, command substitution, wildcard, or inline program such as `python3 -c` | Blocks when the plugin folder is a subfolder of the repository | |
146| Keep launchers and package installs out of each script that a hook or an MCP server runs. When the plugin folder is a subfolder of the repository, also keep shell variables other than `${CLAUDE_PLUGIN_ROOT}`, command substitutions, and calls to other files in the plugin out of those scripts. | Held for a reviewer | **Scripts the validator couldn’t follow** |
147147
148148### Choices a reviewer always checks
149149
from line 159
159159
160160The component checks confirm that Claude Code can load each hook, skill, command, and agent file in the plugin.
161161
162| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
163| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
164| Make `hooks/hooks.json` valid JSON with a top-level `hooks` object, only the hook events and hook types in the [hooks reference](https://code.claude.com/docs/en/hooks), and an `https://` URL on each HTTP hook | Blocks | **hooks.json is invalid** for invalid JSON |
165| Leave `hooks/hooks.json` out of the `hooks` field in `plugin.json`, because Claude Code loads that file automatically | Warning | |
166| Write valid YAML front matter in each skill, command, and agent file, with `description` as a single text value, not a list | Blocks for front matter that doesn't parse or a `description` that isn't text. Warning for no front matter or no `description`. | |
167| Name component folders and files with the exact spelling and capitalization Claude Code expects, such as `hooks/`, `skills/`, and `SKILL.md` | Blocks | |
162| What to do | [Result if you don't](#read-a-validation-result) | Title in the report, if it has one |
163| - | - | - |
164| Make `hooks/hooks.json` valid JSON with a top-level `hooks` object, only the hook events and hook types in the [hooks reference](https://code.claude.com/docs/en/hooks), and an `https://` URL on each HTTP hook | Blocks | **hooks.json is invalid** for invalid JSON |
165| Leave `hooks/hooks.json` out of the `hooks` field in `plugin.json`, because Claude Code loads that file automatically | Warning | |
166| Write valid YAML front matter in each skill, command, and agent file, with `description` as a single text value, not a list | Blocks for front matter that doesn't parse or a `description` that isn't text. Warning for no front matter or no `description`. | |
167| Name component folders and files with the exact spelling and capitalization Claude Code expects, such as `hooks/`, `skills/`, and `SKILL.md` | Blocks | |
168168
169169## Prepare for the security scan
170170
No line in this hunk matches that.