Authoring guide¶
Install¶
pip install minder-plugin-sdk # once published
# or, from source:
pip install "git+https://github.com/minderhq/plugin-sdk"
The smallest plugin¶
A plugin is a class the registry duck-types by the presence of register().
Inherit PluginBase for sensible lifecycle defaults so you only write what you need:
from minder_plugin_sdk import PluginBase, PluginMetadata
class MyPlugin(PluginBase):
__all__ = ["MyPlugin"]
async def register(self) -> PluginMetadata:
return PluginMetadata(
name="my-plugin", version="1.0.0",
description="what it does", author="you",
)
async def collect_data(self) -> dict:
self._last = {"value": 42} # TODO: a real fetch (add httpx to your deps)
return self._last
The registry drives: register() → initialize() → health_check() (60s loop)
→ collect_data() (hourly or on demand) → analyze() → shutdown().
The one gotcha
health_check() must return {"healthy": <bool>, ...} — monitoring reads
health["healthy"]. PluginBase does this for you unless you override it.
Extension points (class attributes)¶
| Attribute | What it does |
|---|---|
CONFIG_SCHEMA |
API-editable config, rendered as a form in the client. |
ACTIONS / READ_ONLY_ACTIONS |
Methods invokable over HTTP (POST /v1/plugins/<name>/actions/<method>). |
AI_TOOLS |
Ollama / OpenAI function-calling tools, mapped to an action. |
DISPLAY |
Branding for the plugin's card: {label, summary, logo, color, category} (logo = a lucide icon name). |
REQUIRES |
{services, optional_services, bundles} the plugin needs from the platform. |
Plugin-driven UI¶
Each config field declares how it renders — the trusted client draws it (a plugin never ships HTML):
CONFIG_SCHEMA = [
{"key": "NOTES", "type": "string", "widget": "textarea", "rows": 4},
{"key": "ENABLED", "type": "bool", "widget": "toggle"},
{"key": "CITY", "type": "string", "widget": "autocomplete",
"options_action": "search_cities"}, # a READ_ONLY action returning options
]
Widgets: text · textarea · code · secret · number · slider ·
toggle · select · multiselect · radio · autocomplete · date ·
datetime · color · file · kv-list. Unknown widgets fall back to a text
input.
Test it¶
The SDK ships a harness:
from minder_plugin_sdk import check_plugin, run_lifecycle
def test_contract():
assert check_plugin(MyPlugin()) == [] # empty ⇒ honours the contract
Or from the CLI: minder-plugin validate my_plugin.py.
See the contract reference for the full API, and publishing to get your plugin into the catalog.