Skip to content
PricingReleases

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.

New plugins must declare manifestFormat: 2 and use grouped v2 keys (runtime, inference, hooks, storage, sync, auth, tools, triggers). The registry loader normalizes grouped keys to the flat fields documented below. See the repo-root VISION.md for the canonical capability taxonomy.

{
"manifestFormat": 2,
"id": "my-plugin",
"name": "My Plugin",
"version": "0.1.0",
"description": "What this plugin does."
}

Unknown fields are ignored with a warning in the plugin’s health panel (typos like skilss get a did-you-mean suggestion); wrong-typed fields are dropped with a warning. Only a missing id prevents the plugin from loading. Validate anytime with the plugin_validate tool.

FieldTypeRequiredDescription
$schemastringNoAgent Plugins schema URL. Present on spec-conformant manifests; ignored by Recursive beyond format detection.
manifestFormatnumberNoMust be 2 for new manifests. Grouped v2 keys are normalized to flat registry fields at load time.
idstringYesUnique kebab-case plugin identifier.
namestringNoDisplay name (falls back to id).
versionstringNoSemver version, compared against the registry for update checks.
descriptionstringNoOne-line summary shown in the plugins UI and registry.
authorstring | objectNoAuthor attribution — a string or { name, url }.
homepagestringNoProject homepage or docs URL.
repositorystringNoSource repository URL (Agent Plugins metadata).
licensestringNoSPDX license identifier (Agent Plugins metadata).
keywordsstring[]NoDiscovery keywords (Agent Plugins metadata); merged with tags in the marketplace.
colorstringNoColour for the icon badge and any mode the plugin contributes. A hex literal, or a theme token such as var(—yellow) to follow the active theme.
iconstringNoIcon name from the Recursive icon set.
thumbnailstringNoPath to a thumbnail image within the plugin.
categorystringNoMarketplace category.
tagsstring[]NoMarketplace search tags.
appScopestringNoLegacy app/domain label used for discovery and grouping; it never gates plugin installation or provider boot.
privatebooleanNoExcluded from registry publishing.
platformsstring[]NoOS allowlist (e.g. [“darwin”]); omit for all platforms.
min_recursive_versionstringNoMinimum Recursive version this plugin supports; enforced at install.
extensionsobjectNoAgent Plugins client extensions, keyed by reverse-domain namespace. Recursive reads “org.recursive”.

Each points to a directory or file (relative to the plugin root) that Recursive loads. A string can be a directory (ending in /) or a single file; an array mixes both. On v2 manifests, use grouped keys such as runtime.operations, inference.adapters.modules, and tools.mcp — they normalize to these flat fields at load time. New product capabilities declare operations; MCP tools project from those contracts. tools.handlers is a legacy compatibility surface.

