Environment
Every environment variable the server reads, with defaults and security notes.
All configuration is loaded by src/config/app.config.ts from environment variables, validated with a Zod schema, and exposed as a typed APP_CONFIG object. The schema is the single source of truth — this page mirrors it verbatim.
Required variables
| Variable | Default | Notes |
|---|---|---|
HASS_HOST | http://homeassistant.local:8123 | Base URL of your Home Assistant instance. Override in production to your real host. |
HASS_TOKEN | — | Long-lived access token from HA. Required for any tool call to succeed. The server refuses to start in production without it. |
JWT_SECRET | — | At least 32 characters. Used by the custom HTTP entry point to sign session tokens. No default in production; the schema throws if it’s missing or too short. Generate with openssl rand -base64 48. |
Optional variables
Server
| Variable | Default | Notes |
|---|---|---|
PORT | 4000 | HTTP listen port. |
NODE_ENV | development | development / production / test. The server relaxes some checks in development and test. |
Logging
| Variable | Default | Notes |
|---|---|---|
LOG_LEVEL | info | error / warn / info / debug / trace. |
LOG_DIR | logs | Directory for rotated log files. |
LOG_MAX_SIZE | 20m | Size before rotation. Accepts the s/m/h/d units used by winston-daily-rotate-file. |
LOG_MAX_DAYS | 14d | How long to keep old log files. |
LOG_COMPRESS | false | Whether to gzip rotated files. Set to true to save disk on busy servers. |
LOG_REQUESTS | false | If true, the HTTP middleware logs every request. Off by default to keep stdout quiet. |
Rate limiting
| Variable | Default | Notes |
|---|---|---|
RATE_LIMIT_WINDOW | 15 (minutes) | Sliding window size. The rate limiter is per-IP, applied to the /mcp endpoint. |
RATE_LIMIT_MAX | 100 | Max requests per window per IP. Set higher on a single-user LAN, lower on a public deploy. |
Server-Sent Events
| Variable | Default | Notes |
|---|---|---|
SSE_MAX_CLIENTS | 1000 | Max concurrent SSE clients (the WebSocket stream). The server starts rejecting new connections above this. |
SSE_PING_INTERVAL | 30000 (ms) | How often to send a ping to keep idle connections open. |
Speech features
The speech subsystem is opt-in. Setting ENABLE_SPEECH_FEATURES=true turns on the wake-word, STT, and TTS tools. Each can be enabled independently.
| Variable | Default | Notes |
|---|---|---|
ENABLE_SPEECH_FEATURES | false | Master switch. Must be true to use any speech tool. |
ENABLE_WAKE_WORD | false | Listens for a wake word locally (uses Porcupine). |
ENABLE_SPEECH_TO_TEXT | false | Whisper-based STT. |
WHISPER_MODEL_PATH | /models | Directory containing the Whisper weights. |
WHISPER_MODEL_TYPE | base | Whisper model size: tiny / base / small / medium / large. |
Where the values land in code
The Zod-parsed APP_CONFIG is re-exported from src/config/app.config.ts and imported wherever typed access is needed:
import { APP_CONFIG } from "@/config/app.config";
const port = APP_CONFIG.PORT; // number, defaulted
const token = APP_CONFIG.HASS_TOKEN; // string | undefined
A handful of legacy call sites read process.env directly (the rate limit, the speech flags). Those are documented inline. New code should import from APP_CONFIG so the Zod defaults apply consistently.
Validation behavior
app.config.ts performs an additional check at module load:
- If
SMITHERY_SCAN=true, skipHASS_TOKENvalidation. This is the env var Smithery sets while it’s probing the server’s tool manifest, so the server can boot without credentials. - If
HASS_TOKENis empty or undefined, also skip the check (the so-called “discovery mode” — tools can be listed but not called). - Otherwise, throw if any required env var is missing.
This is what lets Smithery and the build pipeline list tools without provisioning HA.
Loading from .env
dotenv is loaded by the entry points (src/index.ts, src/stdio-server.ts, src/http-server.ts) before the config is read. Put a .env file in the project root for local development:
HASS_HOST=http://192.168.1.50:8123
HASS_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT_SECRET=$(openssl rand -base64 48)
PORT=4000
The .env file is git-ignored. Never commit a real token.
Next
- Authentication — how JWT and the token manager work.
- Tools — tool-level configuration (annotations, read-only mode, etc.).