Skip to main content
Plugin manifest contract: the tai-plugin.yml schema (PluginSpec). Every installable TAI plugin ships a tai-plugin.yml — at its repo root and as package-data inside the built wheel. The file names the listing (namespace/name), the pip distribution that backs it, the tai42-contract compatibility range, declared capabilities, and the item-level provides index: one entry per tool/agent/extension/… the package registers, because items are what users search for while the plugin is what gets installed. The models here are the one schema shared by the marketplace registry’s validator, the skeleton’s installer, and each plugin repo’s own spec test. The YAML I/O helpers live above the contract (tai42_kit.plugins) — the contract itself has no YAML dependency. KIND_MANIFEST_BINDINGS is the single source for how each provided item kind wires into a Manifest: which manifest field an installer patches and with what shape (a config row, a module-list entry, a single-module slot, a package-name entry, an mcp transport entry, a connectors descriptor-list append, or no manifest field at all for the env-selected config kind). Version strings are validated as PEP 440 (an anchored regex of the spec’s canonical pattern) and contract as a PEP 440 specifier set — shape-level parseability only; evaluating whether a version satisfies a range is the consumer’s concern. display_name and icon are the optional marketplace display metadata: a human UI title (the UI titleizes name when absent) and either a packaged image path relative to the package root or an https URL (the UI falls back to a generated monogram when absent).

DATA_BLOCK_BY_KIND

tai42_contract.plugins.bindings.DATA_BLOCK_BY_KIND

KIND_MANIFEST_BINDINGS

tai42_contract.plugins.bindings.KIND_MANIFEST_BINDINGS

ManifestBinding

tai42_contract.plugins.bindings.ManifestBinding
How one provided item kind wires into a Manifest. field names the manifest field an installer patches for an item of this kind; mode is the patch shape; payload is the axis the item carries — module (the item names an import path) or data (the item carries its kind’s declarative block):
  • config_row — append a config row (tools/agents) whose module is the item’s module.
  • module_list — append the item’s module to a plain module list.
  • scalar_module — set a single-module slot; the slot holds ONE module, so a second plugin claiming an occupied slot is a conflict the caller must reject loudly.
  • package_list — append the plugin’s DISTRIBUTION name (not the item’s module) to a package list (studio_plugins).
  • mcp_entry — append one {title: item.name, config: item.mcp} object to the manifest mcp list; uninstall removes the entry by title.
  • descriptor_entry — append the item’s provider descriptor to the manifest connectors list; uninstall removes by provider.id.
  • env_selected — no manifest field: the kind is selected through the environment (a config provider is named by TAI_CONFIG_MODE and imported by the config seam), so field is None.
The data modes (mcp_entry, descriptor_entry) carry payload == "data"; every other mode carries payload == "module". Attributes

PluginItem

tai42_contract.plugins.item.PluginItem
One installable item in a plugin’s provides index. module is the import path whose import side-effect registers the item (or, for env-selected kinds, the module the selecting seam imports); the installer patches it into the manifest per KIND_MANIFEST_BINDINGS. The item shape is table-driven on KIND_MANIFEST_BINDINGS (a model validator enforces it, loud both ways): a data-payload kind carries exactly the declarative block DATA_BLOCK_BY_KIND names for it — mcp transport config for mcp-server, a provider descriptor for connector — and no module; every module-payload kind carries module and no data block. A connector item’s name MUST equal its provider.id — that id is the manifest/uninstall key. routes declares the item’s HTTP mount: REQUIRED for kind: router, OPTIONAL for kind: channel, and FORBIDDEN (must be absent) for every other kind. group is an OPTIONAL logical family label the author may put on any item of any kind: items sharing a value belong to one family, and a consumer may summarize the provides index by group. It is purely a structural self-description of the plugin — cross-kind groups and single-member groups are both legal, and absent means the item stands alone. It carries a tag’s charset discipline. Attributes

PluginItemKind

tai42_contract.plugins.bindings.PluginItemKind
Kind of one installable item a plugin provides. The values are the ecosystem’s item-kind vocabulary (the same words the catalog and the marketplace facets use). KIND_MANIFEST_BINDINGS maps every member onto its manifest wiring; an unknown kind in a spec is a loud validation reject, never a skipped row. Attributes

PluginPermissions

tai42_contract.plugins.spec.PluginPermissions
Capabilities a plugin declares — informational: surfaced in listings, not enforced by a sandbox. Omitting the block declares none (every flag defaults to False); an unknown key is rejected loudly rather than silently ignored. Attributes

PluginSpec

tai42_contract.plugins.spec.PluginSpec
The complete, validated content of one tai-plugin.yml. Frozen and extra="forbid": a typo’d key fails validation loudly. The listing reference is namespace/name (ref); package is the normalized pip distribution the listing points at, OPTIONAL because a spec whose every item is a data item (mcp-server/connector) may ship no package — a descriptor-only plugin that installs nothing but its manifest entry. delivery derives the one word every surface shows ("package" when package is set, "descriptor" when absent); a spec with any module item MUST name a package. version must equal the built wheel’s version (each plugin repo’s spec test and the registry’s ingest validation both pin that); contract is the tai42-contract compatibility range as a PEP 440 specifier set, required unless every provided item is kind mcp-server (such a package imports no contract), and a spec may not mix mcp-server with any other kind. A connector item’s provider.origin is system iff the listing namespace is tai42 — a community listing may not claim the system label. display_name and icon are optional marketplace display metadata; premium marks a paid listing. migrations is an OPT-IN, package-relative directory holding the plugin’s ordered SQL schema chain — absent means the plugin owns no tables and is not a migrations plugin (most plugins); it requires a package. The contract validates only the path’s SHAPE; the directory’s existence in the installed package is enforced at runner-discovery time, not here. migrations_component optionally names the database component the chain migrates, for a plugin whose feature store is a SEPARATE, off-unless-declared component rather than the distribution itself — absent (the default) runs the chain under the distribution name, byte-identical to prior behavior. Setting it REQUIRES migrations (a component with no chain migrates nothing). Attributes

Members

ref

tai42_contract.plugins.spec.PluginSpec.ref
The full listing reference, namespace/name.

delivery

tai42_contract.plugins.spec.PluginSpec.delivery
How the plugin is delivered: "descriptor" when it ships no package, else "package". A descriptor is an all-data, install-nothing listing.

RouteDecl

tai42_contract.plugins.routes.RouteDecl
One HTTP route a plugin item declares, relative to its mount base. path is /-prefixed with at least one segment; each segment is a literal (ROUTE_LITERAL_SEGMENT_RE) or a {name} template (ROUTE_PARAM_SEGMENT_RE, a python identifier with no converter suffix). methods is a non-empty set of uppercase HTTP methods, unique within the row. public states — with no default, so the declaration is never silent — whether the resolved route answers unauthenticated. Attributes

RouteMethod

tai42_contract.plugins.routes.RouteMethod

RoutesDecl

tai42_contract.plugins.routes.RoutesDecl
The route block one item declares: a default mount base and its rows. base is RELATIVE — one or more ROUTE_BASE_SEGMENT_RE segments joined by /, no leading/trailing slash, no templates. The resolved absolute path of each row is /api/ + base + the row’s path; the /api/ root is fixed platform-wide and only base is remappable. paths is non-empty. Attributes

data_kinds

tai42_contract.plugins.bindings.data_kinds
The item kinds whose binding carries payload == "data". These are the kinds a declarative (no-module) item is built for. Derived from KIND_MANIFEST_BINDINGS so the payload table stays the one source.