FieldTypeRequiredDescription
toolsstring | string[]NoLegacy MCP handler modules. New product capabilities use runtime.operations with mcp.exposed.
routesstring | string[]NoHTTP route handler modules.
operationsstring | string[]NoCanonical OperationDefinition modules contributed to the product runtime.
migrationsstringNoDirectory of one-time data migrations ({ id, run }) applied before this plugin’s operations load. Each id is ledgered once per workspace.
adaptersstring | string[]NoAgent CLI adapter modules (JS/TS).
skillsstring | string[]NoSkill directories containing SKILL.md subfolders.
rulesstring | string[]NoAgent rule markdown files.
commandsstring | string[]NoSlash-command Markdown files.
automationsstring | string[]NoAutomation definitions.
hooksstring | string[]NoIDE-native hook JSON path (v2: hooks.ide). Normalized from grouped hooks.ide; not server lifecycle — use hooks.server → lifecycle after normalization.
mcpstring | string[]NoExternal MCP server configuration (JSON).
themesstring | string[]NoTheme JSON files.
soundsstring | string[]NoSound pack audio files (mp3/wav/ogg/m4a/aac/aiff).
greetingsstring | string[]NoSession-greeting catalog JSON files.
providersstring | string[]NoInference-provider catalog JSON. Each approved row becomes a connectable OpenAI-compatible provider, with no adapter code.
commsstring | string[]NoComms templates directory.
FieldTypeRequiredDescription
uiobjectNoUI extensions: views, panels, commands, chat renderers, components.
settingsobjectNoSettings schema and defaults rendered in the plugin settings page.
detectobjectNoProject auto-detection: file patterns that suggest enabling this plugin.
dataobjectNoLogical collections and document stores with device, user, workspace, or project scope plus schema, sync, indexes, and quotas.
datasetobjectNoPortable dataset semantics: declared storage collections plus field roles for generic platform readers.
entitiesobject[]NoLogical entity providers contributed by this plugin. Each declaration binds an entity type to one declared collection or document store and scope; referenceResolver maps ids to human-readable labels through a read operation.
consumesobjectNoKernel or cross-plugin operations this plugin may invoke from contributed server operations.
providesobjectNoStructured provider declarations (sync_provider, auth_provider, data_provider) — each is { id, name, entry, config_schema }; auth_provider contributes a pluggable identity/permission backend (see core-identity provider-registry).
agentobjectNoAgent catalog contributions: optional promoted Skill mode bindings and named agent templates.
triggersobjectNoDeclares the plugin as an event trigger source: module (exports verify/normalize, plus optional respond), optional channelAdapter (provider-neutral durable conversation transport), a non-empty events[] of { type, label, filters, firehose? }, optional setupSkill, setupInstructions, and requiredSettings.
computerProviderobjectNoIsolated computer lifecycle provider normalized from computer.provider.
remoteAccessobjectNoProvider-owned remote connectivity. Core owns authentication and dispatch.
channelSetupobjectNoChannel-instance setup guidance: title, description, guideUrl, and optional guideLabel.
channelConnectionobjectNoChannel connection contract. managedOAuth declares a hosted gateway provider, authorization URL, scopes, identity response paths, and connection-setting mappings.
pluginAccountsobjectNoDeclares which settings keys make up one account so the platform can store, switch, and forget several of them. OAuth plugins get this for free from pluginOAuth.
pluginOAuthobjectNoDirect plugin OAuth contract. The platform starts authorization, exchanges the callback code, stores token settings, and optionally resolves the connected identity.
lifecycleobjectNoServer lifecycle hook handlers keyed by event name (normalized from hooks.server).
client_lifecycleobjectNoClient (renderer) lifecycle hook handlers keyed by event name (normalized from hooks.client).
soundDefaultsRecord<string, string>NoSound packs: notification event → sound slug mapping.
dependenciesstring[]NoPlugin ids this plugin requires.
bundledOptInbooleanNoBundled copy is marketplace-only until explicitly installed.
npmobjectNonpm install behavior — set { “scripts”: true } to allow lifecycle scripts (disclosed at install).

Terminal actions, onboarding & adapter contract

Section titled “Terminal actions, onboarding & adapter contract”
FieldTypeRequiredDescription
loginCommandstringNoCommand that (re)authenticates the plugin’s CLI.
setupCommandstringNoFirst-time configuration command.
diagnoseCommandstringNoHealth-check / repair command.
installCommandstringNoCommand that installs the CLI itself.
mcpLoginCommandstringNoAuthenticate one downstream MCP server through this CLI. Supports a {{server}} placeholder.
mcpDiagnoseCommandstringNoInspect a downstream MCP server’s connection state. Supports a {{server}} placeholder.
installUrlstringNoDocs or download URL for installing the CLI.
cliobjectNoHow to probe whether the CLI is installed (bin, versionArgs).
appobjectNoDesktop-app provider config (name, downloadUrl, healthUrl).
setupSequenceobject[]NoOrdered getting-started steps shown after install.
onboardingobjectNoDeclarative onboarding steps (fields, verify, links, markdown instructions). Trigger-source plugins also get platform-generated copy-URL and await-event steps appended automatically.
adapterManifestobjectNoUniversal adapter runtime contract: capabilities, cli, bridge, instructionDelivery, mcpDelivery, configDelivery, logNoise.

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.

