Local stdio server
The engine ships an mcp binary that speaks the same protocol over stdio, against a shard you hold
yourself.
Running it
# Against a local shard directory.
NAI_LOCAL_DIR=/data/nai-shard cargo run --release --bin mcp -- local
# Against object storage.
HETZNER_S3_ENDPOINT=... HETZNER_S3_BUCKET=... NAI_PREFIX=news/ \
cargo run --release --bin mcp -- hetzner
stdout carries only protocol messages; status and logs go to stderr. Anything a client's stdio
transport reads on stdout must be JSON-RPC, which is why the binary is careful about it — a stray
println! is enough to break the handshake.
Configuration
| Variable | Effect |
|---|---|
NAI_LOCAL_DIR | Shard directory, for the local target. Defaults to /tmp/nai-shard. |
NAI_PREFIX | Object-storage prefix for the hetzner target. Defaults to news/. |
NAI_SEMANTIC | 1 to enable semantic and hybrid ranking. Needs a reachable embedding service. |
NAI_EMBED_URL | The embedding service, when NAI_SEMANTIC=1. |
NAI_API_KEY | Authenticates the session as a tenant, once at startup. |
Auth happens once
stdio has no per-request headers, so the process resolves NAI_API_KEY at startup and every tool call in
that session runs as that tenant. Two consequences worth knowing:
-
With a key, the tenant's archive depth applies to every query, exactly as it
does over HTTP, and
account_statusreports the real plan. -
Without one, the process runs in open mode: no plan, no clamping, and
account_statussays so. That is the right default for a local shard you own — there is no one to bill.
Client config
Anywhere a client takes a stdio command:
{
"mcpServers": {
"unzoi-local": {
"command": "/path/to/target/release/mcp",
"args": ["local"],
"env": {
"NAI_LOCAL_DIR": "/data/nai-shard"
}
}
}
} It is the same server
stdio and POST /mcp share one lifecycle implementation and one tool surface — the transport is the only
difference. A tool that behaves one way here behaves the same way hosted, which is the point: a local run is a
faithful rehearsal rather than an approximation.