agents/openai.yaml
interface:
display_name: "AI Coding Agents — Plugins"
short_description: "Design coding-agent plugin systems"
default_prompt: "Use $ai-coding-agents-plugins to design plugin manifests, extension points, load order, or trust boundaries for a coding-agent CLI or runtime."
assets/templates/plugin.manifest.template.json
{
"$schema": "https://skills.ai-coding-agents/plugins/plugin.manifest.schema.json",
"id": "{{PLUGIN_ID}}",
"name": "{{PLUGIN_DISPLAY_NAME}}",
"version": "0.1.0",
"description": "{{ONE_LINE_DESCRIPTION}}",
"author": "{{AUTHOR_NAME_OR_ORG}}",
"license": "MIT",
"runtime": {
"min_agent_version": "1.0.0",
"max_agent_version": null,
"platform": ["claude-code", "codex", "agent-sdk"],
"languages": ["node", "python"]
},
"entry": {
"node": "dist/index.js",
"python": "src/main.py"
},
"tools": [
{
"name": "{{TOOL_NAME}}",
"description": "{{TOOL_DESCRIPTION}}",
"schema_path": "schemas/{{TOOL_NAME}}.schema.json",
"side_effects": ["file_read"],
"requires_approval": false
}
],
"commands": [
{
"name": "{{COMMAND_NAME}}",
"description": "{{COMMAND_DESCRIPTION}}",
"prompt_path": "commands/{{COMMAND_NAME}}.md",
"allowed_tools": ["{{TOOL_NAME}}"]
}
],
"permissions": {
"required": [],
"optional": ["network_outbound"]
},
"settings": {
"schema_path": "schemas/settings.schema.json",
"defaults_path": "settings.defaults.json"
},
"hooks": {
"on_install": "scripts/on_install.sh",
"on_uninstall": "scripts/on_uninstall.sh",
"on_update": null
},
"assets": {
"icon": "assets/icon.png",
"screenshots": []
},
"repository": "https://github.com/{{ORG}}/{{PLUGIN_ID}}",
"homepage": "https://{{PLUGIN_ID}}.example.com",
"bugs": "https://github.com/{{ORG}}/{{PLUGIN_ID}}/issues",
"keywords": ["{{KEYWORD_1}}", "{{KEYWORD_2}}"],
"changelog_path": "CHANGELOG.md",
"readme_path": "README.md"
}
data/sources.json
{
"metadata": {
"skill": "ai-coding-agents-plugins",
"title": "AI Coding Agents Plugins - Sources",
"description": "Official documentation and source references for coding-agent plugin architecture, plugin loading, and runtime trust boundaries",
"last_updated": "2026-07-11",
"updated": "2026-07-11",
"total_sources": 15,
"version": "1.1"
},
"categories": {
"official_documentation": [
{
"name": "Claude Code Documentation",
"url": "https://code.claude.com/docs/en",
"type": "documentation",
"relevance": "Primary product documentation for Claude Code runtime behavior and extension surfaces",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "Claude Code Plugins Reference",
"url": "https://code.claude.com/docs/en/plugins-reference",
"type": "reference",
"relevance": "Complete plugin.json manifest schema, lspServers plugin capability, LSP tool activation semantics, install scopes, policy gates, and plugin subagent restrictions",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "Claude Code Discover Plugins",
"url": "https://code.claude.com/docs/en/discover-plugins",
"type": "guide",
"relevance": "Official and community marketplace repos (anthropics/claude-plugins-official, anthropics/claude-plugins-community), /plugin UI tabs, install scopes, and plugin discovery flow",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "Claude Code Hooks Documentation",
"url": "https://code.claude.com/docs/en/hooks",
"type": "guide",
"relevance": "Hook lifecycle and event model used by plugin-provided hooks",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "Model Context Protocol Specification",
"url": "https://modelcontextprotocol.io/",
"type": "specification",
"relevance": "Reference for MCP server capability design and integration constraints",
"update_frequency": "quarterly",
"access": "free",
"add_as_web_search": true
},
{
"name": "GitHub Copilot CLI: About CLI Plugins",
"url": "https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-cli-plugins",
"type": "guide",
"relevance": "Copilot CLI plugin capability model (agents, skills, hooks, MCP, LSP), manifest location, and default marketplaces (copilot-plugins, awesome-copilot)",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "GitHub Copilot CLI Plugin Reference",
"url": "https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference",
"type": "reference",
"relevance": "Complete plugin.json and marketplace.json field schemas for Copilot CLI, including the .claude-plugin/plugin.json fallback manifest location that makes Claude Code plugins load unmodified",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "GitHub Copilot CLI: Finding and Installing Plugins",
"url": "https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-finding-installing",
"type": "guide",
"relevance": "Install source syntax (owner/repo, owner/repo:path, git URL, local path, plugin@marketplace) and declarative enabledPlugins configuration",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
}
],
"implementation_references": [
{
"name": "Claude Code GitHub Repository",
"url": "https://github.com/anthropics/claude-code",
"type": "repository",
"relevance": "Closed-source product page and issue tracker for Claude Code; not the implementation source — use code.claude.com/docs/en for authoritative behavior docs",
"update_frequency": "weekly",
"access": "free",
"add_as_web_search": false
},
{
"name": "Claude Plugins Official Marketplace",
"url": "https://github.com/anthropics/claude-plugins-official",
"type": "repository",
"relevance": "Anthropic-curated official plugin marketplace; auto-loaded in Claude Code; reference for plugin packaging layout and approved plugin examples",
"update_frequency": "weekly",
"access": "free",
"add_as_web_search": false
},
{
"name": "Claude Plugins Community Marketplace",
"url": "https://github.com/anthropics/claude-plugins-community",
"type": "repository",
"relevance": "Community-contributed plugin marketplace; must be manually added via /plugin > Marketplaces; reference for third-party plugin packaging patterns",
"update_frequency": "weekly",
"access": "free",
"add_as_web_search": false
},
{
"name": "Anthropic: Agent Skills for Claude Code",
"url": "https://www.anthropic.com/engineering/claude-code-agent-skills",
"type": "guide",
"relevance": "How packaged skills relate to runtime extension and prompt-budget constraints",
"update_frequency": "quarterly",
"access": "free",
"add_as_web_search": true
}
],
"patterns_and_safety": [
{
"name": "Anthropic: Building Effective Agents",
"url": "https://www.anthropic.com/engineering/building-effective-agents",
"type": "guide",
"relevance": "High-level guidance on agent-tool architecture and durable runtime boundaries",
"update_frequency": "quarterly",
"access": "free",
"add_as_web_search": true
},
{
"name": "Codex CLI Documentation",
"url": "https://github.com/openai/codex",
"type": "documentation",
"relevance": "Cross-runtime comparison point for coding-agent CLI constraints and extension patterns",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": false
},
{
"name": "OpenAI Codex Plugin Manifest Source",
"url": "https://github.com/openai/codex/blob/7d47056ea42636271ac020b86347fbbef49490aa/codex-rs/core-plugins/src/manifest.rs",
"type": "repository_source",
"relevance": "Pinned first-party source for plugin manifest fields, skills/MCP/apps/hooks paths, interface metadata, asset path validation, and default prompt limits",
"update_frequency": "pinned",
"access": "free",
"add_as_web_search": false
}
]
}
}
learnings.consolidated.md
# ai-coding-agents-plugins — Consolidated Learnings
Curated, dated, committed memory for this skill. Pruned from raw `learnings.md` via `agents-skills-feedback-loop/scripts/consolidate.py`. Human-approved.
Cap: 60 entries. When exceeded, promote durable rules to `references/`.
## Filter Override
<!-- Add 2-4 bullets that sharpen what counts as a learning for this skill. Leave empty to use the default filter from agents-skills-feedback-loop/references/learnings-format.md. -->
## Patterns That Work
## Mistakes to Avoid
## Domain Knowledge
## Open Questions
## Consolidated Principles
learnings.md
# ai-coding-agents-plugins — Learnings
## Patterns That Work
## Mistakes to Avoid
- [2026-07-11] Claude Code's community marketplace repo is anthropics/claude-plugins-community but its install-time name is claude-community, not claude-plugins-community.
## Domain Knowledge
- [2026-07-11] Copilot CLI's plugin.json loader falls back to .claude-plugin/plugin.json, so Claude Code plugins load unmodified on Copilot CLI (verified 2026-07-11).
## Open Questions
## Consolidated Principles
references/github-copilot-cli-plugin-manifest-and-marketplace.md
# GitHub Copilot CLI Plugin Manifest And Marketplace
Source: `docs.github.com/en/copilot/concepts/agents/copilot-cli/about-cli-plugins`, `docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference`, and `docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-finding-installing` (verified 2026-07-11). Copilot CLI reached general availability 2026-02-27; enterprise-managed plugins entered public preview 2026-05-06.
## Table of Contents
- [Design Goal](#design-goal)
- [Manifest Shape](#manifest-shape)
- [Component Path Fields](#component-path-fields)
- [Marketplace Manifest](#marketplace-manifest)
- [Install Sources](#install-sources)
- [Cross-Runtime Convergence](#cross-runtime-convergence)
- [Traps](#traps)
## Design Goal
Copilot CLI plugins package the same capability shape as other terminal coding agents: agents, skills, hooks, MCP servers, and LSP servers, discovered from a manifest rather than by executing plugin code.
## Manifest Shape
A plugin is a directory containing a `plugin.json` manifest at its root. The loader checks these locations in order and uses the first one found:
1. `.plugin/plugin.json`
2. `plugin.json`
3. `.github/plugin/plugin.json`
4. `.claude-plugin/plugin.json`
**Required field**: `name` — kebab-case, letters/numbers/hyphens only, max 64 characters.
**Metadata fields**: `description` (max 1024 chars), `version` (semver), `author` (`{name, email, url}`, name required), `homepage`, `repository`, `license`, `keywords`, `category`, `tags`.
## Component Path Fields
| Field | Type | Default | Notes |
|-------|------|---------|-------|
| `agents` | string\|array | `agents/` | `*.agent.md` files |
| `skills` | string\|array | `skills/` | directories containing `SKILL.md` |
| `commands` | string\|array | — | command directories |
| `hooks` | string\|object | — | `hooks.json` path or inline config |
| `extensions` | string\|array\|object | — | supports `{ paths: [...], exclusive: true }` |
| `mcpServers` | string\|object | — | `.mcp.json`/`mcp.json` path or inline |
| `lspServers` | string\|object | — | `lsp.json` path or inline |
LSP server entries require at least one of `command`, `bash`, or `powershell` (platform-specific launch scripts) plus a required `fileExtensions` map. Optional: `cwd` (supports `${PLUGIN_ROOT}`), `args`, `env`, `rootUri`, `initializationOptions`.
## Marketplace Manifest
A marketplace is a `marketplace.json` file (checked at the same four candidate locations, mirroring the plugin manifest search order) with required top-level `name` (kebab-case, max 64 chars), required `owner` (`{name, email?}`), and a required `plugins` array. Each plugin entry carries its own metadata plus a required `source` (relative path, GitHub reference, or URL) and an optional `strict` flag (default `true`) controlling validation strictness for that entry.
Copilot CLI registers two marketplaces by default: `copilot-plugins` and `awesome-copilot`.
## Install Sources
`copilot plugin install` (or the `/plugin install` slash command) accepts:
| Form | Example | Resolution |
|------|---------|------------|
| Marketplace | `plugin@marketplace` | registered marketplace catalog |
| GitHub repo | `OWNER/REPO` | repository root |
| GitHub subdirectory | `OWNER/REPO:PATH/TO/PLUGIN` | specific directory in repo |
| Git URL | `https://github.com/o/r.git` | any Git-compatible host |
| Local path | `./my-plugin` | filesystem directory |
Declarative installs are also supported: list plugins under `enabledPlugins` in configuration rather than issuing an imperative install command. Enterprise-managed plugins (public preview since 2026-05-06) let an organization push required plugins to Copilot CLI users the same way, without user opt-in.
## Cross-Runtime Convergence
Copilot CLI's manifest loader explicitly falls back to `.claude-plugin/plugin.json` if none of its own three preferred locations exist. That is a deliberate interoperability decision, not an accident: a plugin authored for Claude Code (manifest at `.claude-plugin/plugin.json`, with `skills/`, `agents/`, `hooks/hooks.json`, `.mcp.json`) loads as-is under Copilot CLI. The two schemas share nearly identical field names (`name`, `version`, `author`, `homepage`, `repository`, `license`, `keywords`, `skills`, `agents`, `hooks`, `mcpServers`, `lspServers`) and both tolerate unrecognized fields rather than rejecting the manifest.
**Design implication for a new coding-agent runtime**: treat `.claude-plugin/plugin.json` and `plugin.json` component conventions (`skills/<name>/SKILL.md`, `agents/*.md`, `.mcp.json`) as the de facto cross-vendor baseline as of mid-2026. A new host that wants day-one access to the existing plugin ecosystem should check for that manifest shape rather than inventing a parallel one — Copilot CLI's own maintainers made that call. This does not mean the schemas are identical forever; re-verify field-for-field compatibility before promising authors "write once, run on both hosts," since capability-specific fields (Copilot's `extensions` block with `exclusive`, Claude Code's `experimental.themes`/`experimental.monitors`, `channels`) do not have a counterpart on the other host and will be silently dropped or ignored there.
## Traps
- Assuming a Claude Code plugin "just works" on Copilot CLI because the manifest loads — capability-specific fields with no counterpart on the other host are silently ignored, not migrated or warned about.
- Treating the marketplace's registered short name as identical to the source repository name. As with Claude Code (see the Claude Code reference in this skill), a marketplace's install-time name and its GitHub source can differ; always confirm the name shown by the host's own marketplace-list command before writing install instructions into docs.
- Relying on `strict: true` (the default) in a marketplace entry to catch author mistakes at marketplace-build time, then forgetting that per-plugin manifests still load leniently at install/runtime — the strict/lenient split lives at different layers, same as in Claude Code.
references/openai-codex-plugin-manifest-and-marketplace.md
# OpenAI Codex Plugin Manifest And Marketplace
Source snapshot: OpenAI Codex commit `7d47056ea42636271ac020b86347fbbef49490aa` (2026-05-22), especially `codex-rs/core-plugins/src/manifest.rs` and the `codex-rs/core-plugins/src/marketplace_*` modules.
## Table Of Contents
- [Design Goal](#design-goal)
- [Manifest Shape](#manifest-shape)
- [Interface Metadata](#interface-metadata)
- [Path Rules](#path-rules)
- [Marketplace Lifecycle](#marketplace-lifecycle)
## Design Goal
Codex treats plugins as bundles that can contribute several capability families without requiring arbitrary code execution during discovery. Use the manifest as the trust and capability boundary.
## Manifest Shape
The Codex manifest loader recognizes these top-level concepts:
- `name`
- `version`
- `description`
- `keywords`
- paths for `skills`, `mcpServers`, `apps`
- `hooks` as paths or inline hook files
- `interface` metadata for UI presentation
For your runtime docs, this is a better baseline than a generic "plugin has tools" model. Plugins may ship prompt assets, external connectors, app mentions, hooks, and UI metadata in one unit.
## Interface Metadata
Codex's interface block supports:
- display name
- short and long descriptions
- developer name
- category
- capabilities
- website, privacy, and terms URLs
- default prompt suggestions
- brand color
- composer icon, logo, screenshots
This matters for a plugin marketplace: install decisions need enough UI metadata to explain what the plugin does before activation.
## Path Rules
Codex validates manifest paths under the plugin root and rejects path traversal or absolute-path tricks. It also caps default prompt count and prompt length.
Copy these constraints:
- require manifest-contributed paths to be plugin-relative
- normalize before activation
- keep interface assets inside the plugin bundle
- cap prompt suggestions so plugin UI metadata cannot consume unbounded prompt budget
## Marketplace Lifecycle
The Codex repo separates:
- installed marketplace records
- marketplace add/remove/upgrade flows
- remote bundle handling
- startup sync
- plugin store state
For a production runtime, treat plugin lifecycle as more than "load a folder":
- install
- verify manifest and bundle
- activate or toggle
- sync with remote/source-of-truth
- upgrade with compatibility checks
- remove and clean stale state
## Traps
- Discovering plugin capabilities by executing plugin code.
- Letting default prompts or screenshots reference files outside the bundle.
- Treating marketplace identity, installed path, and display name as one field.
- Loading hooks before manifest validation and policy checks complete.
references/plugin-loading-and-runtime-lifecycle.md
# Plugin Loading And Runtime Lifecycle
## Table Of Contents
- [Design Goal](#design-goal)
- [Discovery Sources](#discovery-sources)
- [Activation Pipeline](#activation-pipeline)
- [Component Reload Rules](#component-reload-rules)
- [Hot Reload And Session State](#hot-reload-and-session-state)
- [Failure Handling](#failure-handling)
## Design Goal
A coding-agent plugin system should not just "scan a folder." It should provide a repeatable lifecycle:
1. discover candidate plugins
2. validate manifests and paths
3. load enabled plugins into a normalized registry
4. rebuild component registries
5. reconnect or refresh side-effectful integrations
6. report errors without corrupting the session
That is the pattern visible in `claude_code`, where plugin loading is tied to cache invalidation, command and agent refresh, hook registration, and MCP diffing instead of being treated as a one-off file read.
## Discovery Sources
`pluginLoader.ts` explicitly describes multiple plugin discovery sources:
- installed or marketplace-backed plugins
- session-only plugins, such as inline or `--plugin-dir` sources
- built-in plugins managed through a separate built-in registry
For new coding-agent CLIs, encode this as host-owned precedence:
- core runtime capabilities
- built-in plugins that ship with the product
- installed plugins from user settings or marketplaces
- session-local overlays or inline plugins
The important part is not the exact source list. The important part is that each source class should be tracked separately so the runtime can explain where a capability came from and how it can be reloaded or disabled.
## Activation Pipeline
The `claude_code` pattern is modular:
- `pluginLoader.ts` discovers and normalizes loaded plugins
- capability-specific loaders rebuild commands, agents, hooks, output styles, or MCP integrations
- `refreshActivePlugins(...)` clears plugin caches and refreshes app state
- the CLI then updates mutable command and agent registries and diffs MCP server state
That suggests a good implementation model:
1. load manifests and enablement state
2. build a normalized list of enabled plugins
3. call dedicated loaders for each capability family
4. rebuild runtime registries only after plugin state is internally consistent
5. apply transport or connection diffs after registry rebuild
Do not let every component loader re-discover plugins independently from disk with its own policy rules. Centralize plugin selection first, then let specialized loaders consume that normalized state.
## Component Reload Rules
The source shows that not every capability reloads the same way:
- commands and agents can be rebuilt into fresh registries
- hooks require atomic clear-and-register behavior
- MCP servers may need a reconnect or config diff
- output styles can refresh their caches independently
This is the right default model:
- **pure metadata capabilities** can usually be rebuilt in memory
- **callback registries** need atomic swap behavior
- **transport-backed capabilities** need explicit reconnect or diff logic
- **long-lived external processes** may require staged teardown and restart
Do not market a plugin system as "hot reloadable" unless each capability family has a safe reload path.
## Hot Reload And Session State
Two `claude_code` patterns are worth copying:
- the runtime keeps mutable current command and agent registries so a live session can reflect plugin changes
- reload returns refreshed commands, agents, plugin list, MCP server status, and error count instead of silently mutating state
That is a strong contract for coding-agent CLIs with REPLs or SDK sessions:
- reload should be an explicit control action
- reload should return a structured summary
- caches must be cleared before reconstruction
- SDK- or flag-injected components should survive plugin reload if they live outside disk-backed plugin state
Treat reload as a state transition, not an implementation detail.
## Failure Handling
The source consistently uses best-effort aggregation:
- collect load errors per plugin
- let one failing capability family log errors without discarding successful state changes in others
- return success for the reload action when the registry swap succeeded, even if some readouts fail afterward
That yields better operational behavior for coding-agent hosts:
- manifest validation errors should identify the plugin and field
- registry rebuild errors should not leave partial duplicate registrations behind
- the user should be able to see which plugins are enabled, failed, or blocked
- disabling or uninstalling a plugin should immediately stop future behavior from that plugin, even before the next full reload where possible
The host should own error collection and reporting. Plugins should never decide what counts as a recoverable partial failure.
references/plugin-manifest-and-capability-model.md
# Plugin Manifest And Capability Model
## Table Of Contents
- [Design Goal](#design-goal)
- [Package Shape](#package-shape)
- [Identity And Source](#identity-and-source)
- [Capability Families](#capability-families)
- [Namespacing And Collision Control](#namespacing-and-collision-control)
- [Manifest Defaults Worth Copying](#manifest-defaults-worth-copying)
## Design Goal
For coding-agent runtimes, the plugin manifest should be the host's typed declaration of what a plugin may contribute. Do not force the runtime to execute arbitrary plugin code just to discover what the plugin is.
The `claude_code` source points to a stable pattern:
- plugin metadata lives in `plugin.json`
- standard directories provide conventional component homes
- the host still validates and normalizes each capability type before registration
- built-in plugins and installed plugins eventually become the same loaded-plugin shape, even if their sources differ
That is the right model to copy for coding-agent CLIs.
## Package Shape
The `pluginLoader.ts` header documents a pragmatic package shape:
```text
my-plugin/
├── plugin.json
├── commands/
├── agents/
├── skills/
├── hooks/
└── output-styles/
```
The schema layer adds a useful rule: keep common directories as the default discovery path, but allow the manifest to supplement them with explicit relative paths. In `schemas.ts`, commands, agents, skills, output styles, hooks, and MCP servers can all be declared directly in the manifest, not just inferred from directory presence.
Use that as the host contract:
- conventional directories for common cases
- manifest-declared additional paths for non-standard layouts
- explicit manifest validation before activation
## Identity And Source
The codebase distinguishes plugin identity from plugin path:
- marketplace or installed plugins use a `name@marketplace` style source identifier
- session-local plugins use an inline source sentinel
- built-ins use `name@builtin`
That separation matters because a coding-agent runtime usually needs all of the following:
- a stable logical plugin ID
- a user-facing display name
- a physical path or repository origin
- an enablement state
- a trust or source class
Do not make filesystem location the primary identifier. Use a stable plugin ID, then attach path, marketplace, or built-in status as metadata.
## Capability Families
The `claude_code` plugin subsystem effectively exposes these capability families:
- commands
- skills
- agents
- hooks
- MCP servers
- output styles
That is a strong default list for coding-agent runtimes because it separates:
- prompt-driven entrypoints
- reusable prompt packs
- delegated worker definitions
- lifecycle interception
- external tool transports
- rendering or response-mode customization
Keep these capability families typed in the host model. Avoid a generic `components: any[]` shape unless the runtime has a strict second-layer schema per capability type.
**The family list keeps growing — plan for it.** As of mid-2026, the strongest production plugin systems (Claude Code, and convergently GitHub Copilot CLI) have added at least four more typed families beyond the original six: LSP servers (real-time diagnostics and code navigation, distinct from MCP because they speak a different protocol to a locally installed binary), background monitors (long-lived shell watchers that push notifications rather than answer tool calls), themes (pure presentation, zero tool-call surface), and channels (message-injection bindings on top of an existing MCP server, e.g. Telegram/Slack/Discord). The expert move is to design the capability-family enum as open-ended from day one — a `family` discriminator plus a per-family registration handler — so adding LSP or monitors later is a new case, not a schema migration. Runtimes that hardcoded "plugins have commands, skills, agents, hooks, MCP" paid for it when LSP and monitors arrived.
A second convergence worth designing for explicitly: Claude Code's manifest treats `experimental.themes` and `experimental.monitors` as a distinct, unstable sub-namespace inside the same manifest, with the runtime warning (not erroring) when authors declare them at the top level instead. That is a reusable pattern for any host adding a new capability family — ship it nested under an `experimental` key first, promote it to top-level once the schema stabilizes, and keep both spellings valid with a deprecation warning during the transition instead of a breaking change.
## Namespacing And Collision Control
The `claude_code` loaders do not trust plugin content to remain globally unique. They namespace plugin-provided components in host code:
- commands are prefixed with the plugin name and nested namespace path
- agents are prefixed with the plugin name and namespace path
- output styles are prefixed with `pluginName:styleName`
That pattern is essential for coding-agent CLIs because slash commands, agent names, and style IDs are all user-visible surfaces with collision risk.
Copy these rules:
- the host owns final public names
- names should be deterministic from plugin name plus relative component path
- plugin authors can influence local names, but not bypass host namespacing
- duplicate-path and duplicate-name checks should happen before registration
## Manifest Defaults Worth Copying
From the schema and loader patterns, these defaults are worth preserving:
- keep metadata declarative: name, version, description, homepage, repository, license, keywords
- model dependencies explicitly instead of requiring plugins to probe each other
- reserve sentinel source names such as `inline` and `builtin`
- validate relative paths and refuse path traversal
- allow manifest-level extension of default directories instead of replacing them
- keep manifest parsing lenient at runtime if needed, but provide a stricter validator for authors
For a new coding-agent runtime, a good manifest baseline is:
- `id`
- `version`
- `display_name`
- `description`
- `capabilities`
- `dependencies`
- `default_enabled`
- `trust_level`
- optional capability-specific path fields
The host should then compile that into a normalized loaded-plugin object before any commands, agents, hooks, or MCP servers are made visible.
**Ignore-unknown-fields is a distribution feature, not laziness.** Claude Code's manifest parser silently ignores unrecognized top-level keys at load time (a typed-field-mismatch still fails, e.g. `keywords` as a string instead of an array). That single choice lets one `plugin.json` double as an npm `package.json`, a VS Code/Cursor extension manifest, or an MCPB/DXT bundle manifest, which materially lowers the cost of supporting a new coding-agent runtime — authors do not have to fork their manifest per host. Pair it with a separate strict-mode author tool (`claude plugin validate --strict`) that turns unknown-field and near-miss-field-name warnings into errors during CI, so typos are caught before publish without punishing every runtime load with pedantic parsing. Copy both halves: lenient runtime, strict authoring tool — not just one.
references/plugin-trust-boundaries-and-safety.md
# Plugin Trust Boundaries And Safety
## Table Of Contents
- [Design Goal](#design-goal)
- [Install-Time Trust Boundary](#install-time-trust-boundary)
- [Runtime Restrictions](#runtime-restrictions)
- [Validation And Path Safety](#validation-and-path-safety)
- [Policy And Marketplace Controls](#policy-and-marketplace-controls)
- [Recommended Host Rules](#recommended-host-rules)
## Design Goal
Coding-agent plugins can introduce tools, prompts, callbacks, file access patterns, MCP servers, and networked processes. The trust boundary therefore cannot live only inside plugin content. It has to live in the host.
The `claude_code` source demonstrates a good split:
- plugin installation and enablement are explicit user or policy actions
- plugin manifests are validated before load
- some capability classes are allowed only at the manifest level
- nested agent files are not allowed to quietly escalate certain powers
- policy and marketplace rules can block sources before the plugin becomes active
## Install-Time Trust Boundary
The strongest pattern to copy is this: trust decisions happen when a plugin is installed or enabled, not when a nested file is first discovered.
You can see this in two places:
- the UI warns users to trust a plugin before installing, updating, or using it
- plugin agents in `loadPluginAgents.ts` intentionally ignore `permissionMode`, `hooks`, and `mcpServers` declared inside agent files
That second rule matters. It prevents a third-party plugin from hiding elevated behavior inside a single agent file under `agents/`.
Use that same boundary:
- manifest-level capabilities are the install-time trust surface
- nested files may configure allowed behavior inside those capabilities
- nested files should not be allowed to introduce new high-risk capability classes
## Runtime Restrictions
The `claude_code` plugin runtime places important restrictions on plugin content:
- plugin-provided agents can declare tools, memory, isolation, model, effort, and other bounded metadata
- plugin-provided agents cannot silently add per-agent hooks or MCP servers
- built-in plugins are still treated as plugins in the UI and registry, but their enablement and trust model remains host-controlled
The design lesson is clear:
- let plugins contribute bounded runtime behavior
- keep privileged capability creation in host-controlled or manifest-controlled layers
- document which fields are ignored for third-party plugin content
Do not treat "a Markdown file with frontmatter" as automatically trusted just because it lives under a plugin directory.
## Validation And Path Safety
The validation layer in `validatePlugin.ts` and `schemas.ts` is doing real security work, not just linting:
- path traversal checks reject `..` in plugin component paths
- reserved source names such as `inline` and `builtin` are blocked for authors
- official marketplace impersonation is checked through reserved names and source validation
- runtime load is tolerant, but author-facing validation is stricter so typos and schema drift surface early
That is the right split for coding-agent plugin hosts:
- runtime parsing should be resilient enough not to crash sessions
- author tooling should be strict enough to catch dangerous or broken manifests early
- identity and path validation should happen before any component is activated
## Policy And Marketplace Controls
The source also shows that trust is not only about the user:
- org policy can force-disable plugins
- marketplaces can be allowlisted or blocklisted
- official marketplace names are protected against impersonation
- plugin install and enable commands honor policy and source restrictions
For enterprise or team-grade coding-agent runtimes, copy this model:
- user settings decide ordinary enablement
- policy settings can override user settings
- marketplace and source restrictions apply before install and enable
- the runtime should explain whether a plugin is disabled by the user, blocked by policy, or unavailable for source reasons
## Recommended Host Rules
Use these as defaults when designing a new coding-agent plugin runtime:
- treat plugin trust as a host policy problem, not a plugin author promise
- keep install-time trust separate from runtime component loading
- block nested content from introducing high-risk capability classes outside manifest-approved surfaces
- validate paths, reserved names, and source identity before activation
- namespace plugin-provided components so the user can audit provenance
- keep a separate built-in plugin registry even if built-ins eventually normalize into the same loaded-plugin shape
- make disable and uninstall remove future behavior immediately, even if some capabilities need a later full reload to fully refresh registries
If the runtime cannot safely support third-party plugins, do not fake it. Support built-ins or repo-local extensions only, and keep the trust boundary honest.
scripts/validate_manifest.py
#!/usr/bin/env python3
"""validate_manifest.py — Static validator for plugin.manifest.json files.
Uses Python standard library only (json, pathlib, sys, re, argparse).
No third-party dependencies required.
Usage:
python validate_manifest.py path/to/plugin.manifest.json
python validate_manifest.py . # scans current dir for manifest files
python validate_manifest.py --strict # exit 1 on warnings too
Exit codes:
0 — all checks passed (or passed with warnings in non-strict mode)
1 — one or more errors (or warnings in strict mode)
"""
import argparse
import json
import re
import sys
from pathlib import Path
# ── Constants ──────────────────────────────────────────────────────────────────
REQUIRED_TOP_LEVEL = ["id", "name", "version", "description", "runtime", "entry"]
REQUIRED_RUNTIME = ["min_agent_version", "platform"]
REQUIRED_TOOL = ["name", "description", "schema_path", "side_effects", "requires_approval"]
REQUIRED_COMMAND = ["name", "description", "prompt_path"]
SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$")
ID_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,79}$")
TOOL_NAME_RE = re.compile(r"^[A-Za-z][A-Za-z0-9_-]{0,63}$")
PLACEHOLDER_RE = re.compile(r"\{\{[A-Z_]+\}\}")
VALID_SIDE_EFFECTS = {
"file_read", "file_write", "file_delete",
"process_exec", "network_outbound", "network_inbound",
"env_read", "env_write",
}
VALID_PLATFORMS = {"claude-code", "codex", "agent-sdk", "goose"}
# ── Result collectors ─────────────────────────────────────────────────────────
class ValidationResult:
def __init__(self):
self.errors = []
self.warnings = []
def error(self, msg):
self.errors.append(f"ERROR: {msg}")
def warn(self, msg):
self.warnings.append(f"WARN: {msg}")
@property
def passed(self):
return len(self.errors) == 0
def print_report(self, path):
label = str(path)
all_messages = self.errors + self.warnings
if not all_messages:
print(f" [PASS] {label} — no issues found")
else:
for msg in all_messages:
print(f" {msg} ({label})")
# ── Field validators ──────────────────────────────────────────────────────────
def _check_placeholders(value, field, result):
"""Warn when a template placeholder like {{TOOL_NAME}} was not replaced."""
if isinstance(value, str) and PLACEHOLDER_RE.search(value):
result.warn(f"'{field}' contains unreplaced placeholder: {value!r}")
def _check_semver(value, field, result):
if not SEMVER_RE.match(value):
result.error(f"'{field}' must be a valid semver string (got {value!r})")
def _validate_tool(tool, idx, result):
prefix = f"tools[{idx}]"
for key in REQUIRED_TOOL:
if key not in tool:
result.error(f"{prefix} missing required field '{key}'")
name = tool.get("name", "")
if name and not TOOL_NAME_RE.match(name):
result.error(f"{prefix}.name must match [A-Za-z][A-Za-z0-9_-]{{0,63}} (got {name!r})")
if name:
_check_placeholders(name, f"{prefix}.name", result)
for effect in tool.get("side_effects", []):
if effect not in VALID_SIDE_EFFECTS:
result.error(f"{prefix}.side_effects contains unknown value {effect!r}; valid: {sorted(VALID_SIDE_EFFECTS)}")
requires = tool.get("requires_approval")
if requires is not None and not isinstance(requires, bool):
result.error(f"{prefix}.requires_approval must be a boolean")
schema_path = tool.get("schema_path", "")
_check_placeholders(schema_path, f"{prefix}.schema_path", result)
def _validate_command(cmd, idx, result):
prefix = f"commands[{idx}]"
for key in REQUIRED_COMMAND:
if key not in cmd:
result.error(f"{prefix} missing required field '{key}'")
name = cmd.get("name", "")
_check_placeholders(name, f"{prefix}.name", result)
prompt_path = cmd.get("prompt_path", "")
_check_placeholders(prompt_path, f"{prefix}.prompt_path", result)
# ── Main validator ────────────────────────────────────────────────────────────
def validate(manifest_path: Path) -> ValidationResult:
result = ValidationResult()
# 1. Parse JSON
try:
with manifest_path.open("r", encoding="utf-8") as f:
data = json.load(f)
except json.JSONDecodeError as exc:
result.error(f"Invalid JSON: {exc}")
return result
except OSError as exc:
result.error(f"Cannot read file: {exc}")
return result
if not isinstance(data, dict):
result.error("Manifest must be a JSON object at the top level")
return result
# 2. Required top-level fields
for key in REQUIRED_TOP_LEVEL:
if key not in data:
result.error(f"Missing required top-level field '{key}'")
# 3. id format
manifest_id = data.get("id", "")
if manifest_id and not ID_RE.match(manifest_id):
result.error(f"'id' must be lowercase kebab-case 2–80 chars (got {manifest_id!r})")
if manifest_id:
_check_placeholders(manifest_id, "id", result)
# 4. version semver
version = data.get("version", "")
if version:
_check_semver(version, "version", result)
_check_placeholders(version, "version", result)
# 5. description not empty and no placeholders
desc = data.get("description", "")
if desc:
_check_placeholders(desc, "description", result)
elif "description" in data:
result.error("'description' must not be empty")
# 6. runtime block
runtime = data.get("runtime", {})
if isinstance(runtime, dict):
for key in REQUIRED_RUNTIME:
if key not in runtime:
result.error(f"'runtime' missing required field '{key}'")
min_ver = runtime.get("min_agent_version", "")
if min_ver:
_check_semver(min_ver, "runtime.min_agent_version", result)
max_ver = runtime.get("max_agent_version")
if max_ver:
_check_semver(max_ver, "runtime.max_agent_version", result)
for platform in runtime.get("platform", []):
if platform not in VALID_PLATFORMS:
result.warn(f"'runtime.platform' contains unrecognised value {platform!r}; known: {sorted(VALID_PLATFORMS)}")
elif "runtime" in data:
result.error("'runtime' must be an object")
# 7. entry block — at least one language entry
entry = data.get("entry", {})
if isinstance(entry, dict):
if not entry:
result.error("'entry' must contain at least one language entry")
for lang, path in entry.items():
_check_placeholders(path, f"entry.{lang}", result)
elif "entry" in data:
result.error("'entry' must be an object")
# 8. tools
for idx, tool in enumerate(data.get("tools", [])):
if not isinstance(tool, dict):
result.error(f"tools[{idx}] must be an object")
else:
_validate_tool(tool, idx, result)
# 9. commands
for idx, cmd in enumerate(data.get("commands", [])):
if not isinstance(cmd, dict):
result.error(f"commands[{idx}] must be an object")
else:
_validate_command(cmd, idx, result)
# 10. permissions
perms = data.get("permissions", {})
if isinstance(perms, dict):
for perm in perms.get("required", []) + perms.get("optional", []):
if perm not in VALID_SIDE_EFFECTS:
result.warn(f"'permissions' references unknown side-effect {perm!r}; valid: {sorted(VALID_SIDE_EFFECTS)}")
# 11. Warn on unreplaced placeholders in name, author, repository
for field in ["name", "author", "repository", "homepage"]:
_check_placeholders(data.get(field, ""), field, result)
return result
# ── CLI ───────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="Validate plugin.manifest.json files (stdlib-only)."
)
parser.add_argument(
"path",
nargs="?",
default=".",
help="Path to a manifest file or directory to scan (default: current dir)",
)
parser.add_argument(
"--strict",
action="store_true",
help="Exit 1 on warnings as well as errors",
)
args = parser.parse_args()
target = Path(args.path)
if target.is_dir():
manifest_files = list(target.rglob("plugin.manifest.json"))
if not manifest_files:
# Also accept *.manifest.json patterns
manifest_files = list(target.rglob("*.manifest.json"))
if not manifest_files:
print(f"No manifest files found under {target}")
sys.exit(0)
elif target.is_file():
manifest_files = [target]
else:
print(f"Path not found: {target}")
sys.exit(1)
all_passed = True
print(f"\nValidating {len(manifest_files)} manifest file(s):\n")
for mf in sorted(manifest_files):
result = validate(mf)
result.print_report(mf)
if not result.passed:
all_passed = False
if args.strict and result.warnings:
all_passed = False
print()
if all_passed:
print("All manifests passed validation.")
sys.exit(0)
else:
print("One or more manifests failed validation.")
sys.exit(1)
if __name__ == "__main__":
main()
SKILL.md
---
name: ai-coding-agents-plugins
description: "Designs plugin systems for coding-agent runtimes and CLIs. Use when adding plugin manifests, extension points, built-in plugins, or reloadable agent integrations."
compatibility: Portable core. Works on Claude Code and Codex.
version: "1.1"
last_validated: 2026-07-11
---
# AI Coding Agents Plugins
Use this skill to design or review plugin systems for coding-agent runtimes, especially terminal-first CLIs that load skills, hooks, MCP servers, commands, agents, or output styles from installable extensions.
This skill owns plugin architecture for coding agents. For the broader coding-agent creation workflow, start with [`../ai-coding-agents/SKILL.md`](../ai-coding-agents/SKILL.md).
## ASCII Flow
```text
plugin package
|
v
manifest validation
identity + version + capability declarations + trust class
|
v
install or load
built-in | user | project | marketplace | repo-local
|
v
registration
skills + commands + tools + hooks + agents + MCP servers
|
v
policy filter + namespace
only safe capabilities enter the runtime
|
v
reload/disable/uninstall invalidates caches and visible registries
```
## Quick Reference
| Question | Read | Outcome |
|----------|------|---------|
| How should a coding-agent plugin be structured? | [`references/plugin-manifest-and-capability-model.md`](references/plugin-manifest-and-capability-model.md) | Package layout, manifest fields, capability families |
| How should plugins load, reload, and register? | [`references/plugin-loading-and-runtime-lifecycle.md`](references/plugin-loading-and-runtime-lifecycle.md) | Discovery order, activation flow, cache and reload rules |
| Where should trust boundaries live? | [`references/plugin-trust-boundaries-and-safety.md`](references/plugin-trust-boundaries-and-safety.md) | Install-time safety, runtime restrictions, policy controls |
| How does OpenAI Codex structure plugin manifests and marketplace lifecycle? | [`references/openai-codex-plugin-manifest-and-marketplace.md`](references/openai-codex-plugin-manifest-and-marketplace.md) | Skills/MCP/apps/hooks paths, interface metadata, path rules, marketplace add/remove/upgrade |
| How does GitHub Copilot CLI structure plugin manifests and marketplaces? | [`references/github-copilot-cli-plugin-manifest-and-marketplace.md`](references/github-copilot-cli-plugin-manifest-and-marketplace.md) | Manifest search order, component paths, marketplace.json shape, install sources, cross-runtime manifest convergence with Claude Code |
## When To Use
- Design a plugin system for a coding-agent CLI or terminal runtime
- Add installable extensions that provide commands, skills, hooks, agents, or MCP servers
- Separate built-in capabilities from marketplace or repo-local plugins
- Define plugin manifests, capability registration, namespacing, or reload behavior
- Review whether a plugin architecture has safe trust boundaries and host-owned precedence
## Use Other Skills
| Need | Use Instead |
|------|-------------|
| End-to-end coding agent or coding team design | [`../ai-coding-agents/SKILL.md`](../ai-coding-agents/SKILL.md) |
| MCP server design and connectivity | [`../agents-mcp/SKILL.md`](../agents-mcp/SKILL.md) |
| Hook authoring and lifecycle automation | [`../agents-hooks/SKILL.md`](../agents-hooks/SKILL.md) |
| Subagent definitions and delegation contracts | `agents-subagents` |
| Generic CLI and SDK design outside agent runtimes | [`../software-devtools/SKILL.md`](../software-devtools/SKILL.md) |
| Skill packaging and shared-skills validation | [`../agents-skills/SKILL.md`](../agents-skills/SKILL.md) |
## Default Workflow
1. **Classify the extension surface.** Decide whether the user actually needs a plugin, or just a local skill, hook, MCP server, or built-in command.
2. **Define capability families first.** Model commands, skills, hooks, agents, output styles, and MCP servers as typed host-owned extension points.
3. **Choose the trust boundary.** Decide what is allowed at install time, what is allowed at runtime, and which fields third-party plugin content is forbidden to control.
4. **Write the manifest contract.** Keep identity, metadata, dependencies, and capability declarations in the manifest instead of relying on implicit folder discovery alone.
5. **Namespace plugin-provided components.** Avoid collisions by having the host prefix commands, agents, and output styles with the plugin name or source.
6. **Define deterministic load order.** Core runtime first, then built-ins, then installed plugins, then session-local overlays or inline plugins.
7. **Split plugin state into layers.** Keep install intent, materialized on-disk plugin contents, and active in-memory components as separate layers.
8. **Design reload semantics explicitly.** Separate cache clearing, component re-registration, and transport reconnection. Do not assume hot reload is safe for every capability type.
9. **Preserve partial success.** A failing plugin should not take down the whole runtime if unaffected capability families can still be swapped safely.
10. **Validate with hostile cases.** Test duplicate names, invalid manifests, blocked plugins, stale caches, plugin disable, and partial reload failures.
## Host Rules
Use these defaults unless the runtime has a better documented model:
- Treat the host as the only authority on precedence, activation, and capability registration.
- Keep plugin manifests declarative. Avoid executing plugin code just to discover metadata.
- Make built-ins look like plugins to the UI and registry, but keep their trust and enablement rules host-controlled.
- Keep plugin-provided component names namespaced by plugin ID or plugin name.
- Allow third-party plugins to contribute capabilities, but do not let nested files silently escalate permissions beyond what the user approved at install time.
- Prefer reloadable registries for commands, agents, hooks, output styles, and MCP connections, but allow restart-required behavior when side effects cannot be safely swapped.
- Version plugin caches by compatibility boundary, not only by plugin display version.
- Treat git-subdir and path-based installs as distinct cache identities so different mounts cannot collide.
## Build Order
1. Define capability families and host-owned extension points.
2. Write the manifest contract and validation path.
3. Define plugin trust boundaries and install-time restrictions.
4. Implement deterministic discovery and activation order.
5. Split plugin state into intent, materialization, and active runtime components.
6. Add reload semantics, cache invalidation, and partial-failure handling.
7. Add managed-policy and built-in-plugin interaction rules.
## Core Invariants
- The host owns precedence, trust, and activation.
- Plugin manifests must be declarative and inspectable without executing plugin code.
- Built-ins may look like plugins in UI, but they are not trusted the same way.
- Capability names must be namespaced or collision-safe.
- Reloading one capability family must not silently corrupt another.
- Cache identity must include compatibility-relevant install context, not only plugin name.
## Failure Modes
- Executing plugin code during metadata discovery.
- Name collisions between built-ins and third-party capabilities.
- Stale caches keeping removed or disabled plugins active.
- Partial reload leaving command registries and MCP state inconsistent.
- Different plugin installs colliding in cache because path or subdir identity was ignored.
- Plugin-provided settings or nested files broadening trust beyond approved scope.
## Minimal Viable Version
- One declarative manifest format.
- One validation path before activation.
- One deterministic discovery and precedence order.
- One namespacing rule for plugin-provided capabilities.
- One explicit boundary between install intent, materialized plugin files, and active runtime registration.
- One restart-required fallback when hot reload is unsafe.
## What Strong Implementations Add
- Versioned cache directories and compatibility probing.
- Layered refresh that loads plugins first, then rebuilds dependent registries, then reconnects transports.
- Built-in plugins with host-controlled enablement semantics.
- Managed-policy interaction for plugin-only capabilities.
- Typed plugin errors and partial-failure recovery.
- Orphaned plugin-version cleanup and compatibility-aware cache keys.
- Reloadable registries for commands, hooks, agents, output styles, and MCP connections.
## Known Traps
- Treating plugin discovery as filesystem scanning alone and ending up with activation behavior that changes by path layout rather than manifest contract.
- Loading untrusted marketplace plugins with the same precedence and capability surface as built-ins or managed extensions.
- Rebuilding active registries in place during reload and leaving commands, hooks, or MCP connections in a half-updated state.
- Ignoring compatibility boundaries between core version, plugin API version, cache schema, and persisted state.
- Assuming every capability family can hot reload safely even when it holds long-lived connections or runtime-owned policy hooks.
## Common Anti-Patterns
- Using folder scanning as the manifest contract.
- Letting third-party plugins decide precedence or trust at runtime.
- Assuming hot reload is safe for every capability family.
- Treating installed files and active runtime state as one undifferentiated layer.
- Treating built-ins and marketplace plugins as identical trust classes.
- Ignoring cache schema and compatibility when plugin APIs evolve.
## Claude Code Plugin System Reference (2026)
Source: `code.claude.com/docs/en/plugins-reference` and `code.claude.com/docs/en/discover-plugins` (verified 2026-06-09).
### plugin.json manifest schema
The manifest lives at `.claude-plugin/plugin.json` and is optional (auto-discovery applies without it). `name` is the only required field if a manifest is present.
**Key fields (complete schema):**
| Field | Type | Notes |
|-------|------|-------|
| `$schema` | string | JSON Schema URL for editor autocomplete; ignored by Claude Code at load time |
| `name` | string | Required if manifest present. Kebab-case, no spaces. Used for component namespacing (`plugin-name:skill-name`). The marketplace entry can list the plugin under a different `name`; that entry name, not the one in `plugin.json`, is what `enabledPlugins` and `/plugin` key on |
| `displayName` | string | Human-readable, may contain spaces; shown in `/plugin` picker |
| `version` | string | Semantic version. Resolution order when a user checks for updates: `plugin.json` version, then the marketplace entry's version, then the git commit SHA of the source, then `"unknown"` (non-git sources). Pin an explicit version only if you will remember to bump it on every release — a stale explicit version silently blocks users from receiving new commits, where an unset version updates on every commit |
| `description` | string | Brief purpose statement |
| `author` | object | `{name, email, url}` |
| `homepage` | string | Documentation URL |
| `repository` | string | Source code URL |
| `license` | string | SPDX identifier |
| `keywords` | array | Discovery tags |
| `defaultEnabled` | boolean | If `false`, installs disabled; user must explicitly enable |
| `skills` | string\|array | Custom skill directories (adds to default `skills/`) |
| `commands` | string\|array | Flat `.md` skill files (replaces default `commands/`) |
| `agents` | string\|array | Agent files (replaces default `agents/`) |
| `hooks` | string\|array\|object | Hook configs (merges) |
| `mcpServers` | string\|array\|object | MCP configs (merges) |
| `lspServers` | string\|array\|object | LSP server configs |
| `outputStyles` | string\|array | Output style files (replaces default) |
| `experimental.themes` | string\|array | Color theme files |
| `experimental.monitors` | string\|array | Background monitor configs |
| `userConfig` | object | Prompted at enable time; values available as `${user_config.KEY}` |
| `channels` | array | Message channel declarations (Telegram/Slack/Discord style; bound to MCP servers) |
| `dependencies` | array | Other plugins this one requires, optionally with semver constraints, e.g. `{"name": "secrets-vault", "version": "~2.1.0"}`. Enabling a plugin transitively enables its dependencies at the same scope; disabling fails (with a chained command in the error) while a dependent is still enabled. `claude plugin prune` removes auto-installed dependencies no longer required by anything |
Two directories round out the package but aren't manifest fields: `bin/` (executables added to the Bash tool's `PATH` while the plugin is enabled — invokable as bare commands) and a root `settings.json` (default configuration applied on enable; only the `agent` and `subagentStatusLine` keys are currently honored). A plugin-root `CLAUDE.md` is **not** loaded as context — ship instructions as a skill instead.
**Component path rules**: `skills` adds to default; `commands`, `agents`, `outputStyles`, `experimental.themes`, `experimental.monitors` replace the default; `hooks`, `mcpServers`, `lspServers` have their own merge rules.
**Unrecognized fields are tolerated, not rejected** — Claude Code ignores top-level keys it doesn't recognize (a type mismatch on a recognized field still fails to load). This lets one `plugin.json` double as an npm `package.json` or a VS Code/Cursor/MCPB manifest. Use `claude plugin validate --strict` in CI to turn unrecognized-field and near-miss-name warnings into errors before publishing, without making every runtime load pedantic.
**Plugin subagent restrictions**: plugin-shipped agents support `name`, `description`, `model`, `effort`, `maxTurns`, `tools`, `disallowedTools`, `skills`, `memory`, `background`, `isolation` (`"worktree"` only). Fields `hooks`, `mcpServers`, and `permissionMode` are explicitly not supported for plugin-shipped agents.
### Skills-directory plugins (`@skills-dir`)
Any folder under a skills directory (`~/.claude/skills/` or `<project>/.claude/skills/`) that contains its own `.claude-plugin/plugin.json` loads automatically as a plugin named `<name>@skills-dir` on the next session — no marketplace, no install step, and the plugin is used in place rather than copied into the plugin cache. `claude plugin init <name>` scaffolds one. This is the fastest path from "a skill with extra opinions" to "a real plugin with bundled agents/hooks/MCP," and it is the mechanism a solo developer or small team should reach for before standing up a marketplace.
Trust follows scope, not source-of-truth intent: a personal-scope (`~/.claude/skills/`) plugin has no extra restrictions, but a project-scope (`<cwd>/.claude/skills/`) plugin — checked into a repo and therefore attacker-controlled if the repo is untrusted — loads only after the workspace trust dialog, and its MCP servers, LSP servers, and background monitors are restricted further (monitors do not load at all from project scope). Design new runtimes with this same scope-to-trust mapping: repo-sourced plugin content should never get the same default trust as a plugin the user personally placed in their home directory.
### LSP plugins as a capability family
Plugins with `lspServers` configuration provide real-time code intelligence via the LSP tool. This is distinct from MCP:
- LSP plugins configure how Claude Code connects to a language server binary (must be installed separately)
- Capabilities: jump to definition, find references, hover type info, list symbols, find implementations, call hierarchies
- Automatic diagnostics: after every file edit, the language server reports errors/warnings; Claude sees them without a separate tool call
- `diagnostics: false` in lspServers config keeps code navigation but suppresses diagnostic injection
- Official LSP plugins in `claude-plugins-official`: `clangd-lsp`, `csharp-lsp`, `gopls-lsp`, `jdtls-lsp`, `kotlin-lsp`, `lua-lsp`, `php-lsp`, `pyright-lsp`, `rust-analyzer-lsp`, `swift-lsp`, `typescript-lsp`
### Marketplaces
**Two Anthropic-run marketplaces, three different registered names — do not assume the install-time name matches the repo name:**
| Marketplace | Install-time `@name` | Source repo | Auto-enabled |
|-------------|----------------------|--------------|--------------|
| Official (curated by Anthropic) | `claude-plugins-official` | `anthropics/claude-plugins-official` | Yes — auto-loaded at startup; auto-update on by default |
| Community (safety-screened third-party, pinned to commit SHA per plugin) | `claude-community` | `anthropics/claude-plugins-community` | No — added manually via `/plugin marketplace add anthropics/claude-plugins-community`; auto-update off by default |
| Demo/example (Anthropic-maintained, not curated) | `claude-code-plugins` | `anthropics/claude-code` | No — added manually via `/plugin marketplace add anthropics/claude-code` |
**Trap:** the community marketplace's repo is `anthropics/claude-plugins-community` but its registered marketplace name is the shorter `claude-community` — installing with `<plugin>@claude-plugins-community` fails. Always confirm the actual name with `/plugin marketplace list` before writing install instructions into docs or onboarding scripts. This name/repo mismatch is a general marketplace-design trap, not unique to Anthropic's catalogs — expect it whenever a marketplace author renames the catalog independently of the repo.
Install syntax: `/plugin install <plugin-name>@<marketplace-name>` (interactive scope picker), or non-interactively `claude plugin install <plugin>@<marketplace> --scope {user|project|local}` (default `user`). `claude plugin details <name>` shows the always-on and per-invoke token cost before you install.
### /plugin tabbed UI
The `/plugin` command opens a four-tab interface (cycle with Tab / Shift+Tab):
- **Discover**: browse available plugins from all marketplaces; context-cost estimate shown per plugin; "Will install" section lists all components before install
- **Installed**: view, enable, disable, uninstall; grouped by scope; errors/unresolved deps at top
- **Marketplaces**: add, remove, update marketplace registries; toggle auto-update per marketplace
- **Errors**: plugin load errors and diagnostics
### Install scopes
| Scope | Settings file | Use case |
|-------|--------------|----------|
| `user` | `~/.claude/settings.json` | Personal, all projects (default) |
| `project` | `.claude/settings.json` | Shared via version control |
| `local` | `.claude/settings.local.json` | Project-specific, gitignored |
| `managed` | Managed settings | Read-only, admin-installed, update-only |
### /reload-plugins
Run `/reload-plugins` to activate newly installed, enabled, or disabled plugins without restarting. Shows counts for plugins, skills, agents, hooks, MCP servers, and LSP servers reloaded. If a plugin provides MCP servers, reloading invalidates the prompt cache; a warning is shown; pass `--force` to apply anyway.
### Context-cost estimate
The details pane and `claude plugin details <name>` command show per-component always-on token cost and per-invoke token cost before installation.
### Plugin subagent restrictions (security)
Plugin-shipped agents cannot set `hooks`, `mcpServers`, or `permissionMode`. These fields are silently ignored when the agent is defined inside a plugin. This prevents plugins from escalating privileges through agent definitions.
### Policy gates
| Setting | Effect |
|---------|--------|
| `disableBundledSkills` | Disables skills bundled with Claude Code itself (not marketplace plugins) |
| `strictPluginOnlyCustomization` | Restricts customization sources to plugins and managed settings only; blocks user/project skills, hooks, MCP from non-plugin paths |
| `strictKnownMarketplaces` | Limits installable plugins to admin-allowlisted marketplaces |
| `blockedMarketplaces` | Blocks specific marketplaces (including `skills-dir` source) |
## GitHub Copilot CLI Plugin System Reference (2026)
Source: `docs.github.com/en/copilot/concepts/agents/copilot-cli/about-cli-plugins` and `docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference` (verified 2026-07-11). Copilot CLI went GA 2026-02-27; enterprise-managed plugins hit public preview 2026-05-06. Full field tables in [`references/github-copilot-cli-plugin-manifest-and-marketplace.md`](references/github-copilot-cli-plugin-manifest-and-marketplace.md).
Copilot CLI converged on essentially the same capability model as Claude Code: agents (`*.agent.md`), skills (`SKILL.md`), hooks, MCP servers, and LSP servers, discovered from a `plugin.json` manifest whose required field is `name` and whose install sources include `owner/repo`, `owner/repo:path/to/plugin`, git URLs, local paths, or `plugin@marketplace`. Two marketplaces (`copilot-plugins`, `awesome-copilot`) are registered by default.
**The load-bearing fact for anyone building a new coding-agent plugin host**: Copilot CLI's manifest loader checks `.plugin/plugin.json`, then `plugin.json`, then `.github/plugin/plugin.json`, then falls back to **`.claude-plugin/plugin.json`** — the exact Claude Code manifest location — before giving up. That is a deliberate compatibility decision, not coincidence: a plugin authored for Claude Code loads on Copilot CLI without modification for the fields both hosts share. Treat `.claude-plugin/plugin.json` plus the `skills/`, `agents/`, `hooks/hooks.json`, `.mcp.json` conventions as the emerging cross-vendor baseline, and design any new runtime to read that shape rather than inventing a fourth one. Do not oversell the convergence, though — host-specific fields (Copilot's `extensions` block with an `exclusive` flag, Claude Code's `experimental.themes`/`experimental.monitors`/`channels`) have no counterpart on the other host and are silently dropped there, not migrated.
## Cross-Platform Patterns (Goose)
Goose unifies the "plugin" and "MCP extension" concepts into a single typed extension model, and shows what manifest-in-recipe delivery looks like when tasks ship with their own extension declarations.
### Unified extension kind with `type:` discriminator
Goose recipes declare extensions inline:
```yaml
extensions:
- type: builtin
name: developer
- type: mcp
name: github
```
Both are `extension` entries; the `type` discriminator selects transport and trust. This collapses the dual-track "built-ins vs MCP" mental model into one ontology with two transports.
- **Pattern:** model the extension registry with a single type whose `origin` or `transport` field carries `builtin | mcp | ...`. Manifest, precedence, namespacing, and cache identity apply uniformly.
- **Anti-pattern:** maintaining parallel "plugin registry" and "MCP registry" APIs with subtly different lifecycle, reload, and trust semantics. Future transports (ACP-delegated extensions, WASM plugins) then each need their own track.
- **Recipe:** one `Extension` trait / interface, one activation path, one cache-identity rule. The `transport` field is free to evolve — `builtin`, `mcp-stdio`, `mcp-sse`, `acp-client` — without new mental models.
### Manifest-in-recipe (task-level extension declaration)
In Goose, the unit of work (recipe) declares its extension dependencies inline. This differs from classic plugin systems where plugins are enabled globally at runtime-config level.
- **Pattern:** allow plugin/extension declaration at multiple layers: host config, project/repo config, *and* task artifact. Task-layer declarations are subsets of what the project/host allows and define the active envelope for that task only.
- **Anti-pattern:** forcing all plugin activation to happen at CLI startup. Coding agents that support shareable task blueprints need blueprint-local extension sets — a recipe shared between teammates should carry its dependencies, not assume the receiver pre-enabled them.
- **Recipe:** the plugin resolver is called with a scope: `(host_config, project_config, task_manifest)`. The resolver intersects them — task can narrow but not broaden. A task's declared extensions must be subsets of the project's allowlist; violations are install-time errors, not runtime surprises.
### Runtime-shipped official plugins (Codex Build iOS/Mac)
OpenAI's Codex CLI ships first-party plugins ("Build iOS Apps", "Build macOS Apps", authored by Thomas Ricouard / @Dimillian) that auto-update with each Codex release. They package skills (SwiftUI Liquid Glass, Performance Audit, View Refactor, App Intents, AppKit interop, signing/notarization) behind a single plugin identity.
- **Pattern:** treat host-shipped plugins as a distinct trust class. They look like marketplace plugins in the registry but update in lockstep with the runtime version, so their compatibility key is the runtime version itself, not a separate plugin version.
- **Anti-pattern:** giving official plugins the same cache identity and update cadence as user-installed plugins. Their staleness model is different — a runtime upgrade should invalidate them automatically.
- **Recipe:** carry an `origin: official | builtin | marketplace | local` tag on every plugin record. Tie `official` cache keys to `(runtime_version, plugin_id)` and refresh on runtime upgrade without prompting the user. Source: https://github.com/openai/codex
### Schema validation as a first-class gate
Goose's `recipe-scanner/` validates YAML recipes (and therefore their declared extensions) at build/ship time. This generalizes: plugin manifests and task manifests should face static validation before activation, not just at runtime load.
- **Pattern:** manifests are structured data; ship a validator and run it in CI, at install, and at activation. Treat an invalid manifest as a shipping defect, not a runtime edge case.
## Navigation
### References
- [`references/plugin-manifest-and-capability-model.md`](references/plugin-manifest-and-capability-model.md) — Package layout, manifest structure, and capability typing
- [`references/plugin-loading-and-runtime-lifecycle.md`](references/plugin-loading-and-runtime-lifecycle.md) — Discovery, caching, reload, and activation flow
- [`references/plugin-trust-boundaries-and-safety.md`](references/plugin-trust-boundaries-and-safety.md) — Trust model, validation, and policy guardrails
- [`references/openai-codex-plugin-manifest-and-marketplace.md`](references/openai-codex-plugin-manifest-and-marketplace.md) — OpenAI Codex plugin manifest fields, interface metadata, path validation, and marketplace lifecycle
- [`references/github-copilot-cli-plugin-manifest-and-marketplace.md`](references/github-copilot-cli-plugin-manifest-and-marketplace.md) — GitHub Copilot CLI manifest search order, component paths, marketplace.json shape, and cross-runtime convergence with Claude Code
### Data
- [`data/sources.json`](data/sources.json) — Primary documentation and source references for plugin-runtime guidance
### Related Skills
- [`../ai-coding-agents/SKILL.md`](../ai-coding-agents/SKILL.md) — Broader coding-agent architecture and creation workflow
- [`../agents-hooks/SKILL.md`](../agents-hooks/SKILL.md) — Hook lifecycle design
- [`../agents-mcp/SKILL.md`](../agents-mcp/SKILL.md) — MCP server integration
## Fact-Checking
- Known bugs, regressions, framework/compiler/runtime footguns, and version-specific crash or workaround guidance must be verified against current primary web sources before being treated as current fact.
- The patterns in these references are grounded in a local April 2026 `claude_code` source snapshot. Re-check upstream code or docs before relying on volatile runtime details.
- Plugin manifest and loader semantics change faster than shared-skill guidance. Validate field names, reload behavior, and trust restrictions against the current runtime before shipping.
- If the target runtime differs from Claude Code, preserve the architecture principles but re-check the concrete capability and policy surfaces.
## Learnings Loop
Before applying this skill on a non-trivial task, read `learnings.consolidated.md` in this directory (and `learnings.md` if present).
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to `learnings.md` via `agents-skills-feedback-loop/scripts/append_learning.py`. Do not modify `SKILL.md` itself.