Skip to main content

environment-variables

| mindconnect.namespace | local | The default namespace: the one start-up work runs in, and open to every signed-in user until mindconnect.namespace-admins names its admins — the runtime build, the tool warm-up, the seed loaders. A request or task that names none does not land here: with the namespace starter the scope is strict and an unbound thread that touches a store fails instead, because on a server that silence is a leak between namespaces. Bound to MC_NAMESPACE in the apps' yaml. | | mindconnect.agent.sub-agents.max-depth | 5 | How deep a chain of sub-agents may go before a turn is refused. Delegation itself comes from mc-agent-runtime-feature-subagents; without that module on the classpath an agent's roster yields no run_agent. | | mindconnect.task-queue.retention | — | How long finished task trees stay readable (ISO-8601, e.g. PT1H). Unset keeps them, which is what the Admin UI's task monitor reads. | | mindconnect.task-queue.maintenance-interval | PT20S | How often the queue renews leases, reclaims dead ones and applies the retention. | | mindconnect.task-queue.store | memory | memory, or jdbc for a store in the runtime's Postgres that several nodes share, each claiming with a lease. jdbc needs mindconnect.persistence=postgres. | | mindconnect.task-queue.node-id | host and pid | This node's name on a shared store. | | mindconnect.task-queue.lease | PT30S | How long a claim on a shared store holds before another node may take the task over. |--- title: Environment variables sidebar_position: 10​

Environment variables

This page lists the environment variables the agents area reads. Most are referenced from LLM configs as ${VAR} / ${VAR:default} placeholders, resolved at use time — see how API keys resolve.

Set them in your shell before starting the app, e.g.:

export ANTHROPIC_API_KEY=sk-ant-...
export TAVILY_API_KEY=tvly-...

Your own and your namespace's variables​

On a server several people share, a placeholder does not have to come from the process. A ${VAR} in an LLM config is looked up in this order, and the first place that has it answers:

  1. the user's own variables — set on the profile page (avatar in the header → Your variables), so everyone can bring their own API key;
  2. the namespace's variables — set by the namespace's creator on Namespaces & members, one key for everyone working there;
  3. the process environment — what the server was started with.

Two rules narrow that:

  • Your own variables reach a config's API key, nothing else. Its model, baseUrl and name resolve from the namespace's variables and the process alone. Otherwise anyone could point a shared config — whose key is still the installation's — at an endpoint of their own.
  • The default namespace has no variables of its own. Nobody created it and everyone works there, so it belongs to the installation: set its values in the server's environment. A namespace you create carries variables for everyone in it, and only its creator sets them — the same right as renaming or deleting it.

The same chain feeds the workflow engine's built-in env variable: a workflow run from the admin, from a chat (as a tool) or as a vector-store ingestion reads ${env.OPENAI_API_KEY} the same way.

Variables a tool asks you for​

A variable or a connection?

A variable is right when an operator could set the value once for everybody — TAVILY_API_KEY is the installation's, and the chain above lets a user override it. An account is not: a mailbox belongs to one person, and a person may have two. Those are connections and live on their own tab, not here.

Some tools need something only you can supply — your mailbox, your own key for a service. Such a tool declares the variables it needs, and the installation does the asking rather than leaving you to guess the names:

  • what has a sensible default is written for you on your first request after signing in — a port, a protocol, a default folder;
  • everything declared is listed on your profile under Your variables, in a second table saying what each one is, which tools use it, and whether it is set. Set opens the form with the name already in it;
  • what is required and has no value anywhere raises a notification that goes away by itself once you fill the value in.

A variable is never created empty: an empty user variable would answer the lookup and cut off the namespace and the server behind it. And nothing is asked of you for a name the namespace or the process already answers — an operator who put the value in the server's environment for everyone has settled it.

A config that says ${OPENAI_API_KEY} therefore uses your key when you have one, the namespace's when it has one, and the server's otherwise; the :default of a placeholder is the last word. Values are stored encrypted with MINDCONNECT_ENCRYPTION_SECRET_KEY and are never shown again once saved — the tables list names only. Removing a variable lets the next place answer.

The lookup is pluggable: the gateways take an EnvVarResolver, the servers wire the chain above (UserEnvVarResolver, NamespaceEnvVarResolver, EnvVarResolver.system()), a library gets the process environment unless it passes AgentRuntimeBuilder.envVarResolver(…) — a vault, say — and any Spring host may define an EnvVarResolver bean of its own.

