Spec Version: 1.0.0
This document defines the canonical Open Plugin Specification v1.0.0. It is a self-contained specification for packaging agent extensions into distributable plugins. Everything in sections 1–12 is required for conformance.
The smallest useful plugin is a directory with one skill.
hello-plugin/
├── .plugin/
│ └── plugin.json
└── skills/
└── greet/
└── SKILL.md
./.plugin/plugin.json
{
"name": "hello-plugin"
}./skills/greet/SKILL.md
---
name: greet
description: Greet the user and offer help.
---
Greet the user. If `$ARGUMENTS` is present, include it in the greeting.A host that supports skills can load this plugin by reading .plugin/plugin.json, discovering skills/greet/SKILL.md, and surfacing /hello-plugin:greet.
Note: The Core Profile reading path in this document is package layout (§4), manifest loading (§5–6), discovery (§7), skills and MCP servers (§8), namespacing (§9),
${PLUGIN_ROOT}expansion (§10), and minimum host conformance (§12). Commands, agents, rules, hooks, LSP servers, and output styles are optional extended component types defined in Appendix D.
- Status and version
- Conformance language
- Terminology
- Plugin package model
- Manifest location and precedence
- Manifest schema
- Component discovery
- Component definitions
- Namespacing
- Environment variables and placeholder expansion
- Versioning
- Host conformance
Appendices (not required for conformance)
- Appendix A: Conformance Checklist
- Appendix B: Marketplace Index and Discovery
- Appendix C: Extended Hook Events
- Appendix D: Extended Component Types
- Design Decisions
- Future Considerations
This specification defines version 1.0.0 of the Open Plugin format.
Plugin hosts and plugin packages claiming conformance to Open Plugin v1 MUST implement or follow the requirements in this document.
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in sections 1–12 of this document are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals. Appendices and other non-normative sections use these terms informally.
| Term | Meaning | Description |
|---|---|---|
| Plugin | Package unit | A self-contained directory that bundles one or more components and optional metadata. |
| Plugin root | Filesystem root | The top-level directory of a plugin package. |
| Manifest | Metadata document | A plugin.json file in a metadata directory at the plugin root. |
| Component | Plugin-provided capability | A skill or MCP server configuration (core), or an extended type such as a command, agent, rule, hook, LSP server, or output style. |
| Host | Plugin runtime | A tool that discovers, installs, loads, and executes plugin components. |
| Discovery source | Scan location | A default location, manifest-declared path, inline configuration object, or marketplace entry from which a host loads components. |
| Path config | Discovery object | An object with paths that controls scanning for a component type. |
| Inline MCP config | MCP definition object | A mcpServers manifest field whose object value contains an mcpServers key. |
| Marketplace | Plugin collection | A named collection of one or more plugins declared by marketplace.json. |
- A plugin is a directory rooted at a single filesystem location.
- A plugin MUST include a manifest at
.plugin/plugin.json. - A plugin MUST contain zero or more supported components. A directory with only a manifest is valid but may not be useful on hosts that require at least one supported component at runtime.
- All relative paths declared by the plugin MUST be interpreted relative to the plugin root.
- All relative paths declared by the plugin MUST start with
./. - A plugin MUST NOT reference files outside its own directory tree by using
../traversal. - Hosts MUST reject any configured path that escapes the plugin root after path normalization.
Example: valid and invalid relative paths
{
"skills": "./custom-skills/",
"mcpServers": "./config/mcp.json"
}{
"skills": "../shared-skills/",
"mcpServers": "config/mcp.json"
}The first example is valid — all paths start with ./ and stay within the plugin root. The second is invalid — ../shared-skills/ escapes the plugin root and config/mcp.json does not start with ./.
my-plugin/
├── .plugin/
│ └── plugin.json
├── commands/
├── agents/
├── skills/
├── output-styles/
├── rules/
├── hooks/
│ └── hooks.json
├── .mcp.json
├── .lsp.json
├── scripts/
├── assets/
├── LICENSE
└── CHANGELOG.md
Example: Core Profile layout (minimal)
code-assistant/
├── .plugin/
│ └── plugin.json
├── skills/
│ └── summarize/
│ └── SKILL.md
└── .mcp.json
Example: full plugin with all component types
devtools/
├── .plugin/
│ └── plugin.json
├── commands/
│ ├── deploy.md
│ └── status.md
├── skills/
│ └── code-review/
│ ├── SKILL.md
│ ├── scripts/
│ │ └── analyze.sh
│ └── references/
│ └── checklist.md
├── agents/
│ └── security-reviewer.md
├── rules/
│ └── prefer-const.mdc
├── hooks/
│ └── hooks.json
├── .mcp.json
├── .lsp.json
├── scripts/
│ ├── format.sh
│ └── check-env.sh
├── assets/
├── LICENSE
└── CHANGELOG.md
See also: §5 Manifest location and precedence for how the metadata directory relates to manifest discovery, and §7 Component discovery for how component directories are scanned.
- The metadata directory MUST contain
plugin.json. - The metadata directory MAY also contain
marketplace.jsonwhen the same directory root is both a plugin root and a marketplace root. - Component directories such as
skills/, when present, MUST exist at the plugin root, not inside the metadata directory. - Missing component directories are not errors.
Example:
my-plugin/
└── .plugin/
├── plugin.json
└── marketplace.json
Hosts MUST check for a manifest at .plugin/plugin.json.
A host that defines a vendor-prefixed manifest location such as .<tool-name>-plugin/plugin.json MUST also check that location and SHOULD prefer it over .plugin/plugin.json when both are present.
| Path | Description | Notes |
|---|---|---|
.plugin/plugin.json |
Vendor-neutral manifest | REQUIRED. RECOMMENDED for new multi-host plugins. |
.<tool-name>-plugin/plugin.json |
Vendor-specific manifest | Preferred by the matching host when present. |
Example:
my-plugin/
├── .plugin/plugin.json
└── .claude-plugin/plugin.json
A Claude-like host selects .claude-plugin/plugin.json. A host with no vendor-prefixed manifest location selects .plugin/plugin.json.
See also: §6 Manifest schema for the structure of the manifest file, and §12 Host conformance for requirements around supporting
.plugin/plugin.json.
- A plugin MAY provide identical manifest content in multiple locations.
- When multiple manifest locations exist, a host SHOULD prefer its own vendor-prefixed manifest and SHOULD otherwise fall back to
.plugin/plugin.json. - When both locations exist and contain different content, the selected manifest is authoritative for that host.
- Hosts MAY warn when multiple manifest locations contain inconsistent content.
Implementer note: Example user-facing message:
Plugin "devtools": manifest at ".claude-plugin/plugin.json" differs from ".plugin/plugin.json". Using ".claude-plugin/plugin.json" as authoritative.Example machine-readable record:
{"level":"warn","event":"open_plugin.manifest.inconsistent","plugin":"devtools","selected":".claude-plugin/plugin.json","other":".plugin/plugin.json","action":"used_selected"}
See also: §5 Manifest location and precedence for where the manifest is loaded from, and §7 Component discovery for how manifest fields control discovery paths.
The manifest MUST be JSON and MUST contain a top-level object.
{
"name": "plugin-name",
"version": "1.2.0",
"description": "Brief plugin description",
"author": {
"name": "Author Name",
"email": "author@example.com",
"url": "https://example.com"
},
"homepage": "https://docs.example.com/plugin",
"repository": "https://github.com/example/plugin",
"license": "MIT",
"keywords": ["keyword1", "keyword2"],
"commands": ["./custom/commands/"],
"agents": "./custom/agents/",
"skills": "./custom/skills/",
"rules": "./custom/rules/",
"hooks": "./config/hooks.json",
"mcpServers": "./mcp-config.json",
"lspServers": "./.lsp.json",
"outputStyles": "./styles/"
}Example: minimal manifest
{
"name": "minimal-plugin",
"version": "1.0.0",
"description": "The simplest possible plugin."
}Example: full manifest
{
"name": "devtools",
"version": "2.0.0",
"description": "Full-featured development toolkit.",
"author": {
"name": "Open Plugin Examples"
},
"license": "Apache-2.0",
"keywords": ["devtools", "code-review", "linting", "security"],
"commands": "./commands/",
"skills": {
"paths": ["./skills/"]
},
"hooks": "./hooks/hooks.json",
"mcpServers": "./.mcp.json",
"lspServers": "./.lsp.json"
}| Field | Type | Description |
|---|---|---|
name |
string | Unique plugin identifier used for namespacing and settings keys. |
| Field | Type | Description |
|---|---|---|
version |
string | Version string (Semantic Versioning RECOMMENDED). Used for update checks and cache freshness. |
description |
string | Short description of plugin purpose. |
author |
object | Author object with optional name, email, and url string fields. |
homepage |
string | Documentation or homepage URL. |
repository |
string | Source repository URL. |
license |
string | SPDX license identifier. |
keywords |
string[] | Search and discovery tags. |
Core component path fields:
| Field | Type | Description |
|---|---|---|
skills |
string | string[] | object | Skill directories, or a path config. |
mcpServers |
string | string[] | object | MCP config paths, a path config, or inline MCP config. |
Hosts MAY support additional component path fields for extended component types. See Appendix D: Extended Component Types for details.
When the manifest declares paths for a component type, those paths control discovery for that type. The default location is not scanned unless the manifest explicitly includes it.
Example: custom paths override the default
{
"skills": "./custom-skills/"
}The host scans only custom-skills/; the default skills/ directory is not scanned.
Example: retaining the default alongside custom paths
{
"skills": ["./skills/", "./extra-skills/"]
}The host scans both skills/ and extra-skills/ because the default directory is explicitly included.
The object form for discovery paths is:
| Field | Type | Description |
|---|---|---|
paths |
string[] | Relative files or directories to scan. |
Hosts MUST interpret object values for component path fields as follows:
| Field | Object shape | Interpretation |
|---|---|---|
mcpServers |
Object containing mcpServers |
Inline MCP config. |
| Any path field | Object containing paths |
Path config. |
Hosts that support extended component types (see Appendix D) MAY define additional object shapes for those fields (e.g., inline hook config, inline LSP config).
If an object matches none of the shapes above, or matches more than one shape, the host SHOULD treat the field as invalid and SHOULD warn.
Implementer note: Example user-facing message:
Plugin "devtools": manifest field "mcpServers" has an unrecognized shape (expected path config or inline server config). Field ignored; plugin load continues.Example machine-readable record:
{"level":"warn","event":"open_plugin.manifest.invalid_object","plugin":"devtools","field":"mcpServers","action":"ignored","continue":true}
Example: path config
{
"skills": {
"paths": ["./skills/", "./extra-skills/"]
}
}Example: inline MCP config
{
"mcpServers": {
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"]
}
}
}
}Example: invalid ambiguous object
{
"mcpServers": {
"database": {
"command": "npx"
}
}
}This is invalid because it is neither a path config with paths nor an inline MCP config with a top-level mcpServers key.
The manifest name value MUST satisfy all of the following:
| Constraint | Requirement | Description |
|---|---|---|
| Length | 1-64 characters | The name MUST be between 1 and 64 characters inclusive. |
| Character set | a-z, 0-9, -, . |
Lowercase alphanumeric characters, hyphens, and periods only. |
| Start and end | Alphanumeric | The first and last characters MUST be alphanumeric. |
| Repetition | No -- or .. |
Consecutive hyphens and consecutive periods are not allowed. |
Periods are allowed in plugin names.
Valid names: my-plugin, acme.tools, lint3r, a
Invalid names: My-Plugin (uppercase), -start (leading hyphen), has--double (consecutive hyphens), too.many..dots (consecutive periods), `` (empty)
See also: §4 Plugin package model for directory layout conventions, §6 Manifest schema for how manifest fields declare discovery paths, and §9 Namespacing for how discovered components are named.
Hosts MUST discover core components in the following default locations when no manifest field overrides discovery for that component type. When the manifest declares paths for a component type, those paths control discovery and the default location is not scanned unless explicitly included. See §6.4 for details.
Core component locations:
| Component | Default location | Default pattern |
|---|---|---|
| Skills | skills/ |
Subdirectories containing SKILL.md |
| MCP servers | .mcp.json |
JSON configuration |
Hosts MAY support extended component types with their own default locations. See Appendix D: Extended Component Types for details.
Example: given a plugin reports-plugin with this layout:
reports-plugin/
├── .plugin/plugin.json
├── skills/summarize/SKILL.md
└── .mcp.json
The host discovers skill summarize and MCP servers from .mcp.json — all from default locations.
- If a default location is absent, the host MUST NOT treat that as an error.
- When the manifest declares paths for a component type, those paths control discovery. The default location is not scanned unless explicitly included.
- When no manifest field is declared for a component type, the default location is scanned normally.
Example: a plugin declares "skills": "./custom-skills/" but has no skills/ directory. The host scans only custom-skills/ and does not error on the missing default skills/ path.
Example: default discovery only
reports-plugin/
├── .plugin/plugin.json
├── skills/summarize/SKILL.md
└── .mcp.json
The host discovers skill summarize and MCP servers from .mcp.json — all from default locations.
Example: manifest paths override defaults
./.plugin/plugin.json
{
"name": "reports-plugin",
"skills": "./custom-skills/"
}reports-plugin/
├── skills/summarize/SKILL.md
└── custom-skills/deploy/SKILL.md
The host discovers only /reports-plugin:deploy from custom-skills/. The default skills/summarize/ is not discovered because the manifest declares custom paths.
Example: retaining the default alongside custom paths
./.plugin/plugin.json
{
"name": "reports-plugin",
"skills": ["./skills/", "./custom-skills/"]
}The host discovers both /reports-plugin:summarize and /reports-plugin:deploy because the default directory is explicitly included.
See also: §7 Component discovery for how component files are located, and §9 Namespacing for how component names are surfaced to users and models.
This specification normatively defines discovery for two component types that are backed by open standards: skills and MCP servers. Hosts MAY support additional component types (commands, agents, rules, hooks, LSP servers, output styles). See Appendix D: Extended Component Types for reference definitions of these additional types.
Hosts MUST ignore component types they do not support.
Agent Skills follow the Agent Skills specification. The Agent Skills spec is the source of truth for the SKILL.md format, frontmatter fields, and directory layout (scripts/, references/, assets/).
This specification defines how Agent Skills are discovered and namespaced within a plugin, not the skill format itself.
The default discovery location is skills/. Each subdirectory containing SKILL.md is treated as one skill.
Example: a skill directory named deploy inside skills/:
skills/
deploy/
SKILL.md # name: deploy
scripts/
rollback.sh
references/
runbook.md
MCP server configuration follows the Model Context Protocol specification. The MCP spec is the source of truth for server configuration fields, transport types (stdio, HTTP/SSE), and lifecycle semantics.
This specification defines how MCP servers are discovered within a plugin and how ${PLUGIN_ROOT} expansion applies to configuration values.
The default MCP configuration path is .mcp.json. MCP servers MAY also be declared inline in the manifest mcpServers field.
The configuration MUST contain a top-level mcpServers object. Each key is the server name and MUST be unique within the plugin after source resolution.
The command, args, env, and cwd fields in MCP server configuration MUST support ${PLUGIN_ROOT} expansion.
Example: .mcp.json
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_URL": "postgresql://localhost:5432/mydb"
}
},
"filesystem": {
"command": "${PLUGIN_ROOT}/bin/fs-server",
"args": ["--root", "${PLUGIN_ROOT}/data"],
"cwd": "${PLUGIN_ROOT}"
}
}
}- When no
mcpServersmanifest field is declared, hosts MUST discover MCP configuration from the default.mcp.jsonlocation. - When the manifest declares
mcpServerspaths, those paths MUST point to explicit JSON files (not directories). Each file MUST contain a top-levelmcpServersobject. When the manifest declares inline MCP config, that config is used directly. - Manifest-declared paths control discovery per the rules in §6.4.
- If a server fails to start, the host SHOULD log the error and continue loading other components.
- If multiple discovery sources define the same MCP server name, behavior is implementation-defined. Hosts SHOULD warn and SHOULD resolve conflicts deterministically. Conflicting MCP server names across discovery sources MUST NOT crash plugin loading.
See also: §6.7 Plugin name constraints for allowed characters in plugin names, and §8 Component definitions for the naming rules of each component type.
Hosts SHOULD namespace plugin-provided components to avoid collisions. The RECOMMENDED format is:
{plugin-name}:{component-name}
Example:
deploy-tools:status
When surfacing MCP tools to a model, hosts SHOULD include both the plugin name and the server name to avoid collisions.
The RECOMMENDED surfaced identifier format is:
mcp__plugin_{plugin-name}_{server-name}__{tool-name}
Example:
| Plugin | Server | Tool | Identifier |
|---|---|---|---|
deploy-tools |
database |
query |
mcp__plugin_deploy-tools_database__query |
Hosts that use a different naming convention MAY adapt this format.
Example: component namespacing
| Source file | Component name | Surfaced identifier |
|---|---|---|
skills/code-review/SKILL.md |
code-review |
devtools:code-review |
skills/deploy/SKILL.md |
deploy |
devtools:deploy |
Example: MCP tool namespacing
| Plugin | Server | Tool | Surfaced identifier |
|---|---|---|---|
devtools |
database |
query |
mcp__plugin_devtools_database__query |
devtools |
database |
migrate |
mcp__plugin_devtools_database__migrate |
deploy-tools |
kubernetes |
rollout_status |
mcp__plugin_deploy-tools_kubernetes__rollout_status |
See also: §8.2 MCP servers for the fields where
${PLUGIN_ROOT}expansion applies, and §4.1 General requirements for path safety rules.
Hosts that launch plugin subprocesses (e.g., MCP servers, hook commands) MUST provide an environment variable containing the absolute plugin root path.
| Variable | Description | Notes |
|---|---|---|
PLUGIN_ROOT |
Absolute plugin root path | REQUIRED for hosts that launch plugin subprocesses. |
Hosts that provide persistent plugin storage SHOULD set a data directory variable.
| Variable | Description | Notes |
|---|---|---|
PLUGIN_DATA |
Persistent data directory path | RECOMMENDED. |
PLUGIN_DATA is the absolute path to a host-managed persistent data directory for the plugin. This directory SHOULD survive plugin updates and reinstalls. The host SHOULD create the directory on first reference and MAY delete it when the plugin is uninstalled.
Use PLUGIN_DATA for: installed dependencies (node_modules, virtual environments), generated code, caches, and other plugin state that should persist across updates. Use PLUGIN_ROOT for referencing bundled scripts, binaries, and config files that ship with the plugin.
Example: a host loading the plugin devtools from /home/alex/.agents/plugins/devtools sets:
PLUGIN_ROOT=/home/alex/.agents/plugins/devtools
PLUGIN_DATA=/home/alex/.agents/plugins/data/devtools
Hosts that provide PLUGIN_ROOT MUST expand ${PLUGIN_ROOT} in supported configuration fields. Hosts that provide a persistent data directory SHOULD expand ${PLUGIN_DATA}.
Generic environment variable interpolation (e.g., ${HOME}) is not required by this spec. Hosts MAY support it.
Expansion applies to runtime configuration values — executable paths and environment-bearing strings — not to manifest discovery path fields. Manifest discovery paths (§6.4) are always relative ./ paths resolved from the plugin root.
| Location | Fields | Description |
|---|---|---|
| MCP config | command, args, env, cwd |
All string values support plugin-root expansion. |
Hosts MAY provide additional environment variables beyond the plugin root variables.
Example: ${PLUGIN_ROOT} expansion in MCP
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["--config", "${PLUGIN_ROOT}/config/db.json"],
"cwd": "${PLUGIN_ROOT}",
"env": {
"DATA_DIR": "${PLUGIN_ROOT}/data"
}
}
}
}Plugins SHOULD use Semantic Versioning for version.
| Segment | Meaning | Description |
|---|---|---|
| Major | Breaking change | Incompatible behavior or schema change. |
| Minor | Backward-compatible feature | New behavior without breaking existing hosts or users. |
| Patch | Backward-compatible fix | Corrective change without intended behavioral break. |
Hosts MAY use version to determine whether updates are available and whether caches are stale.
A host is conformant to Open Plugin v1 if it:
- Can load a plugin from a directory path.
- Parses
.plugin/plugin.json. - For each core component type it supports (skills, MCP servers), discovers components in default locations.
- Respects manifest-declared discovery paths for supported component types.
- If the host launches plugin subprocesses (e.g., MCP servers), expands
${PLUGIN_ROOT}in runtime configuration values (command,args,env,cwd). - Supports at least one core component type (skills or MCP servers).
Support for a vendor-prefixed manifest location such as .<tool-name>-plugin/plugin.json is supplemental. A host that defines such a location MUST still support .plugin/plugin.json.
Example: a skills-only host is conformant. It only needs to:
1. Accept a plugin directory path.
2. Read .plugin/plugin.json for the plugin name.
3. Scan skills/ for SKILL.md files (default location discovery).
4. Respect manifest-declared skill paths.
A host that only supports skills — and ignores MCP servers, commands, agents, rules, hooks, LSP servers, and output styles — is fully conformant to Open Plugin v1 as long as it meets all six requirements above.
Support for extended component types (commands, agents, rules, hooks, LSP servers, output styles) is OPTIONAL. See Appendix D: Extended Component Types.
A host is not required to support every component type. Incremental adoption is conformant.
- Hosts MUST ignore unsupported component types.
- Hosts MUST continue loading a plugin when a component fails independently, such as an MCP server startup failure, unless the host explicitly treats that failure as fatal for that component only.
- Hosts SHOULD warn when configuration is invalid, conflicting, or partially unsupported.
This checklist is for convenience only — when it conflicts with the spec text above, the spec governs.
- Parse
.plugin/plugin.json(§5.1) - Support vendor-prefixed manifest locations if applicable (§5.1)
- Validate plugin name against naming constraints (§6.7)
- Reject paths that escape the plugin root via
../(§4.1)
- Scan default locations for each supported component type (§7.1)
- Respect manifest-declared discovery paths (§6.4, §7.2)
- Ignore missing default directories without error (§7.2)
- SHOULD namespace components as
{plugin-name}:{component-name}(§9.1) - SHOULD include both plugin name and server name in MCP tool identifiers (§9.2)
- If the host launches plugin subprocesses, provide
PLUGIN_ROOTenvironment variable (§10.1) - If the host provides
PLUGIN_ROOT, expand${PLUGIN_ROOT}in MCP servercommand,args,env,cwdfields (§10.2)
- Ignore unsupported component types (§12.3)
- Continue loading when optional components fail (§12.3)
- Support at least one core component type (§12.1)
This table consolidates the failure-handling behaviors defined throughout the spec into a single reference. All behaviors listed below restate existing requirements — no new requirements are introduced. Host implementers can use this matrix to verify that their diagnostic output covers every specified failure site.
| Decision point | Existing spec behavior | Human-readable example | JSON example | Fatal? | Verification |
|---|---|---|---|---|---|
| Inconsistent manifest content | Hosts MAY warn when multiple manifest locations contain inconsistent content (§5.2) | WARN open-plugin: plugin "devtools" has inconsistent manifests across .plugin/plugin.json and .claude-plugin/plugin.json; using .claude-plugin/plugin.json |
{"level":"warn","event":"open_plugin.manifest.inconsistent","plugin":"devtools","selected":".claude-plugin/plugin.json","other":".plugin/plugin.json","action":"used_selected"} |
No | Check that the host selects one manifest and continues |
| Invalid ambiguous object field | Hosts SHOULD treat unrecognized object shapes as invalid and SHOULD warn (§6.6) | WARN open-plugin: plugin "devtools" manifest field "mcpServers" is invalid: expected either a path config with "paths" key or an inline config with "mcpServers" key; field ignored, plugin load continues |
{"level":"warn","event":"open_plugin.manifest.invalid_object","plugin":"devtools","field":"mcpServers","action":"ignored","continue":true} |
No | Check that the invalid field is skipped and remaining components load |
| MCP server startup failure | If a server fails to start, the host SHOULD log the error and continue loading other components (§8.2.2) | ERROR open-plugin: plugin "devtools" MCP server "database" failed to start: connection refused on port 5432. Other plugin components remain available. |
{"level":"error","event":"open_plugin.mcp.start_failed","plugin":"devtools","server":"database","error":"connection refused on port 5432","action":"continue_without_mcp"} |
No | Check that other components still load |
| MCP server name conflict | Hosts SHOULD warn and SHOULD resolve conflicts deterministically. Conflicting names MUST NOT crash plugin loading (§8.2.2) | WARN open-plugin: plugin "devtools" MCP server name "filesystem" defined in multiple config files; using first definition |
{"level":"warn","event":"open_plugin.mcp.name_conflict","plugin":"devtools","server":"filesystem","action":"used_first"} |
No | Check that one definition wins deterministically and loading continues |
| Unsupported component type | Hosts MUST ignore unsupported component types (§12.3) | INFO open-plugin: plugin "devtools" declares component type "agents" which is not supported by this host; ignored |
{"level":"info","event":"open_plugin.host.unsupported_component","plugin":"devtools","component_type":"agents","action":"ignored"} |
No | Check that supported components still load |
| Partial host support | Hosts SHOULD warn when configuration is invalid, conflicting, or partially unsupported (§12.3) | WARN open-plugin: plugin "devtools" is partially supported: this host supports skills and hooks but not mcpServers or lspServers |
{"level":"warn","event":"open_plugin.host.partial_support","plugin":"devtools","supported":["skills","hooks"],"unsupported":["mcpServers","lspServers"],"action":"loaded_partial"} |
No | Check that supported components are functional |
Implementer note: The
eventfield values above (e.g.,open_plugin.manifest.invalid_object) are suggested stable identifiers — not required by this spec. Hosts that adopt them gain a machine-readable diagnostic surface that agents, CI pipelines, and plugin validators can consume deterministically. The recommended fields for every diagnostic record are:level,event,plugin(plugin name), the relevant component identifier (e.g.,server,field,hook_event), andaction(what the host did in response).
This section explains why key design choices were made. It is for context only — the binding rules are in sections 1–12 above.
Plugins use filesystem directories as the package unit rather than archive formats (.zip, .tar.gz) or registry-fetched bundles. This keeps plugins inspectable with standard tools (ls, cat, git), editable in-place during development, and compatible with version control without special tooling.
The plugin-name:component-name format was chosen because colons are visually distinct, rarely appear in filenames, and align with existing conventions in tools like Claude Code's slash commands (/plugin:command). Alternatives considered included / (conflicts with filesystem paths) and __ (less readable for user-facing identifiers).
MCP tool identifiers are consumed by language models, which may tokenize or interpret colons and slashes unpredictably. The mcp__plugin_{plugin}_{server}__{tool} format uses only characters that models handle reliably. The double-underscore separators provide unambiguous parsing boundaries even when plugin or tool names contain single underscores.
Every conformant host MUST check .plugin/plugin.json as the vendor-neutral manifest location (§5.1). This gives plugin authors a single guaranteed path that works across all hosts without vendor-specific knowledge. Vendor-prefixed manifests (e.g., .claude-plugin/plugin.json) are supplemental overrides — they let a plugin customize behavior for a specific host, but a plugin that ships only .plugin/plugin.json is portable by default. Making the vendor-neutral path mandatory and vendor-prefixed paths optional keeps the ecosystem interoperable while allowing per-host specialization where needed.
A metadata directory (.plugin/) allows the manifest to coexist with future metadata files (e.g., marketplace.json, lock files) without polluting the plugin root with multiple dotfiles. A single well-known directory is also easier for hosts to check than scanning for multiple dotfile patterns.
Hook commands, MCP server arguments, and LSP configurations often need absolute paths at runtime. Relative paths from the config file location would be ambiguous when configs are loaded from different directories. ${PLUGIN_ROOT} provides an unambiguous, host-resolved anchor that works regardless of the current working directory.
When an MCP server fails to start, an LSP binary is missing, or a hook command exits non-zero, the host continues loading the remaining components (§12.3). This design reflects the reality that plugins bundle heterogeneous components with different runtime dependencies — a plugin that provides skills, hooks, and an MCP server should not become entirely unusable because one server's port is occupied. Non-fatal failures also make plugins more resilient in constrained environments (CI runners, containers, minimal installs) where not every runtime dependency is available. The spec pairs this with diagnostic requirements so that failures are visible rather than silent.
Everything in this section is deferred from v1.0.0. Nothing here is required for conformance. These items may be addressed in future versions.
v1.0.0 does not define a trust model, permission system, or sandboxing requirements for plugins. A future version should address:
- Permission declarations in the manifest (e.g., filesystem access, network access, tool access)
- Host-enforced capability restrictions per plugin
- User consent flows for plugin installation and capability grants
- Approval UX for hooks and MCP servers that execute arbitrary commands or access external services
- Graduated trust levels (e.g., "sandboxed", "user-approved", "organization-approved")
v1.0.0 does not specify how hosts or users can verify the origin or integrity of a plugin. A future version may define:
- Cryptographic signature verification for published plugins
- Attestation chains linking a published plugin to its source repository and build
- Host-side policies for requiring signatures from trusted publishers
Plugin components (hooks, MCP servers, LSP servers) often need credentials or API keys at runtime. v1.0.0 does not specify how sensitive values should be provided, stored, or scoped. A future version may define:
- A
secretsmanifest field or separate secrets configuration - Host-mediated secret injection that avoids plaintext in config files
- Scoping rules that prevent one plugin from accessing another plugin's secrets
- Rotation and revocation semantics for plugin-held credentials
Organizations deploying plugins at scale need policy enforcement that v1.0.0 does not address. A future version may define:
- Allowlist and blocklist policies for plugin installation by name, publisher, or signature
- Organization-scoped plugin registries with approval workflows
- Centralized configuration overrides that take precedence over user-level plugin settings
- Compliance reporting hooks for plugin installation and usage events
v1.0.0 defines diagnostic events for failure sites but does not standardize lifecycle audit events. A future version may define:
- A standard event schema for plugin install, enable, disable, update, and uninstall actions
- Recommended fields: timestamp, actor (user or automation), plugin name, plugin version, action, outcome
- Integration points for forwarding audit events to external logging or SIEM systems
- Retention and access policies for audit records
Plugins currently cannot declare dependencies on other plugins. A future version may define:
- A
dependenciesmanifest field with version constraints - Resolution order and conflict handling for transitive dependencies
- Peer dependency semantics for shared components
v1.0.0 assumes plugins are source-distributable (scripts, markdown, JSON configs). A future version may address:
- Precompiled binary distribution for MCP and LSP servers
- Platform-specific binary selection
- Integrity verification for binary artifacts
Some hosts support a channels manifest field for message channel injection (e.g., Telegram, Slack, Discord style integrations) bound to MCP servers. This feature is host-specific and too specialized for v1 core. A future version may define:
- A
channelsmanifest field with per-channel MCP server bindings - Per-channel user configuration for credentials and settings
- A standard protocol for channel message injection into conversations
The outputStyles field is defined only at the discovery level. A future version should specify the runtime format and rendering contract for output style resources.
No test harness or validation tool is specified. A future version may define:
- A
testmanifest field or convention - A standard plugin linter or validator command
- Conformance test suites for host implementations
This appendix is not required for v1 conformance. It describes a marketplace indexing mechanism implemented by some hosts. A future version of the spec may promote this to a required section after cross-tool validation.
Hosts that support marketplaces should search for marketplace.json in this order:
| Path | Priority | Description |
|---|---|---|
marketplace.json |
First | Marketplace root index. |
.plugin/marketplace.json |
Second | Vendor-neutral metadata directory. |
.<tool-name>-plugin/marketplace.json |
Third | Vendor-specific metadata directory. |
The first match wins.
Required fields:
| Field | Type | Description |
|---|---|---|
name |
string | Marketplace identifier. |
plugins |
array | Non-empty array of plugin entries. |
Optional fields:
| Field | Type | Description |
|---|---|---|
owner |
object | Marketplace owner with name and optional email and url. |
metadata |
object | Marketplace-level metadata. |
metadata.pluginRoot |
string | Base path for plugin source resolution. Defaults to .. |
| Field | Type | Description |
|---|---|---|
name |
string | Required plugin name. Must satisfy plugin name constraints. |
source |
string | Required relative path from metadata.pluginRoot or marketplace root. Must start with ./. |
description |
string | Optional marketplace description override. |
version |
string | Optional marketplace version override for update checks. |
author |
object | Optional marketplace author override. |
license |
string | Optional marketplace license override. |
keywords |
string[] | Optional marketplace keywords override. |
skills |
string[] | Optional explicit skill paths relative to the marketplace root. |
For marketplace-level operations such as display, search, and update checks, plugin-entry metadata overrides manifest metadata. Runtime plugin behavior continues to use the plugin manifest and plugin contents.
Example:
{
"name": "acme-plugins",
"owner": { "name": "Acme Corp", "url": "https://acme.example.com" },
"metadata": { "pluginRoot": "./plugins" },
"plugins": [
{
"name": "code-review",
"source": "./code-review",
"description": "Automated code review with security checks.",
"version": "2.1.0",
"keywords": ["review", "security"]
},
{
"name": "deploy-tools",
"source": "./deploy-tools",
"version": "1.0.3",
"license": "Apache-2.0"
}
]
}metadata.pluginRoot defines the base directory for resolving each plugin entry source. If omitted, source is resolved relative to the directory containing marketplace.json.
If no marketplace index is found, hosts should check whether the root directory itself is a plugin, scan immediate subdirectories, and scan one additional level deep.
| Source | Derived name |
|---|---|
GitHub shorthand owner/repo |
owner-repo |
| Git URL | Last two path segments joined by - |
| Local directory path | Directory basename |
This appendix is not required for v1 conformance. It catalogs hook events implemented by existing hosts. Hosts MAY support any subset of these. Plugin authors should check host documentation for supported events. See Appendix D.4 for the hook format definition.
| Event | Matcher context | Description | Known hosts |
|---|---|---|---|
UserPromptSubmit |
None | Fires when a user prompt is submitted. | Claude Code |
Stop |
None | Fires when the agent finishes responding. | Claude Code |
StopFailure |
Error type | Fires when a turn ends due to an API error. | Claude Code |
SubagentStart |
Agent type | Fires when a sub-agent starts. | Claude Code |
SubagentStop |
Agent type | Fires when a sub-agent stops. | Claude Code |
PreCompact |
Trigger | Fires before context compaction. | Claude Code |
PostCompact |
Trigger | Fires after context compaction completes. | Claude Code |
TeammateIdle |
None | Fires when a teammate agent is about to idle. | Claude Code |
TaskCreated |
None | Fires when a task is created. | Claude Code |
TaskCompleted |
None | Fires when a task is marked completed. | Claude Code |
Notification |
Notification type | Fires when the host sends a notification. | Claude Code |
PermissionRequest |
None | Fires when a permission dialog is shown. | Claude Code |
InstructionsLoaded |
Load reason | Fires when instruction files are loaded. | Claude Code |
ConfigChange |
Config source | Fires when a configuration file changes. | Claude Code |
CwdChanged |
None | Fires when the working directory changes. | Claude Code |
FileChanged |
Filename | Fires when a watched file changes on disk. | Claude Code |
WorktreeCreate |
None | Fires when a worktree is being created. | Claude Code |
WorktreeRemove |
None | Fires when a worktree is being removed. | Claude Code |
Elicitation |
MCP server name | Fires when an MCP server requests user input. | Claude Code |
ElicitationResult |
MCP server name | Fires when a user responds to an MCP elicitation. | Claude Code |
As additional hosts adopt the Open Plugin format, this table will be updated with cross-host event support information. Events supported by multiple hosts are candidates for promotion to the core set in future spec versions.
This appendix is not required for v1 conformance. It defines component types that hosts MAY support beyond the core skill and MCP server types. These formats are based on conventions established by existing hosts but are not yet backed by independent open standards. A future version of the spec may promote some of these to core component types.
Command skills are markdown files discovered from commands/.
- The filename without extension is the command name.
- Command files MAY contain YAML frontmatter.
| Field | Type | Description |
|---|---|---|
description |
string | Short description for display and matching. |
disable-model-invocation |
boolean | If true, require explicit user invocation. Defaults to false. |
The markdown body is the command instruction body. The placeholder $ARGUMENTS in a command body is replaced with user-provided text after the command name.
Agent definitions are markdown files with YAML frontmatter discovered from agents/.
| Field | Type | Description |
|---|---|---|
name |
string | Required agent identifier (1-64 chars, lowercase alphanumeric and hyphens). |
description |
string | Required description (max 1024 characters). |
The markdown body after frontmatter is the agent system prompt.
Rule files are markdown with YAML frontmatter discovered from rules/. The default extension is .mdc.
| Field | Type | Description |
|---|---|---|
description |
string | Required summary of the rule. |
alwaysApply |
boolean | If true, the rule is active automatically. Defaults to false. |
globs |
string | string[] | Optional file glob or glob list limiting rule applicability. |
The markdown body is the rule text injected when the rule is active.
The default hook configuration path is hooks/hooks.json. Hooks MAY also be declared inline in the manifest hooks field.
The hook configuration contains a top-level hooks object. Each event key maps to an array of hook rules.
| Field | Type | Description |
|---|---|---|
matcher |
string | Optional regular expression matched against event-specific context. |
hooks |
array | Required list of hook actions. |
Core hook events: PreToolUse, PostToolUse, PostToolUseFailure, SessionStart, SessionEnd. See Appendix C: Extended Hook Events for additional events.
Hook action types: command (shell command), http (POST to URL), prompt (LLM evaluation), agent (agentic verifier).
The default LSP configuration path is .lsp.json. LSP servers MAY also be declared inline in the manifest lspServers field. The top-level object uses direct server-name keys.
Required fields: command (executable on $PATH), extensionToLanguage (file extension to language ID mapping).
Optional fields: args, transport, env, initializationOptions, settings, workspaceFolder, startupTimeout, shutdownTimeout, restartOnCrash, maxRestarts.
outputStyles is a discovery field for host-defined output style resources. This specification does not define output style runtime semantics beyond discovery.