Skip to content
unzoi docs
Search and navigation
Start here
REST API
MCP
Limits and plans
Agent clients
SDKs
Guides

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

VariableEffect
NAI_LOCAL_DIRShard directory, for the local target. Defaults to /tmp/nai-shard.
NAI_PREFIXObject-storage prefix for the hetzner target. Defaults to news/.
NAI_SEMANTIC1 to enable semantic and hybrid ranking. Needs a reachable embedding service.
NAI_EMBED_URLThe embedding service, when NAI_SEMANTIC=1.
NAI_API_KEYAuthenticates 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_status reports the real plan.
  • Without one, the process runs in open mode: no plan, no clamping, and account_status says 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.