Core​

VariableDefaultNotes
MINDCONNECT_ENCRYPTION_SECRET_KEY(none)Required. Encrypts stored LLM credentials. No default on purpose — the app fails to start without it. Must be 16, 24 or 32 characters (used directly as an AES key).
MC_PERSISTENCEfilefile keeps everything under mindconnect.data.base-dir; postgres keeps it in the database — see Persistence. Since 0.3.0.
MC_NAMESPACElocalThe namespace this process runs in: file data lives under <base-dir>/<namespace>/, Postgres rows carry it in their key. One process serves one namespace.
MC_NAMESPACE_ADMINS(empty)Who shapes the default namespace — a comma-separated list of e-mail addresses. Naming anybody closes it: from then on being signed in is not the same as being let in, and an account listed in no namespace is turned away at /no-access (403 for the API) instead of landing in the default one. Empty keeps it open to every signed-in user, who is an admin there — a single-user installation and the dev mode as they always were.
MC_EMAIL_DOMAINlocalThe domain a name without an @ becomes an address with. Namespaces list their people by e-mail, so an invitation can be written as david where every account is david@company.example; it is also the address an account gets whose identity provider hands over none (<user id>@<this domain>).
MC_TIME_ZONE(the JVM's zone)The installation's time zone, an IANA id such as Europe/Zurich: what a user's agents read "16:16" in until that user has a zone of their own — from their browser, the first time it is seen, or their profile page. Setting the container's TZ is no longer needed for that.
MC_POSTGRES_URLjdbc:postgresql://localhost:5432/mindconnectJDBC URL, used with MC_PERSISTENCE=postgres. Tables are created on start.
MC_POSTGRES_USER / MC_POSTGRES_PASSWORD(empty)Database credentials.
MC_UPLOAD_MAX_FILE_SIZE / MC_UPLOAD_MAX_REQUEST_SIZE25MB / 100MBUpload limits — one file, and all files of one upload request (the chat's attach dialog sends every selected file in one request). Bound to spring.servlet.multipart.max-file-size / max-request-size; an upload above either is answered with HTTP 413. The runtime sends a file inline to the model up to 20MB.

Runtime configuration (Spring properties)​

These are Spring properties; thanks to relaxed binding each one can also be set as an env var in SCREAMING_SNAKE form (e.g. MINDCONNECT_DATA_BASE_DIR).

PropertyDefaultNotes
mindconnect.persistencefilefile or postgres — bound to MC_PERSISTENCE in the apps' yaml.
mindconnect.namespacelocalThe default namespace: where a request or thread works that names none — bound to MC_NAMESPACE in the apps' yaml. Open to every signed-in user while namespace-admins names nobody.
mindconnect.namespace-admins(empty)The admins of that namespace, as e-mail addresses; naming anybody closes it (see MC_NAMESPACE_ADMINS).
mindconnect.email-domainlocalWhat a bare name becomes an address with, and the address of an account whose provider gives none.
mindconnect.time-zonethe JVM's zoneThe time zone of a user who has none of their own yet — an IANA id such as Europe/Zurich; a user's own comes from their browser or their profile page (see the user's time zone). A value that is no zone stops the app from starting. Bound to MC_TIME_ZONE in the apps' yaml.
mindconnect.postgres.*—url, username, password, pool-size (default 10) for postgres mode; bound to MC_POSTGRES_*.
mindconnect.data.base-dirdataRoot for all file persistence (definitions, configs, conversations, uploads, the users' homes); in postgres mode only the file-based side channels.
mindconnect.users.home<data.base-dir>/<namespace>/home/{user}Each user's directory on the server — a path with {user} in it. {user} is the user id when it consists of letters, digits, ., - and _ and does not start with a dot; any other id is reduced to those characters and gets + and a short hash of the id appended (alice@example.com → alice_example.com+…), so no two users share a directory. A session opened without a working directory works in its own directory under there (sessions/<id>), its uploads are put in sessions/<id>/uploads for the file tools, and it is the default root a working directory must lie under. Blank turns it off.
mindconnect.tools.base-diruser homeBase directory for bash and the file tools when a session has no working directory at all — sessions written before 0.5.2, a runtime without a users' home, tools run outside a session — security-relevant.
mindconnect.tools.working-dir-rootusers.homeThe root a session's working directory (workingDir on POST /api/sessions, the chat's directory chooser, /cd in the CLI) must lie under, and the tree the chat's directory picker shows. With {user} in it — /srv/mindconnect/users/{user} — every user gets a root of their own, created on first use, and sees nobody else's. The CLI sets /; the Admin UI app sets the user's home, or MC_WORKING_DIR_ROOT when given.
mindconnect.tools.disabled—Tools this installation does not offer at all, comma-separated (bash,process_kill,process_list). They leave every catalog and never resolve — an agent definition that names one goes without it, and the Admin UI's tool settings cannot switch it back on. MC_TOOLS_DISABLED in the server profile.
mindconnect.extensions.disabled(empty)Extensions switched off everywhere, by id, comma-separated — no namespace and no brand can switch them on. MC_EXTENSIONS_DISABLED in the server profile.
mindconnect.extensions.strictfalseWhat a problem with the extensions on the classpath means at start: false logs it and starts — two manifests with one id, a manifest that cannot be read, a jar that brings tools without a manifest; true refuses to start, for an installation that wants nothing on its classpath it has not been told about. MC_EXTENSIONS_STRICT in the server profile.
mindconnect.working-dirs.choicetrueWhether a user may choose a chat's directories. false: no folder button in the chat, workingDir/additionalDirs on POST /api/sessions, PUT /api/sessions/{id}/working-dir and GET /api/directories are refused (/cd and /add-dir in the CLI too), and every chat works in its own directory under users.home.
mindconnect.agent.instructions.user-dir~/.mindconnectWhere a user's standing instructions live (AGENTS.md, PROMPT.md or CLAUDE.md), read into every session's system prompt beside the project's own file. The default suits a desktop: one person, one home. A server runs as one service account, so put {user} in the value and each user gets a directory of their own. off drops the user scope.
mindconnect.agent.skills.user-dir~/.mindconnect/skillsWhere a user's own skills live — SKILL.md files they can load in every project, beside the ones this installation stores and the ones a project keeps in .mindconnect/skills/. Same shape as the line above: the default suits a desktop, a server puts {user} in the value, off drops the user scope. MC_SKILLS_USER_DIR in the server profile.
mindconnect.user.idapp-specificThe user id that owns sessions and data (the CLI ships a hard-coded default).
mindconnect.remote.url(unset)Points the CLI at a remote agent server instead of local mode.
mindconnect.remote.token(unset)Bearer token the CLI sends to a remote agent server that requires authentication; also read from MC_REMOTE_TOKEN.
mindconnect.code-exec.*—Sandbox limits for code_execute: runtime, network, languages, memory, cpus, timeout-seconds, idle-seconds. languages overrides images (python=python:3.12-slim); python defaults to ghcr.io/mindconnect-ai/code-exec-python:latest with the office libraries.
mindconnect.vector-store.*—Vector-store backend: backend, url, user, password, embedding-config (default embeddings). The memory backend keeps its files in <data.base-dir>/<namespace>/vector-stores.
mindconnect.file-store.*—File-store backend: backend. The filesystem backend keeps uploads in <data.base-dir>/<namespace>/files.
mindconnect.cors.allowed-origins*Origins that may call the REST endpoints from a browser (/api, /chat/api, /v1), comma-separated. * lets every origin call without credentials; a list of origins may also send the session cookie. Both server apps.
mindconnect.agent.trace.max-per-session50LLM call-trace retention per session.
mindconnect.branding.*—What the Admin UI calls itself: title, document-title, logo, logo-href, favicon, theme, stylesheets, assets-dir — see Branding. MC_BRANDING_TITLE, MC_BRANDING_LOGO and MC_BRANDING_ASSETS_DIR in the Admin UI's yaml.

Running on a server​

The apps' defaults are for one person on their own machine. On a server several users share, start them with the server profile (--spring.profiles.active=server, plus keycloak for login). It sets:

  • mindconnect.tools.disabled: bash,process_kill,process_list — bash runs as the server's account, with its environment and the whole file system, and nothing confines it to a chat's directory; without it there are no background processes to list or end. The file and document tools stay: they are confined to the chat's directories.
  • mindconnect.working-dirs.choice: false — nobody points a chat at a directory or browses the server; each chat works in its own directory under users.home, where its uploads are copied for the file and document tools. Deleting the chat removes it.
  • mindconnect.tools.working-dir-root empty, so a user's root is their own home.
  • mindconnect.tools.base-dir: <data.base-dir>/tools instead of the account's home.
  • mindconnect.agent.instructions.user-dir: off — set a path with {user} for one AGENTS.md per user.
  • mindconnect.agent.skills.user-dir: off — likewise, a path with {user} for one skills directory per user. The installation's own skills are unaffected.

Each value can be overridden (MC_TOOLS_DISABLED, MC_WORKING_DIR_ROOT, MC_TOOLS_BASE_DIR, MC_INSTRUCTIONS_USER_DIR, MC_SKILLS_USER_DIR). Uploads need local disk either way: on several nodes, users.home has to be shared storage for the file tools to find a chat's copy.

LLM providers​

VariableUsed byNotes
ANTHROPIC_API_KEYclaude-default, claude-haiku-defaultAnthropic API key
CLAUDE_MODELclaude-defaultOverride model id (default claude-sonnet-4-6)
CLAUDE_HAIKU_MODELclaude-haiku-defaultOverride model id (default claude-haiku-4-5)
OPENAI_API_KEYopenai-default, and through the agent-default alias every bundled agentOpenAI API key
OPENAI_MODELopenai-defaultOverride model id (default gpt-5.4-mini)
OPENAI_EMBEDDING_MODELopenai-embeddingsOverride the embedding model (default text-embedding-3-small; OPENAI_API_KEY is the key)
EMBEDDING_BASE_URLembeddingsLocal embedding server (default http://localhost:1234, LM Studio)
EMBEDDING_MODELembeddingsOverride the local embedding model (default text-embedding-nomic-embed-text-v1.5)
AZURE_OPENAI_API_KEYazure-openai-defaultAzure OpenAI key
AZURE_OPENAI_ENDPOINTazure-openai-defaultAzure resource endpoint URL
AZURE_OPENAI_DEPLOYMENTazure-openai-defaultDeployment name (default gpt-4o)
GEMINI_API_KEYgemini-defaultGoogle Gemini key
GEMINI_MODELgemini-defaultOverride model id (default gemini-2.0-flash)
SPEECH_TO_TEXT_MODELspeech-to-textOverride model id (default whisper-1)
SPEECH_TO_TEXT_BASE_URLspeech-to-textWhere the transcription endpoint lives (default https://api.openai.com) — point it at Groq or a local Whisper server

Local providers (lm-studio-default, gemma-reader, embeddings) need no key — they talk to LM Studio at http://localhost:1234.

Tools​

VariableUsed byNotes
TAVILY_API_KEYweb_searchRequired for web search tools

Authentication (Admin UI / Keycloak)​

See Authentication for what the modes mean.

VariableDefaultNotes
MC_AUTH_ENABLEDfalsefalse (default) runs the Admin UI without Keycloak. Keycloak login is enabled via the keycloak Spring profile (which sets this itself) — setting the variable alone is not enough. The agent server (mc-agent-api-app) has no login and is switched on by this variable alone: every request then needs a bearer token.
MC_DEV_USERmc_userAuto-login username used when auth is disabled
KC_ISSUER_URIhttp://localhost:8180/realms/mindconnectKeycloak realm issuer — the browser login (only with the keycloak profile) and the issuer bearer JWTs on /api/** and /v1/** are checked against. Agent server: no default; without it only API tokens are accepted.
MC_JWT_AUDIENCES(empty)Comma-separated; when set, a bearer JWT must carry one of these audiences
KC_CLIENT_IDmc-admin-uiOIDC client id
KC_CLIENT_SECRET(empty)OIDC client secret, if your client is confidential

Keycloak container (.env.docker)​

These are read by docker-compose.yml / start-keycloak.sh when bringing Keycloak up — copy .env.docker.example to .env.docker and fill them in:

VariableNotes
POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORDKeycloak's database
KEYCLOAK_ADMIN / KEYCLOAK_ADMIN_PASSWORDKeycloak admin console login
KC_PASSWORD_MC_USERPassword seeded for the mc_user user
KC_PASSWORD_MC_ADMINPassword seeded for the mc_admin user
KC_PASSWORD_MC_HRPassword seeded for the mc_hr user
KC_PASSWORD_MC_DEVPassword seeded for the mc_dev user

See Admin UI → Keycloak users for the seed users.

Keep secrets out of git

Put these in a local, git-ignored file (the repo uses mc.env / .env.docker, both ignored) and source it — never commit real keys.