Writing a plugin
1
Create the directory and manifest
.kova-plugin/plugin.json:2
Decide which layers you need
skills/ contributes skills (the agent knows how), panels.json registers panels
(a human can open it), and .mcp.json contributes an MCP server (the agent can call
it). One layer, or all three.3
Register it in the marketplace
Add an entry to
plugins/marketplace.json and it shows up in the in-app marketplace.4
Build artifacts and verify
build:sidecar / test / smoke fill in what is missing.docs/plugin-system-design.md in the repository.
Connecting an MCP server
Seedocs/mcp-design.md. The essentials:
- Multi-server connection pool — several MCP servers connect concurrently without blocking each other;
- Two-layer config merge — global config merges with workspace config, workspace winning;
- Output guards — MCP output is treated as untrusted input and injected content is isolated and marked;
- OAuth — supported for servers that require authorisation;
- Approval and audit — MCP tool calls go through the same approval flow as any other tool, and leave an audit trail.
mcpServers
field — ui-design does exactly this, which is how the agent can drive a design canvas
directly.
Adding a skill
Skills live in a plugin’sskills/ directory or come from user-level configuration. A
skill is a knowledge pack loaded into the agent’s context, loaded and managed under
Settings → Skills.
The bundled iOS / Material / generic mobile design-system packs are shipped exactly this
way and load on demand through use_skill, rather than crowding every context.
Adding a built-in tool
Tools live inapps/sidecar/pi-agent/src/tools/ and are registered in tools.ts. When
you add one, you must decide its permission behaviour:
When you change the permission enums, remember to update the enumeration-expansion
checklist in docs/permission-modes.md — not for tidiness, but to keep the enums and
the UI options from drifting apart.
The NDJSON protocol and cross-surface contract
packages/pi-protocol defines the contract shared by frontend and backend. The
desktop frontend, the sidecar, and the mobile app all take their types from here.
Change the protocol there, not in one surface’s private copy.The desktop host and sidecar speak a one-JSON-object-per-line protocol. Business
table access rides host RPC on stdout, so logs and protocol frames are explicitly
separated on the channel.
/ws
clients at once, so any change has to account for multiple surfaces being online.
Rebranding
This is a template repo, so putting your own brand on it is one command:pi-desktop id,
the Tauri display name and bundle id, and .kova-plugin directory names — and backs
everything up before changing it.
After renaming, remember to update the identifier in
apps/desktop/src-tauri/tauri.dev.conf.json. That identifier is what separates dev
from release, and scripts are easy to miss it.Next
How to package and ship a release.
