Plugin SDK reference
Package: @zana-ai/zcc-plugin-sdk (PLUGIN_SDK_VERSION / engines.zccPluginSdk).
Server — `ZccPluginApi`#
Handed to export default function plugin(zcc).
| Surface | Purpose |
|---|---|
pluginId |
Derived id |
log |
debug/info/warn/error |
settings.define |
Declarative settings the host/CLI can render |
storage.kv |
Per-plugin KV |
rpc.method |
Renderer/host RPC |
realtime.publish |
Events |
background.service / schedule |
Long-running work |
agents.contributeInstructions / contributeSkills |
Agent capabilities |
ui.requestInput |
Host prompt |
status.needsConfiguration |
Degraded-until-configured |
services.provide / services.use / services.has |
Experimental plugin-to-plugin SDK (live proxy; has after provide; service_unavailable until provided) |
onDispose |
LIFO teardown |
Plugins do not get ctx.exec permission tokens. They are full-trust in the
server process and must not be given host-daemon tokens.
Plugin services (experimental)#
zcc.services.provide(impl) is keyed by this plugin's id (another plugin
cannot impersonate it). zcc.services.use(id) returns a live proxy that
always dispatches to the current impl, so a provider reload does not drop
consumers. zcc.services.has(id) is true after that plugin has called
provide. Missing / disposed providers throw PluginServiceUnavailableError
(code: 'service_unavailable'). Stay experimental until a second in-tree
consumer exists — see packages/plugin-sdk/docs/api_to_audit.md.
The Salesforce plugin publishes @zcc-ext/salesforce/sdk (SalesforceSdk) as
a types-only contract. Consumers import type and use('salesforce');
they must not import ConnectionManager / createSalesforceSdk or receive
org credentials. Reuse + extraction map: plugins/salesforce/SDK.md.
App — `definePluginApp`#
Slots are grouped the same way as the in-app Plugin Guide (Plugins hub).
When you add or rename a surface, update plugins/plugin-guide/src/surfaces.ts,
this page, and run node website/scripts/sync-plugin-guide.mjs (copies the map
into website/lib/plugin-guide/; do not hand-edit that folder).
App shell#
navPanel— sidebar entry + full view.placement: "extensions"lists under Plugins;placement: "unlisted"is a full/plugins/<id>/<path>page with no rail row (open viatoPluginPanel)experimental_projectMenuAction— project-row overflow, or the Projects Organize menu (placement: "workspace"means no project is selected;projectIdisnull).toProjectopens aprojectTabexperimental_createProjectAction— Add project (+) menu (openDialogmounts an optional create wizard;addProjectregisters the folder)sidebarFooterAction— host-rendered footer icon (openSettings/toPluginPanel)
Project shell#
projectTab— per-project rail tab (global: falsehides the sidebar entry)experimental_agentsBoardAction— toolbar control on the Agents board (projectIdisnullon the cross-project Agents nav)experimental_agentCardAction— right-click item on an Agents board cardprojectStatusbarItem— project statusbar chip (alignleft/right;runmaytoProject/toPluginPanel/openDialog/openMenu)
Home#
homepageSection— card on the Home compose surfaceexperimental_newThreadPanelAction— CTA under New Chat compose (can open a compose-time side panel)
Composer#
composer—composer.customize: actions, banners, plus-menu items, meta chips, advanced fields, and rich-text effects. Scope tothread,new-thread,cli-agent,queued-message, orside-chat.useComposer().experimental_setLaunchPatchoverlays spawn extraArgs / profile / routing
Thread#
threadPanelAction— thread side-panel tabs; optionalscopesinclude"agent-session"for the CLI-agent inspectorpendingInteraction— custom in-thread prompt UI (idmust matchzcc.ui.requestInputrendererId)experimental_threadHeaderAction— action in the thread detail headerexperimental_threadList— replace the Agents list pane (exclusive; last registered wins Appearance pin)experimental_timelineRenderer— custom body for a timeline row kindmessageDirective— render::name{attr}leaves in markdownmessageAction— per-message menu item on the timelinefileOpener— open a previewed file by extension
Command palette#
commandPaletteAction— a row in ⌘P Extensions (toPluginPanel/toProject/openPanel)
Configure#
settingsSection— React settings UI on the plugin’s Plugins hub detail (Configure)
Platform#
Headless (no pixels), plus picker chrome:
skills—zcc.skills/contributeSkillscli—zcc.cli.registermcp—zcc.mcpServerssettings-define—zcc.settings.definebackground—zcc.background.service/schedulecontentScripts—contentScripts.registerexperimental_providerIcon— picker glyph for a provider id
Registrations replace wholesale per plugin id. Each carries a generation used
as the React remount key. Wrap UI in PluginSlotBoundary. Live-reload with
zcc plugin dev.
Manifest#
package.json zcc block, parsed by @zana-ai/zcc-domain readPluginManifest.
Id from derivePluginId(package.name). Reserved sentinel __builtin__ is
host chrome, not a plugin.
| Field | Meaning |
|---|---|
skills |
Directory roots (BB). Default ["skills"]; [] opts out. Each child dir with a regular SKILL.md is a skill named after the folder. |
mcpServers |
Map of Claude CLI MCP servers. stdio command is basename-only; relative args are rewritten to contained paths. |
extra |
Opaque JSON object (≤32 keys, ≤8 KiB). Displayed on install; never synced as skills/MCP. |
requires |
Other plugin ids this plugin consumes via zcc.services.use. Host topo-sorts load order. A missing required plugin marks the consumer needs-configuration. |
Durable skills belong in zcc.skills. agents.contributeSkills is a runtime extra. There is no registerMcpServer.
Do not put secrets in extra. Env values on mcpServers are written to .mcp.json only — the hub sees envKeys.
Legacy extension.json is shimmed for one release via shimLegacyExtensionManifest.