mcp section; the runtime probes it at startup and binds its tools alongside your local ones.
Declare a server in the manifest
Eachmcp entry names one server. Set exactly one transport per entry — the runtime infers the transport from whichever field you set:
urlfor a remote HTTP or SSE server (with optionalheaders),udsfor a Unix domain socket,command(plusargs/env) for a stdio server the runtime launches.
manifest.yml
uvx and npx, so a stdio server
launched as uvx <package> or npx <package> starts on the image as it is; the runner
fetches the package on its first use.
A command with a directory part is launched as given. A bare command is found at
its first executable match, in this order:
- the entry’s own
env.PATH, when it sets one; - a command an installed
mcp-serverpackage exposes under exactly that name — the console scripts its own package declares, run from that package’s own environment; - the runtime’s
PATH, only when the entry’senvsets noPATH.
command with no executable match fails the server’s start with a message that
names the PATH searched and the exposed command names, and any file of that name that
is not executable. The server is launched with its env exactly as the entry declares
it, plus a short safe-list of the runtime’s own environment (HOME, PATH, USER, …);
nothing else is added, and a PATH the entry sets replaces the inherited one.
A marketplace mcp-server plugin that ships a package names one
of its own console scripts as its command: the install builds the package into its own
environment and refuses, with a 409, a command the package does not declare or that
another installed server package already exposes.
Use include / exclude on an entry to expose only a subset of the mounted server’s tools.
Inspect and reload
1
Check binding status
Snapshot the live MCP binding state — which servers attached, which were skipped by a failed viability probe, and each attached server’s recent dispatch health.The payload carries three blocks: Each
bound maps each attached server’s title to the tool names it serves, failed lists the servers a viability probe skipped — each entry carrying the server title, a coarse status, a category (auth, unreachable for a transport error or a start that outran the start budget, starting for a start still in progress when the caller stopped waiting, which binds when the start completes, preset_store when the preset store could not be read while the server’s tools were being bound, or error), a redacted message, and the http_status the failure carried (null for a pure transport failure) — and health reports each server’s most recent tool-dispatch outcome, keyed by title (a skipped server carries the block at its empty values).health entry has four fields, detailed in the health block reference below.2
List a server's tools
List every tool a server advertised when it was bound, before the entry’s selection, with whether the entry picks it and whether it is bound.The command reads
GET /api/mcp-status/{title}/advertised-tools, a read-tier route: the answering worker answers from the listing it bound the server from, and never starts the server or connects to it. Each row gives the tool’s served name, whether the selection picks it (selected), whether the live binding carries it (bound), and, for a picked tool that is not bound, its unbound_reason: preset_conflict when a preset owns its name, unusable_schema when its input schema cannot be adapted, or pending_bind when no bind has applied the entry’s current selection yet (a bind is in progress, or the next reload binds it). A name the entry’s include (or else exclude) list carries that the server no longer advertises follows as a not advertised row (unadvertised in the JSON).With no listing held, the read refuses: 503 with code mcp-server-starting while the server’s start is in progress, 503 with code mcp-server-not-loaded when the answering worker has not loaded the server, and 502 with the recorded cause when its last start or bind failed. An unknown title is a 404.3
Replace the set at runtime
Write the full replacement
mcp list to a JSON file, then apply it. This hot-reloads the section without restarting the server. set remains the bootstrap and restore path — use it to lay down the whole section at once.4
Add or remove one entry
Change a single server without rewriting the whole list.
tai mcp add appends the entries in a JSON file — one entry object, a bare array, or an object carrying an "entries" list — and refuses a title that already exists unless you pass --replace, which swaps the existing entry in place. tai mcp remove deletes one entry by its title. Both hot-reload the section.remove is a persisted change — it edits the manifest, so the server stays gone across a restart. deregister is a runtime-only detach — it drops the server’s tools from the live registry but leaves the manifest untouched, so the next reload brings it back.5
Reload one server
Re-probe a single server by its manifest title and re-attach it if it is viable. A successful reload also drops that server’s pooled dispatch session, so the next tool call opens a fresh connection instead of reusing a stale one. A reload that finds the server unreachable reports it unavailable and leaves the session as is — once the server is back, the next tool call reconnects on its own; while it stays down, calls return the structured error described below.
A server that fails its viability probe is skipped, not fatal — the rest of the manifest still loads. Inspect skipped servers under
tai mcp status.The health block
Each health entry has four fields:
health covers every bound and failed title. A server that has never been called carries the block at its empty values — last_success, last_error, and failing_since null, consecutive_failures 0. A server that has only ever succeeded carries last_success with last_error and failing_since null. An auth-blocked dispatch records nothing: it signals credential state, not connection health, so it leaves the health block untouched. The health is this worker’s own passive view — per process, so a fleet report reads each worker’s dispatches separately.
When a mounted server’s tools fail
A tool call against a mounted server whose connection has dropped gets one automatic reconnect attempt. If the reconnect succeeds the call runs on the fresh session. If the server is still unavailable, the call returns a structured error whosecode is upstream_mcp_unavailable, with a message that names the entry by its configured title. The raw disconnect detail never reaches that message — it is logged at the dispatch seam, not surfaced to the caller. A dispatch that fails for another reason — a transport error, a timeout, or an output-schema mismatch — returns a structured error whose code is mcp_dispatch_failed, naming the tool and the server rather than a generic 500; the raw detail is logged and recorded on that server’s health entry.
A mounted entry that carries a connection’s reference but whose url is not the server its provider defines for that sub-service is refused before any credential is sent: the call fails with a message that names the entry and both repairs. Reconnecting the account (or, for a provider without sign-in, saving the connection’s services again) rewrites the entry the connector wrote for that connection. Any other entry that carries the reference is not the connector’s own, so a reconnect leaves it as it is and it keeps being refused until the reference is removed from it.
See also
- Transports — the HTTP, SSE, stdio, and UDS paths a mounted server can use.
- The manifest — the
mcpsection and its selection fields. - Live operations — hot-reload and deregister at runtime.
- CLI reference — the full
tai mcpsurface.

