Step-by-step guide
Package a complete capability
Turn a useful extension into something another team can understand and adopt.
What this changes
A plugin brings related behavior together as one reviewable capability. It is the right shape when knowledge, integrations and lifecycle policy need to travel as a coherent unit.
-
01
One adoption surface
Teams can understand the capability without assembling scattered pieces.
-
02
Explicit trust
What the extension contributes remains visible before it is enabled.
-
03
Independent evolution
Improve the capability without changing the core runtime.
Minimum setup
Start with one working configuration.
Place plugin.json at the root of a named directory, keep its related assets beside it, then enable that exact name in settings. Installing and enabling are separate acts.
~/.clarvis/plugins/repo-hygiene/
├── plugin.json
└── skills/
└── house-style/
└── SKILL.md
# ---- ~/.clarvis/plugins/repo-hygiene/plugin.json ----
{
"name": "repo-hygiene",
"version": "0.1.0",
"description": "House conventions, as a skill the agent can load."
}
# ---- ~/.clarvis/plugins/repo-hygiene/skills/house-style/SKILL.md ----
---
name: house-style
description: How code is written in this repository. Load before editing source.
---
Keep functions under 40 lines. Name tests after the behaviour they pin down.
The listing above is the plugin directory only. Installing it does nothing until you add its exact name to enabledPlugins in ~/.clarvis/settings.json, which is a separate file outside the plugin. A plugin can contribute skills, agents, hooks, tool servers, and capability providers. It cannot declare guard or sandbox settings, so an extension cannot weaken the adopter’s runtime boundary.
Working patterns
Adapt it to work you actually do.
Open a pattern to see the complete files and the reason behind each choice.
Pattern 01 A skills-only plugin Configuration +
You want to package a methodology as skills and have one of them framed into the system prompt of every agent that holds the use_skills grant, without the plugin ever running code or asking anyone to approve anything. An agent without that grant sees no skills section at all.
superpowers/
├── plugin.json
└── skills/
├── using-superpowers/SKILL.md
├── brainstorming/SKILL.md
└── writing-plans/SKILL.md
# ---- superpowers/plugin.json ----
{
"name": "superpowers",
"version": "6.2.0",
"description": "Brainstorming, planning, TDD, debugging and code review, as skills.",
"author": "Your Name",
"bootstrapSkill": "using-superpowers"
}
# ---- superpowers/skills/using-superpowers/SKILL.md ----
---
name: using-superpowers
description: Read this before responding. How the other skills in this set fit together.
---
Start from brainstorming. Write the plan before the code. Test first.
# ---- ~/.clarvis/settings.json ----
{
"enabledPlugins": ["superpowers"]
}
Pattern 02 A plugin that contributes a server, an agent, and a plans provider Configuration +
You want the agent to reach a private note store through MCP, ship an agent profile that uses it, and additionally offer an external plans service that the operator can select. Note the asymmetry in the two commands below. A capabilityExecutable runs with the plugin's own install directory as its working directory, so providers/plans.py resolves relative to the plugin. A contributed mcpServer does not: it inherits the workspace root, there is no plugin-root variable to interpolate into args, and the settings schema has no cwd key. Its entry point has to be reachable from anywhere, so it is given as an absolute path here; a command on PATH works equally well.
notes-kit/
├── plugin.json
├── agents/
│ └── librarian.md
├── server/
│ └── notes.ts
└── providers/
└── plans.py
# ---- notes-kit/plugin.json ----
{
"name": "notes-kit",
"version": "1.0.0",
"description": "A note store the agent can query, plus an external plans service.",
"mcpServers": {
"notes": {
"type": "stdio",
"command": "bun",
"args": ["run", "/opt/notes-kit/server/notes.ts"],
"env": { "NOTES_DIR": "${HOME}/notes" },
"resources": false
}
},
"capabilityExecutables": {
"plans": {
"command": "python3",
"args": ["-B", "providers/plans.py"],
"env": { "PLANS_TOKEN": "${PLANS_TOKEN}" },
"platforms": {
"win32": { "command": "py", "args": ["-3", "providers/plans.py"] }
},
"timeout_ms": 30000
}
}
}
# ---- notes-kit/agents/librarian.md ----
# Becomes the agent named notes-kit:librarian
---
description: Answers questions from the note store and nothing else.
tools:
- notes.search
- notes.read
---
Answer only from the notes. If the notes do not say, say so.
# ---- ~/.clarvis/settings.json ----
# Enabling turns on the server and the agent. The plans service stays inert
# until you also select it, which the plugin cannot do for itself.
{
"enabledPlugins": ["notes-kit"],
"plans": {
"provider": { "kind": "plugin", "plugin": "notes-kit" }
}
}
Verify the behavior
Check the boundary, not just the happy path.
Use a small disposable task first. Confirm what works and what is deliberately unavailable before relying on the configuration in a real workflow.
- 01
The directory name and plugin.json name match exactly.
- 02
Nothing contributed by the plugin appears until its name is in enabledPlugins.
- 03
Executable contributions and hooks remain visible for review instead of being silently trusted.