--- summary: "Plugin manifest + JSON schema requirements (strict config validation)" read_when: - You are building an OpenClaw plugin - You need to ship a plugin config schema or debug plugin validation errors title: "Plugin Manifest" --- # Plugin manifest (openclaw.plugin.json) This page is for the **native OpenClaw plugin manifest** only. For compatible bundle layouts, see [Plugin bundles](/plugins/bundles). Compatible bundle formats use different manifest files: - Codex bundle: `.codex-plugin/plugin.json` - Claude bundle: `.claude-plugin/plugin.json` or the default Claude component layout without a manifest - Cursor bundle: `.cursor-plugin/plugin.json` OpenClaw auto-detects those bundle layouts too, but they are not validated against the `openclaw.plugin.json` schema described here. For compatible bundles, OpenClaw currently reads bundle metadata plus declared skill roots, Claude command roots, Claude bundle `settings.json` defaults, and supported hook packs when the layout matches OpenClaw runtime expectations. Every native OpenClaw plugin **must** ship a `openclaw.plugin.json` file in the **plugin root**. OpenClaw uses this manifest to validate configuration **without executing plugin code**. Missing or invalid manifests are treated as plugin errors and block config validation. See the full plugin system guide: [Plugins](/tools/plugin). For the native capability model and current external-compatibility guidance: [Capability model](/plugins/architecture#public-capability-model). ## What this file does `openclaw.plugin.json` is the metadata OpenClaw reads before it loads your plugin code. Use it for: - plugin identity - config validation - auth and onboarding metadata that should be available without booting plugin runtime - config UI hints Do not use it for: - registering runtime behavior - declaring code entrypoints - npm install metadata Those belong in your plugin code and `package.json`. ## Minimal example ```json { "id": "voice-call", "configSchema": { "type": "object", "additionalProperties": false, "properties": {} } } ``` ## Rich example ```json { "id": "openrouter", "name": "OpenRouter", "description": "OpenRouter provider plugin", "version": "1.0.0", "providers": ["openrouter"], "providerAuthEnvVars": { "openrouter": ["OPENROUTER_API_KEY"] }, "providerAuthChoices": [ { "provider": "openrouter", "method": "api-key", "choiceId": "openrouter-api-key", "choiceLabel": "OpenRouter API key", "groupId": "openrouter", "groupLabel": "OpenRouter", "optionKey": "openrouterApiKey", "cliFlag": "--openrouter-api-key", "cliOption": "--openrouter-api-key ", "cliDescription": "OpenRouter API key", "onboardingScopes": ["text-inference"] } ], "uiHints": { "apiKey": { "label": "API key", "placeholder": "sk-or-v1-...", "sensitive": true } }, "configSchema": { "type": "object", "additionalProperties": false, "properties": { "apiKey": { "type": "string" } } } } ``` ## Top-level field reference | Field | Required | Type | What it means | | --------------------- | -------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `id` | Yes | `string` | Canonical plugin id. This is the id used in `plugins.entries.`. | | `configSchema` | Yes | `object` | Inline JSON Schema for this plugin's config. | | `enabledByDefault` | No | `true` | Marks a bundled plugin as enabled by default. Omit it, or set any non-`true` value, to leave the plugin disabled by default. | | `kind` | No | `"memory"` \| `"context-engine"` | Declares an exclusive plugin kind used by `plugins.slots.*`. | | `channels` | No | `string[]` | Channel ids owned by this plugin. Used for discovery and config validation. | | `providers` | No | `string[]` | Provider ids owned by this plugin. | | `providerAuthEnvVars` | No | `Record` | Cheap provider-auth env metadata that OpenClaw can inspect without loading plugin code. | | `providerAuthChoices` | No | `object[]` | Cheap auth-choice metadata for onboarding pickers, preferred-provider resolution, and simple CLI flag wiring. | | `skills` | No | `string[]` | Skill directories to load, relative to the plugin root. | | `name` | No | `string` | Human-readable plugin name. | | `description` | No | `string` | Short summary shown in plugin surfaces. | | `version` | No | `string` | Informational plugin version. | | `uiHints` | No | `Record` | UI labels, placeholders, and sensitivity hints for config fields. | ## providerAuthChoices reference Each `providerAuthChoices` entry describes one onboarding or auth choice. OpenClaw reads this before provider runtime loads. | Field | Required | Type | What it means | | ------------------ | -------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `provider` | Yes | `string` | Provider id this choice belongs to. | | `method` | Yes | `string` | Auth method id to dispatch to. | | `choiceId` | Yes | `string` | Stable auth-choice id used by onboarding and CLI flows. | | `choiceLabel` | No | `string` | User-facing label. If omitted, OpenClaw falls back to `choiceId`. | | `choiceHint` | No | `string` | Short helper text for the picker. | | `groupId` | No | `string` | Optional group id for grouping related choices. | | `groupLabel` | No | `string` | User-facing label for that group. | | `groupHint` | No | `string` | Short helper text for the group. | | `optionKey` | No | `string` | Internal option key for simple one-flag auth flows. | | `cliFlag` | No | `string` | CLI flag name, such as `--openrouter-api-key`. | | `cliOption` | No | `string` | Full CLI option shape, such as `--openrouter-api-key `. | | `cliDescription` | No | `string` | Description used in CLI help. | | `onboardingScopes` | No | `Array<"text-inference" \| "image-generation">` | Which onboarding surfaces this choice should appear in. If omitted, it defaults to `["text-inference"]`. | ## uiHints reference `uiHints` is a map from config field names to small rendering hints. ```json { "uiHints": { "apiKey": { "label": "API key", "help": "Used for OpenRouter requests", "placeholder": "sk-or-v1-...", "sensitive": true } } } ``` Each field hint can include: | Field | Type | What it means | | ------------- | ---------- | --------------------------------------- | | `label` | `string` | User-facing field label. | | `help` | `string` | Short helper text. | | `tags` | `string[]` | Optional UI tags. | | `advanced` | `boolean` | Marks the field as advanced. | | `sensitive` | `boolean` | Marks the field as secret or sensitive. | | `placeholder` | `string` | Placeholder text for form inputs. | ## Manifest versus package.json The two files serve different jobs: | File | Use it for | | ---------------------- | ------------------------------------------------------------------------------------------------------------------ | | `openclaw.plugin.json` | Discovery, config validation, auth-choice metadata, and UI hints that must exist before plugin code runs | | `package.json` | npm metadata, dependency installation, and the `openclaw` block used for entrypoints and setup or catalog metadata | If you are unsure where a piece of metadata belongs, use this rule: - if OpenClaw must know it before loading plugin code, put it in `openclaw.plugin.json` - if it is about packaging, entry files, or npm install behavior, put it in `package.json` ## JSON Schema requirements - **Every plugin must ship a JSON Schema**, even if it accepts no config. - An empty schema is acceptable (for example, `{ "type": "object", "additionalProperties": false }`). - Schemas are validated at config read/write time, not at runtime. ## Validation behavior - Unknown `channels.*` keys are **errors**, unless the channel id is declared by a plugin manifest. - `plugins.entries.`, `plugins.allow`, `plugins.deny`, and `plugins.slots.*` must reference **discoverable** plugin ids. Unknown ids are **errors**. - If a plugin is installed but has a broken or missing manifest or schema, validation fails and Doctor reports the plugin error. - If plugin config exists but the plugin is **disabled**, the config is kept and a **warning** is surfaced in Doctor + logs. See [Configuration reference](/gateway/configuration) for the full `plugins.*` schema. ## Notes - The manifest is **required for native OpenClaw plugins**, including local filesystem loads. - Runtime still loads the plugin module separately; the manifest is only for discovery + validation. - Only documented manifest fields are read by the manifest loader. Avoid adding custom top-level keys here. - `providerAuthEnvVars` is the cheap metadata path for auth probes, env-marker validation, and similar provider-auth surfaces that should not boot plugin runtime just to inspect env names. - `providerAuthChoices` is the cheap metadata path for auth-choice pickers, `--auth-choice` resolution, preferred-provider mapping, and simple onboarding CLI flag registration before provider runtime loads. For runtime wizard metadata that requires provider code, see [Provider runtime hooks](/plugins/architecture#provider-runtime-hooks). - Exclusive plugin kinds are selected through `plugins.slots.*`. - `kind: "memory"` is selected by `plugins.slots.memory`. - `kind: "context-engine"` is selected by `plugins.slots.contextEngine` (default: built-in `legacy`). - `channels`, `providers`, and `skills` can be omitted when a plugin does not need them. - If your plugin depends on native modules, document the build steps and any package-manager allowlist requirements (for example, pnpm `allow-build-scripts` - `pnpm rebuild `).