plugin¶
A plugin is a native extension of a harness. It can add skills,
agents, commands, hooks, client MCP configuration, or other surfaces
where that harness documents them.
A plugin answers the question: which package extends the harness itself?
It does not answer "which single MCP server is connected?"
(mcp), "which workflow should the agent follow?"
(skill), or "which catalog of plugins is this shipped
through?" (that is marketplace packaging, not a component kind).
A plugin is not a marketplace
marketplace is native packaging: a collection or ledger a harness
uses to distribute plugins. It is not one of the closed component
kinds. Grok plugins/marketplaces is a service container, not a
plugin, and discovery does not return it as a candidate.
A directory under plugins/ becomes a plugin only through an exact
manifest from the closed set. JSON manifest values are not read
— the file's existence proves that the directory is a plugin.
| Object | What it is | Component kind? |
|---|---|---|
Pack with .claude-plugin/plugin.json |
plugin | yes, plugin |
Pack with .codex-plugin/plugin.json |
plugin | yes, plugin |
Pack with .cursor-plugin/plugin.json |
plugin | yes, plugin |
Pack with plugin.json |
plugin | yes, plugin |
Marketplace / plugins/marketplaces |
packaging / service container | no |
Inside a proven plugin, nested members keep their own kinds (skill,
agent, command, hook, instruction, mcp). The pack is the
plugin; the members are not relabelled as plugins.
Neighbours¶
| Kind | The main difference |
|---|---|
skill |
a skill extends the agent's working behaviour; a plugin extends the harness |
mcp |
an MCP server is mcp with harness_id=null; plugin .mcp.json is client config, still kind mcp |
instruction |
Cursor plugin rules/ files are instructions, not the plugin itself |
hook |
a plugin may carry hooks/hooks.json; that member is kind hook |
command |
a plugin may carry commands/; each file is kind command |
agent |
a plugin may carry agents/; each file is kind agent |
setting |
a setting holds parameters; a plugin is a package |
Choose plugin when you are shipping a harness package. Choose mcp
when you are shipping a server. Choose skill when you only need a
workflow.
Recommended package structure¶
--language for a plugin is one of python, typescript,
javascript, rust, go, or dart-flutter. OpenCode and Pi plugins
are a single JS/TS module, not an invented manifest: --language
must be javascript or typescript for those two harnesses.
Manifest-directory plugins (Claude Code, Codex, Cursor, and portable
plugin.json):
review-pack/
├── .claude-plugin/
│ └── plugin.json # or .codex-plugin / .cursor-plugin / plugin.json
├── skills/ # optional; each child with SKILL.md is a skill
├── agents/ # Claude Code / Cursor, when present
├── commands/ # Claude Code / Cursor, when present
├── hooks/
│ └── hooks.json # Claude Code / Codex, when present
└── .mcp.json # Claude Code client config; not a server
Cursor inside a proven pack: skills, agents, commands, and
rules (each file is an instruction). The official schema also names
hooks and mcpServers; the walker does not invent those types from an
adjacent directory.
When you start from ai_stp, start the author intent. Do not type component scaffold plan. The authoring directory is
wider than the published package: discover / adopt transfer source/
when portable and projections/<harness>/ when a harness was selected,
not the whole tree.
review-pack/ # component-scaffold/6
├── .ai-stp-template.json
├── .gitignore
├── README.md
├── component-passport.json
├── eval-profile.json
└── source/
├── plugin.json
└── skills/
└── README.md
Everyday:
Expert recovery (already-held digest):
ai-stp component scaffold plan \
--type plugin \
--language python \
--harness portable \
--name review-pack \
--output ./review-pack \
--json
ai-stp component scaffold apply \
--type plugin \
--language python \
--harness portable \
--name review-pack \
--output ./review-pack \
--expected-plan-digest <digest> \
--json
For OpenCode or Pi, scaffold a single {name}.js or {name}.ts under
source/ (and projections/<harness>/). Do not invent a plugin.json
those products do not use.
Adoption of a directory requires a closed-set manifest. The plugin names
in that set are plugin.json, .claude-plugin/plugin.json,
.codex-plugin/plugin.json, and .cursor-plugin/plugin.json. A
plugins/ directory whose members carry no manifest from any
supported harness produces no components and reports
unsupported_manifest once per collection.
There is no ai-stp component plugin validate. Structural readiness is
component passport validate. Kind-specific specification checking
exists only for skill.
Standards and frameworks¶
- Claude Code plugins (verified):
Create plugins. Discovery
layout_sourcefor that pack iscode.claude.com/docs/en/plugins. - Codex and Cursor packs: cite
layout_sourceon the finding (learn.chatgpt.com/docs/build-plugins,cursor.com/docs/reference/plugins). Do not invent a docs URL. - Nested skills still follow the Agent Skills Specification.
- Nested MCP client config follows MCP
as a protocol; the
.mcp.jsonfile is still not a server.
NVIDIA SkillSpector and Cisco Skill Scanner are skill scanners. They do not validate a plugin package as a whole.
Native layouts per harness¶
Discovery only reports layouts that are declared. Exact paths on a
machine come from the author intent envelope. Each finding
carries layout_source. If classification is uncertain, show that
field; do not guess a neighbour's path.
From the discovery matrix:
| Harness | Global | Project | Notes that are in the discovery contract |
|---|---|---|---|
| Claude Code | yes | yes | proven only by exact .claude-plugin/plugin.json; inside: skills, agents, commands, hooks/hooks.json, .mcp.json |
| Codex | plugin root | plugin root | proven by .codex-plugin/plugin.json; inside: skills, hooks/hooks.json |
| Pi | yes | yes | bounded native plugin/extension directory; no separate project-plugin manifest declared |
| OpenCode | yes | yes | bounded native plugin directory; single JS/TS module |
| Grok Build | yes | yes | bounded native plugin directory; plugins/marketplaces is not a plugin |
| Cursor | yes | yes | proven by .cursor-plugin/plugin.json; inside: skills, agents, commands, rules |
| Antigravity | yes | yes | bounded native plugin directory |
undefined |
portable conventions | portable conventions | not a harness; automatic install is not considered safe |
A pack for one harness does not trigger a complaint from another: a Codex pack remains a pack even without a Claude manifest.
Under skills/, a directory with SKILL.md is a skill; a directory
with .claude-plugin/plugin.json or plugin.json is a plugin.
Discovery tells them apart by the manifest, not by the parent folder
name.
Expert recovery — everyday authoring is the author intent:
Do not type component discover. The engine finds the native tree.
Versions are X.Y, not SemVer¶
A published plugin version is immutable and has the form X.Y. There is
no patch number. Changing the manifest, a nested member, or the entry
module is a new version. Updating a plugin inside a setup is a new setup
version.
Vendor plugin manifests may contain their own version strings. Those
strings are not ai_stp versions. ai_stp still mints immutable X.Y.
ai-stp component version list --id <stable_id> --json
ai-stp component version release --id <stable_id> --json
--major opens the next major line. A major line is a separate access
boundary.
What ai_stp checks¶
The catalog percent and the required-versus-optional split are explained on Security checks. For a plugin, expect at least:
- structure, digest, license, tags, source repository;
- bounded unpack and path denylist;
- secret scanning (
secrets_heuristic, and Gitleaks when enabled); - prompt-injection and hidden-content rules;
- language SAST and SCA when scripts and lockfiles are present;
- nested members of other kinds, when present, are covered by the families on Security checks for those kinds.
A passed scan reduces known risk. It is not a guarantee that the plugin is harmless. Required checks that fail or cannot run block publication.
Before install, also look at:
| Check | Why it matters |
|---|---|
| Exact manifest | no manifest means it is not a plugin |
| Nested members | skills, hooks, and .mcp.json change behaviour after install |
| Provenance | github/exact is not platform verification or plugin safety |
| Who is the author | a verified author does not make the plugin automatically safe |
Which X.Y is pinned |
updating a plugin makes a new version of the setup |
| Trust line | experimental needs explicit consent |
author_verified and component_verified are independent. Neither is a
safety guarantee.
Related CLI commands¶
Only commands that exist. Flags always from the CLI pages, and always
--json. The executable is ai-stp (package ai-stp-cli). There is no
component inspect and no setup show. The only kind-specific validate
is ai-stp component skill validate.
This kind, specifically: there is no component plugin validate.
Use passport validation. Nested skills may still be checked with:
Author, adopt, publish:
ai-stp task start --intent author --idempotency-key author-session-01 --json
ai-stp task start --intent publish --idempotency-key publish-session-01 --json
If the path is also claimed as a skill directory:
Expert recovery:
Install:
Expert catalog inspect:
A plugin can also be an embedded member of a compose manifest. See Setups.
How a plugin moves through ai_stp¶
The author publishes the plugin from a public GitHub source, or imports it locally. The version pins an exact commit and subpath. Discovery does not run the plugin.
The catalog shows what it is for, the supported harnesses, the constraints, the author's trusted status and the component's own independent status.
The compiler checks that the plugin can be built into the chosen setup and that its file structure suits the provider's projection.
The provider installs the native package only after a plan, a digest and a confirmation. Rollback must return the target as far as that harness's provider allows.
Red flags¶
- A
plugins/directory with no supported manifest, treated as a plugin. Discovery reportsunsupported_manifestonce; an empty inventory without that diagnostic would be worse. - Labelling a marketplace, or Grok
plugins/marketplaces, as kindplugin. - Putting
commands/,agents/,skills/, orhooks/inside.claude-plugin/(onlyplugin.jsonbelongs there). - A directory under
skills/that is actually a plugin, labelled as a skill. - Opening
.mcp.jsonto copy tokens into a passport. - Live tokens, private keys, or
.envbodies in the package. experimentaltrust line withoutconsent allow.- Harness not in the component's compatibility list.
- "Latest" or a branch name instead of an exact
X.Yand commit. - Treating
author_verifiedascomponent_verified. - Treating
github/exactas proof the plugin is safe.
Can a plugin be used without publishing it
Yes. Your own, imported or exactly pinned plugin can be used after
local checks. It does not thereby become platform-verified, and it
must be shown as exactly what it is: a local or pinned object
(local_owner_or_pinned). Supply-chain and post-install behaviour
still need a plan.
Author checklist¶
- Scaffold with
--type pluginand a real--language. For OpenCode or Pi usejavascriptortypescriptand a single module. - Prove the pack with the exact manifest for that harness. Do not invent a marketplace kind.
- Put nested members at the plugin root (
skills/,agents/,commands/,hooks/hooks.json,.mcp.json, Cursorrules/) only when that harness's proven pack actually reads them. - Declare post-install behaviour in the passport. No secrets.
- Register the directory through
ai-stp task start --intent author --idempotency-key author-session-01 --json. Expertcomponent discover/component adoptremain for recovery. - Pin an exact public GitHub commit and subpath.
component passport validate→component version releaseto mint immutableX.Y.- Publish through the publication path. In a
setup, pin that
X.Y.
Related: Authoring,
Components, mcp, skill.