Choosing a transport
tai serve takes --transport. HTTP is the default; the MCP endpoint is then
served under /mcp.
- HTTP — the default network transport, for MCP clients over the network.
- SSE — a streaming HTTP transport.
- STDIO — for a client that launches the server as a subprocess and talks over its standard streams.
The UDS path
For same-machine communication,--uds <path> binds the server to a
Unix-domain socket instead of a host and port — a fast local path that avoids the
network stack. tai42-kit supplies the client-side UDS transports and a selector,
get_mcp_transport, that picks the right transport for a target: a UDS transport
for a socket, or a plain config for a networked server. A mounted external MCP
server can be reached over any of these — the manifest’s mcp entry sets url,
uds, or command per server.
Statefulness and workers
The HTTP transports are stateful per process: each MCP session is pinned to the worker that created it. Under multiple workers a session opened on one worker breaks when another worker handles its next request, sotai serve refuses
--workers greater than one on any stateful transport (http, streamable-http,
sse) with an error that names the fix.
The fix is --stateless-http, which turns on the underlying stateless HTTP mode and
lifts the refusal — for the HTTP transports only. It has no meaning for sse or
stdio (SSE has no stateless mode and STDIO has no HTTP sessions), so combining it
with either is itself refused.
More than one worker also needs the worker bus, or sibling
workers would serve stale config after a reload with no channel to converge on, and
shared sub-MCP and hooks stores (SUB_MCP_REDIS_URL
and HOOKS_REDIS_URL; TAI_DEFAULT_REDIS_URL stands in for them and the bus), or a sub-MCP app or
a hook registered through one worker would be unknown to its siblings. tai serve
hands its worker count to each worker, and each worker refuses to boot without them,
after it applies the stored env; tai serve then exits non-zero. So a multi-worker
HTTP server needs --stateless-http, TAI_BUS_REDIS_URL, and both stores.
Embedding the runtime in your own process applies the session-pinning rule the same
way: pass
create_app(manifest_path="manifest.yml", stateless_http=True) to run
several workers. The bus and the stores, though, are a documented limitation when
you embed — the worker count reaches the workers rules only from tai serve, and
in-process code cannot count sibling processes, so a create_app(..., stateless_http=True) factory run under an external --workers counts each process
as one worker and passes the workers rules even with no bus and per-process stores.
Set TAI_BUS_REDIS_URL, SUB_MCP_REDIS_URL and HOOKS_REDIS_URL yourself in any
multi-process embed. See
Multi-worker and fleet sync.
Before binding a UDS path,
tai serve cleans up a stale socket left by an unclean
shutdown, but refuses to unlink a socket that answers a connect probe (a live
server) or a path that is not a socket. This keeps one command from clobbering
another server’s socket.tai serve options.