Frontend stores are discovered by convention, not declared: any ui/*-store.ts module in a bundled plugin is auto-registered, its load<Name>() export is called on mount, and SSE events matching the plugin id (or declared via ui.sseEvents) re-invoke it.

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
}
}
}

Declares the plugin as an event trigger source for event-mode automations. See Building a Trigger Plugin for the full contract. When the plugin also supports bidirectional conversations, add channelAdapter and the channel manifest fields — see Building a Channel Plugin.

{
"triggers": {
"module": "./triggers/index.js",
"channelAdapter": "./channel/index.js",
"setupSkill": "connecting-my-provider",
"requiredSettings": ["signing_secret"],
"events": [
{ "type": "issue.created", "label": "Issue created", "filters": ["project"] }
]
}
}
FieldRequiredDescription
moduleYesPath to the file exporting verify(rawBody, headers, connection) and normalize(payload, headers, connection). Without it the whole block is dropped (with a warning).
channelAdapterNoPath to a module exporting createAdapter(settings) that returns a ChannelAdapter. Enables durable two-way conversations — distinct from one-shot event automations.
eventsYesNon-empty array of { type, label, description?, filters?, firehose? }. type (non-empty string) is matched against automations’ event_type; label is shown in the automation editor. A zero-event source is skipped by the trigger catalog — the validator warns about it.
setupSkillNoSkill id that walks the user through connecting the provider. Should correspond to a skill this plugin ships — plugin_validate warns when skills/<name>/SKILL.md is missing.
setupInstructionsNoMarkdown checklist of provider-side setup steps. Rendered as a platform-generated onboarding step between copy-URL and await-event.
requiredSettingsNoSettings keys (from settings.schema) that must be non-empty for the source to show as “Connected”. Defaults to every password-type setting.

Human-readable guidance shown when a user configures a channel instance in settings.

{
"channelSetup": {
"title": "Get your credentials",
"description": "Create a provider app, install it in your workspace, then copy its signing secret and bot token.",
"guideUrl": "https://api.example.com/apps",
"guideLabel": "Open setup guide"
}
}
FieldRequiredDescription
titleYesShort heading for the setup card.
descriptionYesOne or two sentences explaining what the user needs to do.
guideUrlYesExternal docs or console URL.
guideLabelNoLink button label (defaults to “Open guide”).

Declares how a channel instance connects to its provider. Core owns the OAuth state machine and connection UI; the plugin supplies provider metadata and response field mappings.

{
"channelConnection": {
"managedOAuth": {
"gatewayProvider": "recursive-slack",
"authorizationUrl": "https://slack.com/oauth/v2/authorize",
"scopes": ["chat:write", "app_mentions:read"],
"scopeSeparator": "comma",
"identity": { "idPath": "team.id", "labelPath": "team.name" },
"settings": {
"bot_token": "access_token",
"provider_team_id": "team.id"
}
}
}
}
FieldRequiredDescription
managedOAuth.gatewayProviderYesNamed credentials registered by the Recursive gateway operator. Plugin users never supply the shared app secret.
managedOAuth.authorizationUrlYesProvider OAuth authorize endpoint.
managedOAuth.scopesYesNon-empty scope list requested at connect time.
managedOAuth.scopeSeparatorNo"space" (default) or "comma" when building the authorize URL.
managedOAuth.identityYesidPath / optional labelPath — JSON paths into the token response for the connected account.
managedOAuth.settingsYesMaps token-response paths into plugin settings keys saved on the connection.

These two fields solve different problems — don’t mix them up:

  • onboarding is declarative plugin setup: a subtitle plus steps of kind link (open an external console), field (save a settings value, optionally secret), and verify (confirm required credentials are present), rendered in the plugin’s settings page. Use it for integrations that need credentials — trigger sources, API-backed tools.
  • setupSequence is adapter/CLI onboarding only: ordered terminal-action steps (install / auth / setup / diagnose, falling back to installCommand / loginCommand / setupCommand / diagnoseCommand) shown after installing a provider CLI plugin. Use it when the plugin wraps a CLI that must be installed and authenticated.
{
"onboarding": {
"subtitle": "Connect your workspace so agents pick up events.",
"steps": [
{ "kind": "link", "title": "Create an app", "url": "https://example.com/apps", "actionLabel": "Open console" },
{ "kind": "field", "title": "Add your Signing Secret", "field": { "key": "signing_secret", "secret": true }, "actionLabel": "Save" },
{ "kind": "verify", "title": "Verify setup", "actionLabel": "Verify" }
]
}
}