Skip to content

plugin.json Manifest Reference

The plugin.json file is the only required file in a plugin. It declares what the plugin provides and how Recursive should load it.

{
"id": "my-plugin",
"name": "My Plugin",
"version": "0.1.0",
"description": "What this plugin does."
}
FieldTypeRequiredDescription
idstringYesUnique plugin identifier. Kebab-case, no spaces. Used in file paths and tool prefixes.
namestringNoHuman-readable display name. Falls back to id if omitted.
versionstringNoSemver version (e.g., "1.2.3"). Used for update checks.
descriptionstringNoOne-line description shown in the plugin list.
authorstring | { name: string }NoAuthor attribution.
colorstringNoHex color for the plugin’s icon badge (e.g., "#8b5cf6").
iconstringNoIcon name from the Recursive icon set.
thumbnailstringNoRelative path to a thumbnail image for the plugin detail panel.

Each of these fields points to a directory or file that Recursive loads. All paths are relative to the plugin root.

FieldTypeDescription
toolsstring | string[]Directory or files containing MCP tool handlers.
routesstring | string[]Directory or files containing HTTP route handlers.
adaptersstring | string[]Directory or files containing agent adapter implementations.
skillsstring | string[]Directory containing skill subdirectories (each with a SKILL.md).
rulesstring | string[]Directory or files containing agent rule markdown.
workflowsstring | string[]Directory or files containing workflow definitions.
automationsstring | string[]Directory or files containing automation definitions.
hooksstring | string[]Directory or files containing lifecycle hook handlers.
mcpstring | string[]External MCP server configuration files.

The ui field is an object that declares frontend components and behaviors.

Array of view definitions that add new pages to the dashboard.

{
"ui": {
"views": [
{
"id": "my-view",
"label": "My View",
"icon": "layers",
"route": "my-view",
"component": "./ui/MyView.svelte",
"detailPanel": "./ui/MyDetailPanel.svelte"
}
]
}
}
FieldTypeDescription
idstringUnique view identifier.
labelstringDisplay name in navigation.
iconstringIcon name.
routestringURL route segment (e.g., my-view/my-view).
componentstringPath to the main Svelte component.
detailPanelstringOptional path to a detail panel component.

Array of navigation entries added to the sidebar.

{
"ui": {
"nav": [
{
"route": "my-view",
"label": "My View",
"icon": "layers",
"shortcut": "Mod+5",
"order": 50,
"surface": "sidebar.nav.my-view"
}
]
}
}

Array of command palette entries.

{
"ui": {
"commands": [
{
"id": "nav-my-view",
"label": "Go to My View",
"icon": "layers",
"section": "Navigation",
"type": "navigate",
"route": "my-view",
"shortcut": "Mod+5",
"keywords": ["custom", "view"]
}
]
}
}

Panels added to the workspace detail area (alongside file viewer, preview, etc.).

Panels added to the task detail view.

Custom renderers for specific tool call results in the chat feed.

{
"ui": {
"chatRenderers": [
{
"kind": "goal_create",
"component": "./ui/GoalCreateRenderer.svelte"
}
]
}
}

Register custom entity types for mention resolution and search.

{
"ui": {
"entityTypes": [
{
"type": "goal",
"storeKey": "allGoals",
"nameKey": "title"
}
]
}
}

SSE event channels that the plugin’s store should listen to.

Path to a TypeScript store module that manages the plugin’s frontend state.

Map of named components that other parts of the UI can reference.

Plugin configuration with defaults and a schema for the settings UI.

{
"settings": {
"defaults": {
"api_key": "",
"enabled_features": ["basic"]
},
"schema": [
{
"key": "api_key",
"type": "string",
"label": "API Key",
"description": "Your service API key.",
"section": "Authentication"
},
{
"key": "enabled_features",
"type": "multi-select",
"label": "Features",
"options": ["basic", "advanced", "experimental"],
"section": "Features"
}
]
}
}

Auto-detection rules that enable the plugin when a project matches.

{
"detect": {
"files": ["package.json", "tsconfig.json"],
"strategy": "all"
}
}
FieldTypeDefaultDescription
filesstring[]File patterns to check in the project root.
strategy"any" | "all""any"Whether any file match suffices or all must be present.

Server-side lifecycle event handlers. Keys are event names, values define the handler.

{
"lifecycle": {
"session.complete": {
"handler": "./hooks/on-session-complete.ts",
"priority": 10,
"type": "action"
}
}
}

Client-side lifecycle event handlers. Same structure as lifecycle but handlers run in the browser.

{
"client_lifecycle": {
"session.complete": {
"handler": "./hooks/client-on-complete.ts",
"priority": 10
}
}
}