This page covers the server manifest. A plugin’s own descriptor,
tai-plugin.yml, is a different file — see
The plugin descriptor.Where it comes from
A server loads its manifest from the file pointed at by--manifest-path (its
env default is TAI_MANIFEST_PATH), or from its config directory. An empty
manifest ({}) boots a bare MCP server with no tools; every section is optional.
What it holds
Most entries are Python import paths. Importing a module runs its@tai42_app.*
registration decorators, which is how a tool, agent, or provider becomes live.
tools— each entry imports amoduleand selects tool names withinclude/exclude. A separateextensionsmap attaches an extension branch to a selected tool.agents— each entry imports a module whose@tai42_app.agents.agentdecorator registers an agent and generates its run tool.mcp— external MCP servers to mount, one transport per entry (url,uds, orcommand).- Startup module hooks — single-valued slots and module lists that wire
plugins in:
storage_module,backend_module,monitoring_module,extensions_modules,lifecycle_modules,routers_modules,middlewares_modules,webhook_verifier_modules, andchannel_modules. studio_plugins— names the installed Python packages that ship a Studio plugin (a browser-side UI extension), not import paths. Each named package carries its ownstudio-manifest.jsondeclaring what it contributes to the Studio shell — tool panels, pages, settings tabs, and sidebarnav_entries. That plugin manifest is a separate file from this server manifest; see Author a Studio plugin.api_tools— curates which management operations project as MCP tools (below). It never selects your own tools; it governs only the operation-projected surface.
A minimal manifest
The hello-world manifest names one local tool module and nothing else:module is an import path, so myapp must be importable when the server starts.
include limits which tool names from the module are exposed; omit it to expose
all of them.
Selection versus attachment
Within atools entry, include / exclude are selection only — they decide
which tool names go live. The extensions map is attachment — it branches a
selected tool into a new variant ({toolname: [ext]} produces toolname_ext).
Attachment never selects: mapping an extension onto a tool that is not in the
selected set raises loudly rather than silently doing nothing.
The api_tools block
The management operations — reload a tool, replace
the manifest, re-probe a failed MCP server — are always available as HTTP routes
and CLI commands. api_tools decides which of them also project as MCP tools:
enabled— withfalse, no operation projects as a tool; the surface is empty.expose_destructive— gates the destructive-flagged operations (a tool reload, a manifest replace). Withfalsethey stay off the MCP surface even though their routes remain.include/exclude— name operations to force on or suppress. A name appearing in both lists is a loud validation error, and anincludenaming an operation that is not registered fails boot rather than silently doing nothing.extensions— attaches extension combos to a projected operation, exactly as atoolsentry’sextensionsmap does.
run_tool, which runs any
tool by name — is never projectable: it is hardcode-blocked from the MCP surface
even when named in include. An authority-changing operation — one that mints
keys, edits policy, replaces the manifest (update_manifest), or restores
unshipped state (a backup import) — is off the default surface and projects only
when include names it explicitly.
The skeleton ships tool extensions and HTTP router modules that do not load
automatically — you opt each one in by naming its module in the manifest, exactly
like your own code. The management tools are the exception: they project from the
operations layer, curated by
api_tools, not loaded module by module.Manifest
and ApiToolsConfig models and the CLI reference for
tai manifest.
