Plugin Development¶
GridBear has five plugin types, all discovered from manifest.json at boot:
| Type | Implements | Example |
|---|---|---|
| channel | core/interfaces/channel.py::BaseChannel |
Telegram, Discord, WhatsApp, webchat |
| service | core/interfaces/service.py::BaseService |
memory, sessions, attachments, voice |
| runner | core/interfaces/runner.py::BaseRunner |
Claude (CLI and API), OpenAI, Gemini, Ollama |
| mcp | core/interfaces/mcp_provider.py::BaseMCPProvider |
Gmail, Google Workspace, GitHub, Playwright |
| theme | core/interfaces/theme.py::BaseTheme |
Nordic (default), Tailadmin, Enterprise |
Minimal manifest¶
{
"name": "my-plugin",
"version": "0.1.0",
"type": "service",
"description": "What this plugin does in one sentence.",
"entry_point": "service.py",
"class_name": "MyPluginService",
"dependencies": {
"required": ["memory"],
"optional": ["gmail"]
},
"provides": "my-capability"
}
type— decides which interface theclass_namemust implementdependencies— can also be a flat list[…], treated as all-requiredprovides— capability name; another plugin declaring the sameprovidesreplaces this one. Useful for swapping in alternative implementations (e.g.memoryvs a vector-store-backed alternative)
Layout conventions¶
A plugin is fully self-contained. All its files live under its own directory:
plugins/my-plugin/
├── manifest.json
├── service.py # the class referenced by entry_point + class_name
├── admin/
│ ├── routes.py # FastAPI router exposed to the admin UI
│ ├── menu.json # sidebar entry metadata
│ └── templates/ # Jinja templates, referenced by filename alone
├── api/
│ └── routes.py # optional internal REST endpoints
├── portal/
│ └── routes.py # optional user-portal routes under /me/...
├── skills/ # optional markdown skills surfaced to agents
└── service_connections/ # optional OAuth2 provider configs
Dependency direction¶
plugins/ ──depends on──► core/
plugins/ ──depends on──► ui/ (admin routes only)
core/ ──NEVER imports──► plugins/
ui/ ──NEVER imports──► plugins/ (use interfaces + registry)
If you need plugin functionality in core/ or ui/, declare an interface under core/interfaces/ and have your plugin implement it. Consume it from core through core.registry.get_service_by_interface(BaseMyService) — never hardcode plugin names.
Next steps¶
- Copy one of the existing plugins under
plugins/as a starting point —plugins/memois a minimal example - Read through
core/interfaces/<type>.pyfor your plugin type to see the full contract - Add your plugin to
config/plugins.json(or enable it from/plugins/in the admin UI) and restart the container - See the Architecture page for how plugins fit into the message flow