0007 — Plugin protocol design
Source:
docs/decisions/0007-plugin-protocol.mdis the canonical artefact. This page is a short summary; read the full ADR for the four alternatives considered, the ten binding decisions, and the implementer’s notes.
- Status: Accepted
- Date: 2026-05-16
- Deciders: Thomas Lennon (maintainer),
mcp-architect
Context
Section titled “Context”The plan defines the plugin protocol in one paragraph: every directory under plugins/ ships a plugin.yaml manifest declaring name, type, version, neurotype tags, and trust level; the substrate auto-discovers plugins matching the user’s profile. Phase 3 promises a federated registry at plugins.neurodock.org and language packs for ≥ 3 locales. ADR 0005 flagged that the language-pack manifest schema would need its own ADR before external contributors could land packs. This ADR is that schema.
Three properties make the contract load-bearing: six plugin types across two ecosystems (Python and TypeScript), trust without central authority (air-gapped installs must work), and AGPL license-boundary protection.
Decision
Section titled “Decision”Adopt Option B: dedicated plugin.yaml manifest with JSON Schema validation. Schema lives at packages/core/schemas/plugin.schema.json. Discovery is filesystem-based in v0.1.0; the federated registry indexes the same manifests in Phase 3.
View alternative approaches and technical debates
Alternatives rejected:
- Ad-hoc plugin discovery (no manifest) — can’t express cross-cutting metadata (trust, license, neurotypes, locale).
- Reuse
package.jsonorpyproject.toml— couples manifest to language ecosystems; profiles and language packs have neither. - One global registry file — single-point-of-merge-conflict; breaks the drop-a-directory air-gapped install.
Ten binding design decisions
Section titled “Ten binding design decisions”-
Forward-compatibility is paramount.
additionalProperties: trueat every nesting level. Loaders preserve unknown keys on round-trip. Mirrors ADR 0004. Adding a new plugin type, asset sub-type, optional field, or trust level is additive — no major bump required. -
Four-tier trust ladder.
official— published by the maintainer. Installs without prompting.verified— signed by a contributor whose key is in the maintainer-curated keyring. Installs without prompting (signature verification ships Phase 3).community— signed by the author’s own key. Provenance recorded but not vouched. Prompts the user per profile preference.experimental— unsigned. Substrate refuses by default; user opts in explicitly.
-
Six plugin types (extends plan.md’s five).
skill | mcp-server | profile | translation-pack | language-pack | theme.themeis added in v0.1.0 becausedesign-system-keeperalready needs the format. Adding a new type later is forward-compat: v0.1.0 loaders encountering an unknowntypeMUST skip with a structuredunknown_plugin_typewarning rather than erroring. -
Discovery via filesystem scan. Two roots at init:
<repo>/plugins/*/plugin.yamland the platform’s per-user XDG-style root. No central registry required; the federated registry is opt-in. -
requiresis hard but acyclic. Plugin requirement graph runs Tarjan’s strongly-connected-components at load. Any SCC with size ≥ 2 is a cycle; the loader refuses every plugin in the SCC. -
provides[].pathpaths are sandboxed. Resolved against the plugin’s absolute directory; rejected if the resolved path escapes the directory, follows a symlink out, or is absolute/Windows-drive-prefixed. Validation runs at manifest load before any asset is opened. -
License compatibility is enforced at load. SPDX whitelist:
AGPL-3.0-or-later,AGPL-3.0-only,GPL-3.0-or-later,GPL-3.0-only,LGPL-3.0-or-later,MIT,Apache-2.0,BSD-3-Clause,BSD-2-Clause,ISC,MPL-2.0,CC0-1.0. Plugins with any other value refuse to load withlicense_not_allowed.LicenseRef-*is not accepted in v0.1.0. -
Signature mechanism is reserved in v0.1.0; verified in Phase 3. The schema reserves
trust.signatureandtrust.keyring_fingerprint. v0.1.0 loaders store the signature on round-trip but do not verify it. -
Hooks are optional and refuse-by-default in v0.1.0.
on_installandon_uninstallare paths to scripts inside the plugin directory. Hook execution refuses unlesstrust.level in {official, verified}. The Phase 3 sandbox allow-list (read-only FS inside the plugin directory; no network; allow-listed executables) is sketched but deferred. -
Profile composability decides auto-activation. A plugin’s
neurotypesarray intersects withprofile.identity.neurotypes:- Empty plugin
neurotypes(or omitted) → neurotype-agnostic → auto-activate. - Non-empty intersection → auto-activate.
- Empty intersection → installed-but-not-activated; user can enable manually.
For language-pack and translation-pack,
localeintersects similarly. Profile remains the single consent surface. - Empty plugin
Cross-cutting rules established here
Section titled “Cross-cutting rules established here”- Round-trip preservation. YAML write paths use comment-preserving libraries (
ruamel.yaml; equivalent in TS). Read paths may use simpler libraries. - Versioning posture. Patch and minor bumps within
v0.1.xMUST be additive-only. Renames, value removals, required-field additions are major bumps at/v1.0.0/.... - Vendor neutrality. Plugins of
type: mcp-serverMUST NOT bundle vendor SDKs. - Structured errors, never silent. Every loader failure has a stable error code:
unknown_plugin_type,plugin_requirement_cycle,license_not_allowed,path_sandbox_violation,hook_sandbox_violation,signature_invalid.
Open questions
Section titled “Open questions”The full ADR carries six open questions:
- Signing key management for
verifiedplugins. (Recommended: per-author keys with maintainer-curated keyring.) - Plugin update mechanics. (Recommended: user-driven in v0.1.0; opt-in substrate-driven in Phase 3. No automatic updates without prompt.)
- Marketplace governance at
plugins.neurodock.org. (Recommended: registry indexing opt-in, curation-free at v0.1.0.) - Should
themeship in v0.1.0 or defer? (Recommended: ship in v0.1.0; adding later is forward-compat but cleaner to list now.) extends:for plugins inheriting from a base manifest. (Recommended: no in v0.1.0; revisit if a clear pattern emerges.)- Cross-package version coupling at
requires.substrate_version. (Recommended: confirm that minor substrate bumps are additive so">=0.1.0"is the right shape for almost every plugin.)
What’s next
Section titled “What’s next”- Read the full ADR.
- Plugins concept for the higher-level framing.
- Plugin manifest reference for the v0.1.0 schema.
- Write a plugin for the contributor on-ramp.