Plugins

Plugins are how you extend Koris without touching its core. There are three plugin families — channels, tools, and MCP servers — that share the same shape and the same kernel, described below. plugins/ also holds skills, but skills are a different kind of extension — plain Markdown, synced by SkillSyncService rather than the kernel this page describes.

The kernel

plugins/registry.ts holds the family-agnostic core: ExtensionPoint, PluginRegistry, and buildRegistry. All three families build on it. At startup core/src/app.ts builds one shared PluginRegistry from the channel plugins, tool plugins, and MCP server plugins together.

Dependency inversion

A plugin imports only from its own family's contracts.ts and the shared plugins/registry.ts. It never imports from core/src/, and never from another family's contracts.ts.

Core depends on the SDKs and injects concrete services the other way, through a PluginContext (channels), ToolPluginContext (tools), or McpPluginContext (mcps) assembled at the composition root, core/src/app.ts. Each plugin folder exposes a create(context) function that receives that context.

The one documented exception is plugins/tools/create-tool/, which reaches into scripts/scaffold-tool.ts to generate new tool folders.

Discovery

The scanner loads every subdirectory of a family folder — plugins/channels/, plugins/tools/, and plugins/mcps/. Files that sit directly in those folders, such as contracts.ts, are skipped. Adding a plugin means adding a folder; for tools, pnpm scaffold:tool <name> creates one for you. No core changes are required. (plugins/skills/ sits alongside these, but isn't scanned here — see Skills for how it's synced.)

On/off state

Enablement lives in the database, in the plugin_settings table: family, name, and enabled, with a primary key of (family, name). resolvePluginEnabled reads the row and falls back to a code-level default when no row exists yet.

Toggling is live — no restart. PluginCatalogSingleton keeps every registered { family, name } pair so the admin API can list plugins without rescanning disk. See the admin dashboard for the Plugins panel.

Channel and MCP plugins still keep their secrets and endpoint configuration in each folder's config.yml; only the enabled flag moved to the database.