docs/docs/Lfx/extensions-manifest.mdx
This page is the reference for the extension.json manifest used by manifest-shipping extensions, including curated packages and graduated standalone packages.
The lfx-bundles metapackage uses manifest-less discovery through the lfx.bundles group and does not use extension.json.
The canonical JSON Schema lives at https://schemas.langflow.org/extension/v1.json and is generated from the Pydantic model at lfx.extension.manifest.ExtensionManifest.
To export the live schema, run:
lfx extension schema --output extension.schema.json
Use the $schema reference in your manifest so editors can autocomplete and validate inline:
{
"$schema": "https://schemas.langflow.org/extension/v1.json",
"id": "lfx-my-extension",
"version": "0.1.0",
"name": "My extension",
"lfx": { "compat": ["1"] },
"bundles": [{ "name": "my_bundle", "path": "components/my_bundle" }]
}
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Globally-unique extension ID. Lowercase, hyphenated, starts with a letter, 2-64 chars. Convention: lfx-<name>. |
version | string | yes | SemVer 2.0.0 version string for this extension release. |
name | string | yes | Human-readable display name shown in the Langflow palette. 1-200 chars. |
description | string | null | no | Optional one-paragraph summary, max 2000 chars. |
lfx | object | yes | Compatibility declaration against the BUNDLE_API contract. |
bundles | array | no | Bundle list. v0 accepts at most one bundle; omit it for a provider-only extension. |
providers | array | no | Model providers contributed to the unified model-provider registry. |
capabilities | object | no | Optional capability flags. Defaults to all-false. |
$schema | string | no | Optional pointer to this JSON Schema; editors use it for autocomplete. |
additionalProperties: false — any field not listed here is rejected with a typed error. Reserved names (services, routes, hooks, starterProjects, userConfig) are documented under Deferred fields and surface a more specific error code.
An extension must declare at least one entry in either bundles or providers. It can declare both when one package contributes components and model providers.
lfx: compatibility declaration"lfx": { "compat": ["1"] }
| Field | Type | Description |
|---|---|---|
compat | array of strings | Non-empty list of BUNDLE_API.md contract versions this extension supports. Each entry is a positive-integer string. |
The runtime compares str(BUNDLE_API_VERSION) against this list. A mismatch fails install with version-constraint-unsatisfied. v0 accepts only "1"; lists like ["1", "2"] become meaningful when a future BUNDLE_API revision ships.
bundles"bundles": [
{ "name": "my_bundle", "path": "components/my_bundle" }
]
| Field | Type | Description |
|---|---|---|
name | string | Bundle name; addressable as ext:<bundle>:<Class>@<slot>. Lowercase snake_case, starts with a letter, 2-64 chars. |
path | string | Path to the bundle directory, relative to the manifest. Must not start with / or contain ... |
v0 enforces maxItems: 1; multi-bundle extensions are rejected with multi-bundle-unsupported and ship in a later epic. The list can be empty when the extension declares at least one model provider.
providersA provider-only extension can add a provider without adding a component bundle:
{
"$schema": "https://schemas.langflow.org/extension/v1.json",
"id": "lfx-acme-models",
"version": "0.1.0",
"name": "Acme Models",
"lfx": { "compat": ["1"] },
"providers": [
{
"name": "Acme",
"provider_id": "acme.models",
"display_name": "Acme Models",
"aliases": ["Acme Legacy"],
"metadata": {
"icon": "Bot",
"variables": [],
"mapping": {
"model_class": "ChatAcme",
"model_param": "model"
}
},
"api_key_required": false,
"model_class": {
"module": "langchain_acme",
"attr": "ChatAcme",
"install_hint": "langchain-acme"
},
"catalog_loader": "lfx_acme.catalog:load_models"
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Canonical provider name used by existing saved flows and provider selectors. |
provider_id | string | no | Stable lowercase machine identity used by policy and extension compatibility. Must match ^[a-z0-9][a-z0-9._-]*$. Legacy manifests can omit it and receive a deterministic ID derived from name. |
display_name | string | no | User-facing label. It can change without changing provider_id or saved-flow identity. |
aliases | array of strings | no | Unique legacy names accepted when resolving the provider's stable identity. |
metadata | object | yes | Provider icon, variables, API documentation URL, and mapping. mapping.model_class must be non-empty. |
model_class | object | no | Lazy LLM class import with module, attr, and optional install_hint. Omit it when reusing an already-registered class. |
embedding | object | no | Optional embedding class import and parameter mapping. Includes class_name, module, attr, param_mapping_key, param_mapping, and optional install_hint. |
api_key_required | bool | no | Whether unified runtime helpers reject missing API-key credentials. Defaults to true. |
live | bool | no | Enables always-on live model discovery. Mutually exclusive with conditional_live. |
conditional_live | bool | no | Enables live discovery only when the provider has a custom endpoint configured. Mutually exclusive with live. |
live_discovery | string | no | Lazy module:callable that receives (user_id, model_type) and returns live model rows. |
validator | string | no | Lazy module:callable that validates (provider, variables, model) and raises on invalid credentials. |
catalog_loader | string | no | Lazy module:callable returning the provider's static model metadata rows. |
catalog_loader must return a flat list[dict]. Every row requires a non-empty name; model_type can be llm or embeddings and defaults to llm. Langflow overwrites any supplied provider value with the descriptor's canonical name, supplies the provider icon when omitted, and rejects duplicate (model_type, name) identities. Deployments that require an extension catalog can call validate_registered_provider_catalogs() or get_registry_snapshot(validate_catalogs=True) during readiness.
Providers that use ambient authentication or need no credentials should set api_key_required to false. They remain eligible for unified model options when metadata.variables is empty or contains only optional configuration fields.
Built-in providers take precedence over extension declarations. A provider-name collision is skipped without preventing other providers or components from loading. Stable-ID or alias collisions are reported as provider-invalid.
capabilitiesOptional. Defaults to { "requiresCredentials": false }.
| Field | Type | Description |
|---|---|---|
requiresCredentials | bool | If true, the loader records that components in this bundle expect credential variables to be configured before use. |
Additional capability keys are rejected with extra="forbid" so a misspelled key surfaces immediately rather than silently turning a feature off.
The schema strips these names from the published properties map but reserves them via x-deferred-fields, so a manifest that sets one gets a specific error code instead of the generic "additional property" message.
| Reserved key | Replacement error code | Future epic |
|---|---|---|
services | field-deferred-in-this-milestone | B2 — non-component primitives |
routes | field-deferred-in-this-milestone | B2 — non-component primitives |
hooks | field-deferred-in-this-milestone | B2 — non-component primitives |
starterProjects | field-deferred-in-this-milestone | later milestone |
userConfig | field-deferred-in-this-milestone | later milestone |
A manifest that sets any of these to a non-null value is rejected at validate / load time. Setting them to null is allowed (the loader treats null and absent identically).
If you'd rather not ship a separate extension.json next to pyproject.toml, declare the same fields under [tool.langflow.extension]:
[tool.langflow.extension]
id = "lfx-my-extension"
version = "0.1.0"
name = "My extension"
[tool.langflow.extension.lfx]
compat = ["1"]
[[tool.langflow.extension.bundles]]
name = "my_bundle"
path = "components/my_bundle"
The loader prefers an extension.json when both exist. The Pydantic validator is the same so error codes and field semantics are identical.
The loader and validator both emit typed errors keyed by the manifest field that triggered them. The full reference is on the Extension error codes page; the codes most relevant when authoring a manifest are:
| Code | Cause |
|---|---|
manifest-invalid | Schema validation failed; the message names the field. |
manifest-not-found | No extension.json and no [tool.langflow.extension] section at the extension root. |
version-constraint-unsatisfied | lfx.compat does not include this Langflow's BUNDLE_API_VERSION. |
field-deferred-in-this-milestone | A reserved field was set to a non-null value. |
multi-bundle-deferred-in-this-milestone | bundles has more than one entry. |
path-escape | A bundles[].path resolves outside the manifest root (typically a symlink). |
bundle-path-not-found | bundles[].path does not exist or is not a directory. |
provider-invalid | A provider descriptor or one of its lazy import paths is malformed. The invalid provider is skipped. |
provider-skipped | A provider name collides with a built-in or previously loaded provider. The existing provider wins. |
Run lfx extension validate <path> to see every error as a structured object with code, message, location, hint, and ref_url.