Step-by-step guide

Package a complete capability

Turn a useful extension into something another team can understand and adopt.

You will build
An enabled plugin that ships reusable capabilities.
Time
15 minutes
Before you start
A capability worth sharing as one unit.

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.

  1. 01
    One adoption surface

    Teams can understand the capability without assembling scattered pieces.

  2. 02
    Explicit trust

    What the extension contributes remains visible before it is enabled.

  3. 03
    Independent evolution

    Improve the capability without changing the core runtime.

01

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
~/.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.
Why this is the minimum

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.

02

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.

A skills-only plugin example
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.

A plugin that contributes a server, an agent, and a plans provider example
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" }
  }
}
03

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.

  1. 01

    The directory name and plugin.json name match exactly.

  2. 02

    Nothing contributed by the plugin appears until its name is in enabledPlugins.

  3. 03

    Executable contributions and hooks remain visible for review instead of being silently trusted.

Next guide

Publish it in a team catalog

Let teammates discover supported plugins without passing repository URLs around.