Extension manifest
kubb-extension.json describes an extension package. It sits at the package root and ships on npm. The Kubb registry, kubb add, kubb list, and the Studio agent read it. Generation does not: at runtime Kubb reads the extension's meta.
The JSON Schema is https://kubb.dev/schemas/kubb-extension.json. Set it as $schema to get validation and completion in your editor.
{
"$schema": "https://kubb.dev/schemas/kubb-extension.json",
"kind": "plugin",
"name": "plugin-example",
"export": "pluginExample",
"displayName": "Example",
"description": "Writes a comment with the HTTP method and path of every operation.",
"category": "documentation",
"icon": { "light": "https://example.com/icon.svg" },
"tags": ["comments", "operations"],
"docs": "https://github.com/acme/kubb-plugin-example#readme",
"maintainers": [{ "name": "Acme", "github": "acme" }],
"compatibility": { "kubb": ">=5.8.3", "node": ">=22" },
"options": [
{
"key": "prefix",
"path": ["prefix"],
"label": "Prefix",
"description": "Text written at the start of every comment.",
"type": "text"
}
]
}Fields
Readers ignore unknown top-level keys.
$schema
https://kubb.dev/schemas/kubb-extension.json. Only editors read it.kind
kubb add adds a plugin to the config and prints the code for an adapter or parser. Studio builds forms only for plugins.name
meta.name, such as plugin-zod, oas, or typescript. Kubb skips a manifest whose name is not shaped like an npm package name: lowercase letters, digits, ., _, ~, and -, with an optional @scope/.export
kubb.config.ts, such as pluginZod. It must be a JavaScript identifier. kubb add imports this name, and the Studio agent calls it.displayName
Zod.description
category
validation. The registry replaces it with the main category the reviewer picks from the category list.icon
media
tags
docs
maintainers
name, such as Stijn Van Hulle, and can add a github username, such as stijnvanhulle.dependencies
["plugin-ts"]. kubb.dev lists them on the extension's page. Generation does not read them: at runtime Kubb uses the plugin's own dependencies.compatibility
Semver ranges for Kubb (@kubb/core) and Node.js, such as { "kubb": ">=5.8.3", "node": ">=22" }. Only these two keys are allowed. Any other key makes the manifest invalid, so a typo such as nodejs cannot skip a check.
kubb add and kubb update check the ranges before they install. Kubb checks the same ranges at runtime through meta.compatibility, so import it from the manifest.
role
client provides the HTTP client runtime, like @kubb/plugin-fetch. consumer generates code that calls a client but ships none, like @kubb/plugin-react-query. Studio uses it to require a client plugin next to a consumer before it generates.options
Validation
Kubb and the registry check a manifest at different points:
| Reader | Checks | On failure |
|---|---|---|
kubb add, kubb update, kubb list, the Studio agent | The required fields are set and well formed. Each maintainers entry has a name. Each dependencies entry is an extension name. compatibility holds only kubb and node string ranges. options is an array. | The manifest is skipped. |
| The registry | The JSON Schema, unique option keys, choices on every select, and a select default that equals one of its choices. | The review pull request lists the problem, and the weekly bump keeps the previous pin. |
Import compatibility into meta
Keep the ranges in one place. Import only compatibility from the manifest into meta, so a bundler leaves the rest of the file, including options, out of your build:
import { defineExtension } from 'kubb/kit'
import { compatibility } from '../kubb-extension.json'
import { version } from '../package.json'
export const pluginExampleName = 'plugin-example'
export const pluginExample = defineExtension({
kind: 'plugin',
meta: { name: pluginExampleName, version, compatibility },
setup(options) {
return options
},
})Keep name as a typed constant. A JSON import widens it to string. Named imports from JSON need a bundler, such as tsdown, that inlines them. With plain tsc and "module": "NodeNext", write the same ranges in meta.compatibility instead.
See also
- Extensions:
metaand compatibility checks at runtime - Publish a plugin: ship the manifest and submit to the registry
- Extension registry: how the registry serves manifests