MCPHub reads the following environment variables at startup. The authoritative list is whatever appears under process.env.* in src/; this page summarizes everything currently read by the code on main.
Core application
| Variable | Default | Description |
|---|
PORT | 3000 | HTTP server port. |
BASE_PATH | '' | Mount-point prefix when MCPHub is served under a sub-path (e.g. /mcphub). |
DEFAULT_REQUEST_TIMEOUT | 60000 | Default request timeout (ms) for upstream MCP server requests when a server does not set options.timeout. |
INIT_TIMEOUT | 300000 | Initial timeout (ms) used when starting upstream MCP servers. |
READONLY | false | Reject mutating dashboard API requests. GET and ${basePath}/tools/* remain allowed. |
DISABLE_WEB | false | Skip serving the bundled dashboard. The API and MCP transport routes still run. |
TRUST_PROXY | auto | Express trust proxy setting. Accepts true, false, a hop count (1, 2, …), or names like loopback,linklocal,uniquelocal. Defaults to 1 inside Docker/Kubernetes, false otherwise. |
MCPHUB_SETTING_PATH | mcp_settings.json | Override the path of the settings file used by file-mode storage. |
NODE_ENV | development | development / production / test. |
DEBUG | false | Set to true to enable verbose startup logging for frontend-path discovery. |
Authentication
| Variable | Default | Description |
|---|
ADMIN_PASSWORD | random | Password seeded for the bootstrap admin user when no users exist yet. If unset, a 24-character random password is generated and printed to the logs. |
JWT_SECRET | random per-process | Secret used to sign JWTs issued by /api/auth/login. See the warning below — this must be set explicitly in any non-development deployment. |
JWT_SECRET must be set to a persistent, high-entropy value (≥ 32 random bytes) in staging and production. If it is left unset, MCPHub generates a new random secret on every process start, which means: every restart invalidates all outstanding sessions and bearer tokens; horizontally-scaled instances cannot share sessions because each replica signs with its own secret; and CI / container restarts silently log everyone out. Generate one with openssl rand -hex 32 and store it in your secret manager. Treat it like any other long-lived cryptographic key — rotation requires invalidating outstanding JWTs.
Better Auth (optional social login)
Better Auth is initialized once at process startup in src/betterAuth.ts. Because of that, changing any Better Auth setting still requires a restart.
For non-secret Better Auth settings, MCPHub resolves values in this order:
BETTER_AUTH_* environment variables
systemConfig.auth.betterAuth (from mcp_settings.json or the database-backed system config)
- Built-in defaults
Provider client credentials remain environment-variable only.
Better Auth currently requires PostgreSQL-backed storage in MCPHub. In practice that means DB_URL must be configured; file-only mode does not support Better Auth sessions.
| Variable | Description |
|---|
BETTER_AUTH_ENABLED | Master switch for Better Auth. Better Auth is still disabled automatically when MCPHub is not in database mode or when no provider has the required credentials. |
BETTER_AUTH_URL | Public base URL used to build Better Auth redirect URIs. This now takes precedence over systemConfig.install.baseUrl. Its origin is also auto-added to the trusted origin list. |
BETTER_AUTH_BASE_PATH | Mount path for the Better Auth handler. Defaults to /api/auth/better. |
BETTER_AUTH_TRUSTED_ORIGINS | Optional additional origins allowed to start Better Auth login flows. Accepts a comma-separated list, whitespace-separated list, or JSON array. When omitted, MCPHub still auto-trusts the origins from BETTER_AUTH_URL and systemConfig.install.baseUrl. |
BETTER_AUTH_GOOGLE_ENABLED | Enable or disable the Google provider after credentials are present. Defaults to true. |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Google OAuth app credentials. |
BETTER_AUTH_GITHUB_ENABLED | Enable or disable the GitHub provider after credentials are present. Defaults to true. |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | GitHub OAuth app credentials. |
BETTER_AUTH_OIDC_ENABLED | Enable or disable the generic OIDC provider. Defaults to false. |
BETTER_AUTH_OIDC_PROVIDER_ID | Provider identifier passed to Better Auth’s generic OAuth plugin. Defaults to oidc. |
BETTER_AUTH_OIDC_DISCOVERY_URL | Discovery URL for a local OIDC issuer (for example Keycloak, Authentik, or Dex). |
OIDC_DISCOVERY_URL | Legacy alias for BETTER_AUTH_OIDC_DISCOVERY_URL. It is also still useful for ${OIDC_DISCOVERY_URL} interpolation inside mcp_settings.json. |
BETTER_AUTH_OIDC_SCOPES | Optional requested scopes for the local OIDC provider. Accepts a comma-separated list, whitespace-separated list, or JSON array. Defaults to openid profile email. |
BETTER_AUTH_OIDC_PKCE | Enable or disable PKCE for the local OIDC provider. Defaults to true. |
BETTER_AUTH_OIDC_PROMPT | Optional prompt parameter for the local OIDC provider (login, consent, select_account, etc.). |
OIDC_CLIENT_ID / OIDC_CLIENT_SECRET | Client credentials for a local OIDC provider such as Keycloak, Authentik, or Dex. |
You can still store the non-secret Better Auth settings in systemConfig.auth.betterAuth, but BETTER_AUTH_* variables now override those values at startup. A full env-only local OIDC example looks like this:
BETTER_AUTH_ENABLED=true
BETTER_AUTH_URL=https://mcphub.example.com
BETTER_AUTH_BASE_PATH=/api/auth/better
BETTER_AUTH_TRUSTED_ORIGINS=https://dashboard.example.com,https://admin.example.com
BETTER_AUTH_OIDC_ENABLED=true
BETTER_AUTH_OIDC_PROVIDER_ID=local-oidc
BETTER_AUTH_OIDC_DISCOVERY_URL=https://auth.example.com/.well-known/openid-configuration
BETTER_AUTH_OIDC_SCOPES=openid,profile,email
BETTER_AUTH_OIDC_PKCE=true
BETTER_AUTH_OIDC_PROMPT=login
OIDC_CLIENT_ID=your-oidc-client-id
OIDC_CLIENT_SECRET=your-oidc-client-secret
Storage
MCPHub picks file or database storage at boot:
useDatabase = (USE_DB === 'true') OR (USE_DB unset AND DB_URL is set)
| Variable | Default | Description |
|---|
USE_DB | unset | Force database mode (true) or file mode (false). |
DB_URL | unset | PostgreSQL connection URL. Required for database mode. |
USE_DAO_LAYER | unset | Force the DAO layer on even in file mode (advanced; usually unnecessary). |
DB_POOL_SIZE | 10 | Maximum TypeORM connection pool size. |
DB_POOL_IDLE_TIMEOUT | 30000 | Idle pool connection timeout (ms). |
DB_CONNECTION_TIMEOUT | 30000 | Initial connect timeout (ms). |
DB_CONNECTION_RETRY_DELAY | 2000 | Base delay between connection retries (ms). |
DB_MAX_CONNECTION_RETRIES | 5 | Maximum connection retry attempts at startup. |
DB_ENABLE_HEALTH_CHECK | true | Run periodic DB health probes (consumed by /health). |
DB_HEALTH_CHECK_INTERVAL | 30000 | Health-probe interval (ms). |
See Database Configuration for end-to-end setup.
Smart Routing & embeddings
Smart Routing performs vector search over upstream tools to surface only the few most relevant tools for a given query. It requires Postgres + pgvector and an embedding endpoint.
| Variable | Default | Description |
|---|
SMART_ROUTING_ENABLED | false | Master switch for smart routing. |
SMART_ROUTING_EMBEDDING_PROVIDER | openai | Embedding provider. Currently openai or azure_openai. |
SMART_ROUTING_EMBEDDING_ENCODING_FORMAT | provider default | Override the encoding_format passed to the embedding API. |
SMART_ROUTING_BASE_PACING_DELAY_MS | 0 | Base delay (ms) applied between embedding requests to avoid rate limits. |
SMART_ROUTING_PROGRESSIVE_DISCLOSURE | unset | When set, expose progressively-disclosed tool metadata (see Smart Routing docs). |
SMART_ROUTING_SERVER_DESCRIPTION_MODE | names | Control how search_tools lists available servers: names for server names only, full to include server descriptions/instructions when available. |
EMBEDDING_MODEL | text-embedding-3-small | Embedding model name. |
EMBEDDING_MAX_TOKENS | per-model | Override token truncation limit before embedding. Useful to match a local inference server’s batch size. |
OpenAI
| Variable | Description |
|---|
OPENAI_API_KEY | API key. |
OPENAI_API_BASE_URL | Override the base URL (set this for any OpenAI-compatible endpoint, e.g. LocalAI). |
Azure OpenAI
| Variable | Description |
|---|
AZURE_OPENAI_API_KEY | API key. |
AZURE_OPENAI_ENDPOINT | Resource endpoint (e.g. https://my-resource.openai.azure.com). |
AZURE_OPENAI_API_VERSION | API version (e.g. 2024-02-01). |
AZURE_OPENAI_EMBEDDING_DEPLOYMENT | Deployment name. |
AZURE_OPENAI_EMBEDDING_MODEL | The underlying OpenAI model the deployment maps to (e.g. text-embedding-3-small). Used for tokenizer and limit selection. |
MCPRouter (optional upstream catalog)
| Variable | Description |
|---|
MCPROUTER_API_KEY | API key for the MCPRouter cloud catalog. |
MCPROUTER_API_BASE | Override the API base URL (default https://api.mcprouter.to/v1). |
MCPROUTER_REFERER | Optional Referer header sent on MCPRouter requests. |
MCPROUTER_TITLE | Optional Title header sent on MCPRouter requests. |
Configuration examples
Development
NODE_ENV=development
PORT=3000
JWT_SECRET=dev-secret-key
Production with database + Smart Routing
NODE_ENV=production
PORT=3000
BASE_PATH=
JWT_SECRET=replace-with-32+-random-bytes
USE_DB=true
DB_URL=postgresql://mcphub:secret@db:5432/mcphub
SMART_ROUTING_ENABLED=true
OPENAI_API_KEY=sk-...
EMBEDDING_MODEL=text-embedding-3-small
Loading order
MCPHub loads variables in this order; later sources override earlier ones:
- System environment variables.
.env.local (gitignored).
.env.{NODE_ENV} (e.g. .env.production).
.env.
dotenv-expand is enabled, so ${VAR} interpolation works inside .env* files.
Security best practices
- Never commit secrets —
.env* files are gitignored by default; keep it that way.
- Always set
JWT_SECRET explicitly in production.
- Rotate bearer keys (
/api/auth/keys) and OAuth client secrets periodically.
- Use Docker / Kubernetes secrets for container deployments instead of plain
-e.