Environment Variables
Complete reference of every environment variable read by the bundle. If you are setting the bundle up for the first time, walk through the steps in Installation first - this page is the reference you return to when tuning a running system.
All agent-server-specific variables use the AGENT_SERVER_ prefix to
avoid colliding with unrelated Symfony or Node variables. Standard
Node conventions (PORT, HOST, NODE_ENV) and the PIMCORE_*
shared variables stay un-prefixed.
Env vars are loaded into the agent-server container via Docker
Compose's env_file directive pointing at .env.local. Env vars used
by the PHP side are loaded the usual Symfony way.
Core runtime
| Variable | Default | Description |
|---|---|---|
PORT | 3032 | Agent-server port |
HOST | 0.0.0.0 | Bind host |
NODE_ENV | production | development / production / test |
PIMCORE_INTERNAL_URL | http://nginx | Internal URL the agent-server uses to reach Pimcore |
PIMCORE_CONFIG_API_PATH | /pimcore-studio/api/bundle/agent/configurations/export | Path (appended to PIMCORE_INTERNAL_URL) of PHP's agent-config export endpoint the agent-server reads at startup and on reload |
AGENT_SERVER_LOG_LEVEL | info | verbose / debug / info / warn / error |
AGENT_SERVER_CORS_ORIGIN | * | Allowed CORS origins |
Mercure (shared - reused, not new)
Live cross-client chat sync publishes turn events to Studio's per-user Mercure topic. The agent-server reuses the
existing shared Mercure variables - the same secret and hub URL the rest of the stack already uses (root .env).
These are deliberately un-prefixed (they are not agent-server internals) and must be forwarded into the
agent-server service's environment: block in docker-compose.yaml.
| Variable | Default | Description |
|---|---|---|
MERCURE_JWT_KEY | - | Shared HMAC key the agent-server signs its publisher JWT with - must equal the key the hub validates against. Blank/unset → live sync disabled (no-op publisher, startup warning); chat still works, PHP remains the record. Must be ≥ 32 chars. |
MERCURE_SERVER_URL | http://mercure/.well-known/mercure | Internal hub publish endpoint the agent-server POSTs private updates to. |
Do not introduce AGENT_SERVER_MERCURE_* variants - one secret, one source of truth. For the model these drive
see Architecture → Real-time Sync and the publisher JWT in
Architecture → Authentication.
LLM adapter
Provider and model configuration is defined in the Symfony config tree under pimcore_agent.inference, not as
environment variables. The agent-server reads the provider config from PHP's config export endpoint at startup and
on every reload. See Inference for the conceptual overview and
Inference Providers for the full schema and examples.
The bundle ships a default copilot provider whose token references AGENT_SERVER_GH_TOKEN - a GitHub PAT
with the Copilot Requests permission. It is the one inference value a fresh install must set. It carries the
AGENT_SERVER_ prefix because it backs the shipped provider rather than being a user-named external secret.
| Variable | Default | Description |
|---|---|---|
AGENT_SERVER_GH_TOKEN | - | GitHub PAT (Copilot Requests permission) used by the shipped copilot provider's ${AGENT_SERVER_GH_TOKEN} placeholder. Required only while the default GitHub-Copilot provider is in use. |
Provider tokens for any further providers you add (API keys, other PATs) are referenced in provider blocks using the
${VAR_NAME} placeholder syntax and resolved against the agent-server container's own environment at session-config
build time. Name those variables however you like:
# .env.local - forwarded into the agent-server container
AGENT_SERVER_GH_TOKEN=ghp_... # used by the shipped default copilot provider
ANTHROPIC_API_KEY=sk-ant-...
HF_ENDPOINT_URL=https://xyz.us-east-1.aws.endpoints.huggingface.cloud/v1
HF_API_TOKEN=hf_...
# config/packages/pimcore_agent.yaml
pimcore_agent:
inference:
default_provider: anthropic-cloud
providers:
anthropic-cloud:
auth_mode: byok
provider: anthropic
token: '${ANTHROPIC_API_KEY}' # resolved agent-server-side
default_model: claude-sonnet-4-5
The following variables remain relevant for the LLM layer:
| Variable | Default | Description |
|---|---|---|
AGENT_SERVER_ADAPTER | copilot | copilot for real LLM sessions, dummy for local dev |
AGENT_SERVER_DUMMY_MODE | echo | Dummy adapter mode: echo / scripted / random |
AGENT_SERVER_MODEL_CHECK_INTERVAL_MS | 3600000 | Periodic model validation interval in ms (0 to disable) |
Provider configuration is required. The agent-server reads no
AGENT_SERVER_COPILOT_*provider/model variables - all LLM provider, model, auth, and compat settings come from thepimcore_agent.inferenceconfig (Inference Providers). An agent that resolves to no provider (noprovider:and nodefault_provider) fails with a clear error. See the migration reference if you previously used theAGENT_SERVER_COPILOT_*variables.
Sessions, files, and admin
| Variable | Default | Description |
|---|---|---|
AGENT_SERVER_AUTH_CACHE_TTL | 60 | Auth-validation cache TTL in seconds |
AGENT_SERVER_INACTIVITY_TIMEOUT_MS | 300000 | End the stream after this many ms with no SDK events (suppressed while an ask_user is pending) |
AGENT_SERVER_FIRST_EVENT_DEADLINE_MS | 90000 | Per-inference first-output deadline: how long an accepted send() may stay silent (no first SDK event) before the turn fails fast |
AGENT_SERVER_ASK_USER_TIMEOUT_MS | 600000 | How long to wait for a reply to an ask_user / pause prompt before auto-resolving and ending the turn. Min 10s, max 30 min (1800000). Kept finite: a parked prompt pins a session slot. |
AGENT_SERVER_MAX_SESSIONS | 1000 | Max in-memory sessions retained; when exceeded, the oldest idle (non-active, non-ask_user-pending) session is evicted |
AGENT_SERVER_UPLOAD_DIR | /app/uploads | File attachment directory (bind-mounted from ./var/tmp/agent-uploads in Compose) |
AGENT_NODE_STAGING_BASE_DIR (PHP side) | /app/uploads | Base path the PHP MCP tools hand the agent for staged files - must equal the agent-server's AGENT_SERVER_UPLOAD_DIR. Set on the php service. Override both (e.g. to /agent-uploads) when the dev override mounts uploads outside /app to avoid a nested bind mount - see Development. |
AGENT_SERVER_SKILLS_MATERIALIZE_DIR | /app/var/skills | Target directory for materialized skill files |
AGENT_SERVER_COPILOT_STATE_DIR | /app/.copilot-state | Storage root for the Copilot runtime's own session state (event logs, session.db, checkpoints). Passed as the client baseDirectory, which sets COPILOT_HOME on the spawned runtime. Bind-mounted from ./var/tmp/copilot-state in Compose so SDK history survives container recreation and resumeSession can reload it - see Session Storage. |
AGENT_SERVER_STAGED_FILE_MAX_AGE_MS | 86400000 | Max age of staged asset files before auto-cleanup (24 h default) |
AGENT_SERVER_STAGED_FILE_CLEANUP_INTERVAL_MS | 3600000 | How often the cleanup job runs (1 h default; set to 0 to disable) |
AGENT_SERVER_ADMIN_TOKEN | - | Shared bearer token for the reload and export endpoints |
Setting either AGENT_SERVER_STAGED_FILE_MAX_AGE_MS or AGENT_SERVER_STAGED_FILE_CLEANUP_INTERVAL_MS to 0
disables periodic cleanup entirely. User-uploaded files (under uploaded/) are never auto-cleaned - only session
deletion removes them. For the directory layout and why only staged/ is swept, see Features → File Attachments.
Pimcore Studio integration (PHP side)
These variables are set on the PHP side (Pimcore's .env /
.env.local) and used by the Pimcore Studio configuration system.
| Variable | Default | Description |
|---|---|---|
AGENT_SERVER_URL | http://agent-server:3032 | Agent-server URL, used for reload triggers. Defaults to the compose-convention name; override only for a different name/port |
AGENT_SERVER_ADMIN_TOKEN | "" | Must match the agent-server container's AGENT_SERVER_ADMIN_TOKEN |
Third-party MCP server credentials
Agent YAML files support ${VAR_NAME} placeholders in MCP URLs and
headers (for third-party servers only - Pimcore-internal MCP servers
use cookie forwarding). Placeholder names are user-defined: the
agent-server interpolates whatever placeholders it finds, at
session-resolve time, against its own container environment.
mcpServers:
pimcore-datahub:
type: http
url: ${MCP_PIMCORE_URL}
headers:
Authorization: "Bearer ${MCP_PIMCORE_TOKEN}"
Names here are user-defined - the convention in the bundled presets
is MCP_<SERVICE>_URL / MCP_<SERVICE>_TOKEN, but any name works.
They are not prefixed with AGENT_SERVER_ because they identify
external services, not agent-server internals. See
MCP Servers → Environment variable interpolation.
Migration from single-provider env vars
Projects that configured the LLM layer with AGENT_SERVER_COPILOT_* variables can migrate to named providers
by adding a pimcore_agent.inference block and setting provider tokens as discrete env vars. The table below
maps each old variable to its equivalent in the provider config.
| Old environment variable | Equivalent in pimcore_agent.inference.providers.<name>: |
|---|---|
AGENT_SERVER_COPILOT_AUTH_MODE | auth_mode: byok or auth_mode: github |
AGENT_SERVER_COPILOT_PROVIDER | provider: openai|anthropic|azure |
AGENT_SERVER_COPILOT_API_KEY | token: '${YOUR_API_KEY_VAR}' |
AGENT_SERVER_COPILOT_BASE_URL | base_url: '...' or base_url: '%env(VAR)%' |
AGENT_SERVER_COPILOT_MODEL | default_model: ... |
AGENT_SERVER_TITLE_MODEL | title_model: ... |
AGENT_SERVER_AVAILABLE_MODELS | available_models: map (one entry per model id) |
AGENT_SERVER_COPILOT_PROVIDER_COMPAT | compat: cerebras|openai-strict |
AGENT_SERVER_GH_TOKEN | token: '${GH_TOKEN_VAR}' with auth_mode: github |
The AGENT_SERVER_COPILOT_* variables are no longer read by the agent-server; configure providers via
pimcore_agent.inference instead. Keep provider secrets as discrete env vars and reference them from a provider's
token: with the ${VAR} placeholder syntax (resolved agent-server-side).
Known BYOK limitations (Copilot SDK upstream bugs)
These are upstream issues in the GitHub Copilot SDK, tracked at github/copilot-sdk#637:
- No streaming deltas with Anthropic BYOK. Responses arrive as a
single
assistant.messagewhen the LLM finishes, not word-by-word. The agent-server extracts the full text once the message arrives so chat still works - users just see the full response appear at once instead of streaming. - No thinking / reasoning events with Anthropic BYOK. The
streamThinkingagent field is effectively ignored; Anthropic extended thinking is not exposed via the SDK. - Model discovery requires
available_modelsin BYOK mode. The SDK's built-in model catalog only works in GitHub mode. Without anavailable_modelsblock in the provider config, the Studio UI model dropdown is empty and typos in agentmodelfields only surface at runtime.