Skip to main content
The manifest is the single declarative file a server loads at startup. It names the tools, agents, extensions, external MCP servers, and plugin modules that make up one running system. Nothing loads automatically — a capability is present because the manifest names it.
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.
Before you deploy, validate a manifest offline — no server required — with the CLI:

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 a module and selects tool names with include / exclude. A separate extensions map attaches an extension branch to a selected tool.
  • agents — each entry imports a module whose @tai42_app.agents.agent decorator registers an agent and generates its run tool.
  • mcp — external MCP servers to mount, one transport per entry (url, uds, or command).
  • 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, and channel_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 own studio-manifest.json declaring what it contributes to the Studio shell — tool panels, pages, settings tabs, and sidebar nav_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 a tools 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 — with false, no operation projects as a tool; the surface is empty.
  • expose_destructive — gates the destructive-flagged operations (a tool reload, a manifest replace). With false they 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 an include naming an operation that is not registered fails boot rather than silently doing nothing.
  • extensions — attaches extension combos to a projected operation, exactly as a tools entry’s extensions map does.
Two rules override the block. A meta-executorrun_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.
See the Python SDK reference for the Manifest and ApiToolsConfig models and the CLI reference for tai manifest.