tai serve CLI.
tai42_skeleton.asgi.create_app returns the same ASGI app tai serve runs. You own the process, the uvicorn flags, and the hosting — run it with your own uvicorn main:app, or mount it inside an existing FastAPI/Starlette service.
There are two entry modes, and they trade the same way. The tai CLI is the managed path: its command family — tai serve, tai backend, tai metrics — owns the worker fleet, the metrics sidecar, the backend worker processes, UDS binding, and logging, a full ops surface across a few commands. The factory is the embedded path: you drop the runtime into a service you already run and keep your own process manager, uvicorn invocation, and logging story. Pick the CLI for a standalone server; pick the factory to embed the runtime in an existing process.
Serve directly
The minimal host is a module that builds the app and a uvicorn command that serves it.main.py
/mcp, and the Studio SPA and API routes serve exactly as they do under tai serve — the factory returns the same app, so nothing about the request surface changes.
Mount inside an existing FastAPI or Starlette app
Mounting the app is not enough on its own. Starlette does not run a mounted sub-app’s lifespan, so the runtime never boots unless the host runs the tai app’s lifespan itself. Thelifespan helper returns the tai app’s lifespan context manager; compose it into the host’s lifespan, then mount the app:
main.py
One app per process
Thetai42_app handle is a process-global singleton, and so is the built app. A second create_app lifespan entered while another is still active in the same process raises RuntimeError rather than silently rebinding the handle. Sequential lifespans in one process — enter, exit, then enter again — stay legal. Two different apps means two processes.
Multi-worker and fleet sync
Run several workers withuvicorn main:app --workers 4, and the HTTP transports need create_app(..., stateless_http=True) — the same session-pinning rule the CLI enforces, because a stateful session is pinned to the worker that created it and a second worker would not see it. The factory cannot see your worker count, so this matrix is yours to get right. See Transports for the statefulness and worker rules in full.
Fleet convergence — a config reload reaching every worker — rides the app’s own worker bus, internal infrastructure you enable with TAI_BUS_REDIS_URL. It is not a backend capability and not a CLI feature, so an embedded worker joins the fleet the moment the bus is configured, exactly like a tai serve worker. With no bus configured a mutation applies in that one process and reaches no sibling — the correct behavior only for a genuinely single-process embed.
What the CLI owns that embedding does not
Some of whattai serve sets up, the factory deliberately leaves to you.
- Root logging. The factory configures no logging — bring your own. A tai config reload that changes
TAI_LOG_LEVELdoes not re-apply logging in an embedded process; that re-apply is CLI-owned. - Prometheus multiproc. An embedded single process serves the in-process registry at
/metrics. ExportPROMETHEUS_MULTIPROC_DIRyourself before the first import and you get multiproc mode instead — and you own that directory’s lifecycle, including wiping stale files between runs. - The metrics sidecar, backend worker processes (
tai backend), and UDS handling. These are fleet-orchestration concerns the CLI owns; the factory does none of them.
- Manifest env stamp. When you pass
manifest_path, the app writesTAI_MANIFEST_PATHinto the process environment for the lifespan’s duration, and restores the prior value on exit. Host code reading that variable during the tai app’s life sees the tai-set value. - Config env store. On every config reload — first reload onward, since boot-time
.envloading is a CLI-onlyload_dotenvthe factory never runs — tai’s config engine applies its managed.envstore to the process environment:os.environgains every key in the store and drops keys removed from it. Host code reading those keys sees tai-managed values. Keep tai env keys namespaced away from your host app’s, and never putTAI_MANIFEST_PATHorPROMETHEUS_MULTIPROC_DIRin the tai env store of an embedded deployment — the factory’s own lifespan stamp and the multiproc opt-in own those. - Default metrics registry. In the default in-process metrics mode, tai’s counters live in the process-wide
prometheus_clientdefault registry. So tai’s/metricsalso renders the host’s own default-registry metrics, and a host that exposes its own default-registry/metricsincludes tai’s counters. This is inherentprometheus_clientbehavior; the multiproc opt-in separates them. - Log-record redactor. The app installs a
logging.setLogRecordFactorywrapper that redacts secret-bearing connector-meta patterns from log records. In embed mode it applies only to the records tai’s operation feeds — thetailogger family (tai42_skeleton,tai42_kit,tai42_contract, and the rest) plus themcp/fastmcplibrary loggers the runtime drives, whose session layers are the primary token-carrying path — so an embedded tai app cannot leak connector secrets through the logs its operation produces, while your app’s own records pass through untouched. Under thetaiCLI, where tai owns the whole process, the wrapper covers every record in the process instead. It is fail-closed: a record the redactor cannot process is still emitted, but its message is replaced by an error marker and its args, exception, and stack are blanked — content is scrubbed rather than leaked.
manifest_path parameter is authoritative for the lifespan. The other three persist for the process lifetime: after a tai lifespan exits — sequential re-init is legal — reload-applied env keys linger, registered counters remain in the default registry, and the redactor’s factory wrapper stays installed, still inspecting every record the process creates and redacting the in-scope ones.
See also
- Deploy — running a standalone server with
tai serve. - Transports — the statefulness and worker rules the multi-worker matrix follows.
- Config and secrets — the config engine whose env store the runtime applies on reload.
tai42_skeleton.asgi— thecreate_appandlifespansignatures and parameters.

