Runtime Configuration

July 22, 2026 · View on GitHub

description: Runtime configuration reference - server, AI providers and keys, MCP exposure, observability, sandbox, and data dirs, set from the desktop app, Docker, or source.

Runtime Configuration

Spring AI Playground is a Spring Boot application, so it reads standard Spring configuration - application.yaml, OS environment variables, JVM system properties, and command-line arguments. Every knob below can be set the same way in all three launch modes; only the mechanism for supplying it differs.

This page is the single reference for those knobs. The task pages - Desktop App, Alternative Runtimes - show the everyday paths; come here for the full list.

How configuration is supplied { #how }

Precedence, lowest to highest (a later source overrides an earlier one):

  1. The bundled application.yaml (including the active profile section, ollama by default - the ollama / openai / mlx profiles are defined inline) - the defaults.
  2. An external application.yaml next to the runtime or under the app config directory.
  3. OS environment variables.
  4. JVM system properties (-Dkey=value).
  5. Command-line arguments (--key=value).

Property ↔ environment-variable mapping. Any property binds from an env var via Spring's relaxed binding - uppercase, dots and dashes to underscores. So spring.ai.playground.tool-studio.timeout-seconds is set by SPRING_AI_PLAYGROUND_TOOL_STUDIO_TIMEOUT_SECONDS.

Per launch mode:

ModeHow you set configuration
Desktop appThe launcher has a YAML editor and an environment-variable editor, plus seven provider templates (Ollama · OpenAI · OpenAI-Compatible for Ollama / llama.cpp / TabbyAPI / LM Studio / vLLM). Pick a template, edit, and it writes your application.yaml into the app config directory before starting the bundled runtime. See Desktop App.
DockerPass -e KEY=value for each env var, -p 8282:8282 to publish the port, and -e SPRING_PROFILES_INCLUDE=mcp-stdio to switch transport. Mount a volume to persist data. See Alternative Runtimes → Docker.
Source build./mvnw spring-boot:run or java -jar target/*.jar; supply env vars, -Dkey=value, --key=value, or an external application.yaml.

Profiles { #profiles }

The default active profile is ollama (spring.profiles.default: ollama). Profiles ship in the jar:

ProfileEffect
ollama (default)Local Ollama chat + embedding models.
openaiOpenAI chat + embedding (needs OPENAI_API_KEY).
mcp-stdioSwitches the built-in MCP server transport from Streamable HTTP to STDIO (see below).
mlx (auto)MLX-optimized Ollama model defaults for Apple Silicon. Auto-activated, not set by hand - see the note below.

Activate with SPRING_PROFILES_ACTIVE=openai, layer with SPRING_PROFILES_INCLUDE=mcp-stdio, or use --spring.profiles.active= / -Dspring.profiles.active=.

On an Apple Silicon Mac the mlx profile is layered onto ollama automatically (default chat model qwen3.5:4b-mlx plus a model menu of -mlx builds alongside the standard GGUF builds, which stay in the menu for image chat - MLX builds ship without vision tensors). An EnvironmentPostProcessor gates it on spring.ai.playground.ollama.mlx-auto-select (default true) and the OS/arch check, so it never activates on Intel, Windows, Linux, or Docker. Pass --spring.ai.playground.ollama.mlx-auto-select=false to opt out. The desktop launcher disables this and resolves MLX builds itself - see Alternative Runtimes → Apple Silicon and MLX models.

Server & web { #server }

PropertyEnv / -DDefaultNotes
server.portSERVER_PORT8282HTTP port for the web UI and the built-in MCP endpoint at /mcp.
server.shutdownrelaxed-binding envgracefulGraceful shutdown.
spring.lifecycle.timeout-per-shutdown-phaserelaxed-binding env30sDrain time per phase.
vaadin.pushmoderelaxed-binding envautomaticVaadin server push.
spring.servlet.multipart.max-file-size / max-request-sizerelaxed-binding env20MB / 20MBUpload limits (Vector Database ingest).
management.endpoints.web.exposure.includerelaxed-binding envhealth,info,metrics,prometheusActuator endpoints exposed at /actuator/*.

AI providers & models { #ai }

Provider selection (which Spring AI model backs each capability):

PropertyDefaultNotes
spring.ai.model.chatollamaSet to openai by the openai Spring profile.
spring.ai.model.embeddingollamaUsed by the Vector Database.
spring.ai.model.image / moderation / audio.speech / audio.transcriptionnoneOpt-in capabilities.

Ollama profile (ollama, default):

PropertyDefaultNotes
spring.ai.ollama.base-urlhttp://localhost:11434Point at a remote/host Ollama via SPRING_AI_OLLAMA_BASE_URL (common in Docker).
spring.ai.ollama.init.pull-model-strategywhen_missingAuto-pull models on startup.
spring.ai.ollama.chat.options.modelqwen3.5:4bDefault chat model.
spring.ai.ollama.embedding.options.modelqwen3-embedding:0.6bDefault embedding model.
spring.ai.ollama.chat.keep-alive / spring.ai.ollama.embedding.keep-alive30mHow long Ollama keeps each model loaded in memory after a call, as a Go duration (0 unloads it at once, -1 keeps it forever). The default trades VRAM for no reload stall when you come back to a chat. Sent per request, so it wins over the Ollama server's own OLLAMA_KEEP_ALIVE default; that server env var is still the right knob when you want one duration for every client of a self-managed Ollama. Override a single conversation from the chat settings drawer's provider-options JSON with {"keep_alive": "5m"}.
spring.ai.playground.chat.modelsqwen3.5:2b/4b/9b, qwen3.6:27b/35b, gemma4:e2b/e4b/12b/31b, gpt-oss:20b, deepseek-r1:8bThe model menu shown in the chat UI.
spring.ai.playground.ollama.mlx-auto-selecttrueOn Apple Silicon, auto-activate the mlx profile (MLX model defaults). Set false to keep the generic model names.

OpenAI profile (openai):

PropertyEnvDefaultNotes
spring.ai.openai.api-keyOPENAI_API_KEY(required)Set as an env var; never commit it.
spring.ai.openai.chat.options.model-gpt-5.4-miniDefault chat model.
spring.ai.openai.embedding.options.model-text-embedding-3-smallDefault embedding model.
spring.ai.playground.chat.models-gpt-5.4, gpt-5.4-mini, gpt-5.4-nano, gpt-5.2Chat model menu.

Tool / MCP-server API keys (GitHub PAT, BRAVE_API_KEY, MS_TENANT_ID, Google keys, ...) are documented per surface on the Default Tools and Default MCP Servers pages - they are supplied the same way (env var or the launcher's env editor).

Telemetry & analytics { #telemetry }

KnobHow to setDefaultNotes
Google Tag Manager in the UISPRING_AI_PLAYGROUND_TELEMETRY_ENABLED=false (env) or -Dspring.ai.playground.telemetry.enabled=falseenabled (production builds only)When false, the app omits the GTM tag and disables its own GA. Development mode (mvnw spring-boot:run, IDE runs) never sends telemetry regardless of this switch. Set this for QA / offline / privacy.

What is collected { #telemetry-events }

Telemetry is anonymous and content-free: no prompts, no responses, no file names, no URLs, no API keys, no server addresses. Names of things you create yourself (tools, presets, custom MCP servers) are masked to authored / custom / external - only names that ship in the built-in catalogs are ever sent as-is. Every sender goes through one of two classes, so the complete list is auditable in code: UsageEventTracker for the events below, and GoogleAnalyticsNavigationListener for page_view, which overrides the browser's default page location and title with the bare route path so that neither the conversation id in the URL nor the conversation title can reach the wire.

EventParametersFired when
page_viewroute pathNavigating between views
chat_message_sentprovider, model, reasoning, dynamic_tools, tool_count, image_count, rag_enabledSending a chat message
model_selectedprovider, modelApplying chat settings
model_downloadedmodel, viaAn Ollama model download completes
tool_calledtool_name (catalog names only), source, risk_level, hitl, catalog_id (catalog ids only)A tool call finishes in chat
preset_applied / preset_blockedpreset_id (catalog ids only), dynamic_tools, tool_countApplying a preset / the key gate blocks Apply
mcp_server_addedtransport, catalog_id (catalog ids only), oauthSaving and connecting an MCP server
mcp_tools_exposedmode, builtin_count, composed_count, max_riskApplying the Expose Tools drawer
rag_document_indexeddoc_type, chunk_countEmbedding a document into the vector store
tool_authoredaction, sandbox_overrides, hitlSaving a tool in Tool Studio
action_card_renderedcard_typeA visualization action card renders in chat
voice_input_usedmodeStarting voice input
usage_snapshot (at most once per day, per app run)aggregate counters only: recent call/token/error/latency totals, conversation count, installed model count, VRAM, RAM, top quantization, HITL and risk-signal totalsFirst page load of the day
mcp_server_active (at most once per day and app run, per configured server)transport, catalog_id (catalog ids only), oauth, connectedFirst page load of the day
model_usage (at most once per day and app run, per model used in the last 24h)model, provider, calls, tokens, errors, p95_msFirst page load of the day
js_error (via Google Tag Manager)error_message, error_url, error_lineAn uncaught JavaScript error occurs in the UI

Session-level user properties: app_version, app_surface (web/desktop), platform, chat_provider, embedding_provider, embedding_model, tool_search_index.

MCP built-in server & exposure { #mcp }

The playground publishes its own MCP server at /mcp (Streamable HTTP). These control its identity and what it exposes - see MCP Server Proxy for the UI equivalent.

PropertyEnvDefaultNotes
spring.ai.playground.built-in-mcp-server.namerelaxed-binding envspring-ai-playground-built-in-mcpAdvertised server name.
spring.ai.playground.built-in-mcp-server.descriptionrelaxed-binding env(see yaml)Advertised description.
spring.ai.playground.built-in-mcp-server.exposure-moderelaxed-binding envbothbuiltin-only · composed-only · both - whether /mcp serves your Tool Studio tools, the composed external tools, or both.
spring.ai.playground.mcp-server.composed-tools-max-riskrelaxed-binding envL5Caps which composed tools are published (L1-L5).
spring.ai.playground.mcp-server.composed-toolsrelaxed-binding env[]Declarative list of composed (proxied) external tools - see Configure exposure via YAML.
spring.ai.mcp.server.protocolrelaxed-binding envSTREAMABLESSE · STREAMABLE · STATELESS.
spring.ai.playground.chat.tool-result-max-charsrelaxed-binding env12000Caps the characters of any single tool result before it returns to the model in the Agentic Chat loop - built-in, authored, or external. Oversized results are truncated (with a marker) so one verbose tool call cannot blow up the context window. 0 disables the cap.
spring.ai.playground.chat.memory-max-messagesrelaxed-binding env10How many recent messages are sent to the model each turn - the conversation memory window. Overridable per chat from the settings drawer's Recent messages field. See Context Engineering → Conversation memory.
spring.ai.playground.chat.history-max-messagesrelaxed-binding env2000Safety cap on the full local conversation store that the screen and on-disk history read from; messages beyond this are dropped. The memory window above is what the model actually sees.
spring.ai.playground.chat.default-presetrelaxed-binding envself-equipping-agentThe prompt preset a brand-new chat opens with - its system prompt plus, for a dynamic preset, dynamic tool discovery. It does not touch the built-in MCP exposure. Empty or an unknown id falls back to a plain chat.
spring.ai.playground.chat.tool-search.enabledrelaxed-binding envtrueMaster switch for dynamic tool discovery - the toolSearchTool advisor, the boot-time tool index, and the chat checkbox. false removes the feature entirely.
spring.ai.playground.chat.tool-search.default-onrelaxed-binding envfalseWhether new chats start in dynamic-discovery mode when no preset decides it. Note the shipped chat.default-preset (self-equipping-agent, below) already opens new chats in dynamic mode; this flag matters once you change or clear that preset.
spring.ai.playground.chat.tool-search.min-toolsrelaxed-binding env10Minimum searchable tools before the chat's Dynamic tool discovery checkbox enables - discovery only pays off with a real catalog to search.
spring.ai.playground.chat.tool-search.max-resultsrelaxed-binding env3Tool names returned per toolSearchTool search.
spring.ai.playground.chat.tool-search.index-typerelaxed-binding envHYBRIDHYBRID (exact tool-name match, then vector search) or VECTOR (vector only).
spring.ai.playground.chat.tool-search.vector-storerelaxed-binding envDEDICATEDDEDICATED (a private, persisted tool index) or SHARED (reuse the RAG vector store). See Context Engineering → Tools.
spring.ai.mcp.server.request-timeoutrelaxed-binding env150Seconds.

Agent loop { #agent-loop }

spring.ai.playground.chat.agent-loop.* bounds the Agentic Chat tool-calling loop and its interactive dialogs. See Agent Loop for how these are enforced. Defaults suit a broad agent; raise the round caps only if a legitimate multi-step task needs more tool rounds.

PropertyEnvDefaultNotes
spring.ai.playground.chat.agent-loop.soft-max-roundsrelaxed-binding env16Once a turn exceeds this many tool rounds, further tool calls are answered with a "wrap up now" message instead of running, nudging the model to reply with what it has.
spring.ai.playground.chat.agent-loop.hard-max-roundsrelaxed-binding env18Hard stop: the loop is ended with a final message if the model keeps calling tools past this. Raised to soft-max-rounds if set lower.
spring.ai.playground.chat.agent-loop.max-identical-callsrelaxed-binding env3How many times a tool may be called with identical arguments in one turn before repeats are short-circuited (a legitimate re-read is fine; a stuck loop is not).
spring.ai.playground.chat.agent-loop.interactions-per-roundrelaxed-binding env1How many interactive dialogs (approval, file upload, image pick) may open per round. Keep at 1 so a round's blocking waits cannot stack past the stream timeout.
spring.ai.playground.chat.agent-loop.approval-timeout-secondsrelaxed-binding env120How long a human-in-the-loop approval dialog waits before failing safe to decline. Clamped below the 300s stream timeout.
spring.ai.playground.chat.agent-loop.dialog-timeout-secondsrelaxed-binding env180How long a file-upload or image dialog waits before returning "not provided". Clamped below the 300s stream timeout.

Observability { #observability }

spring.ai.playground.observability.* (architecture):

PropertyEnvDefaultNotes
...observability.ring-buffer-capacityrelaxed-binding env2000Trace events held in memory.
...observability.persistrelaxed-binding envtrueWrite traces to disk (one JSON file per trace).
...observability.retain-daysrelaxed-binding env30Days of persisted traces to keep.
...observability.max-spans-per-tracerelaxed-binding env200Span cap per trace.
...observability.capture-prompt-contentrelaxed-binding envtrueCapture prompt/response text in spans.
...observability.max-prompt-content-bytesrelaxed-binding env4096Truncation limit per captured message.
...observability.max-captured-messages-per-spanrelaxed-binding env16Message cap per span.
...observability.active-trace-ttl-secondsrelaxed-binding env300Idle-trace finalize timeout.
management.tracing.sampling.probabilityrelaxed-binding env1.0Trace sampling (0.0-1.0).
OTLP export endpointMANAGEMENT_OTLP_TRACING_ENDPOINT(unset)Opt-in: set to a collector URL to export spans.

Spring AI's own prompt/completion logging is off by default and toggled with spring.ai.chat.observations.log-prompt / log-completion, spring.ai.chat.client.observations.log-prompt / log-completion, spring.ai.tools.observations.include-content, spring.ai.vectorstore.observations.log-query-response (all false).

Tool Studio & JS sandbox { #tool-studio }

spring.ai.playground.tool-studio.* (sandbox architecture):

PropertyEnvDefaultNotes
...tool-studio.timeout-secondsrelaxed-binding env30Per-tool JS execution timeout.
...tool-studio.fs.base-pathrelaxed-binding env${user.home}/spring-ai-playground/workspaceRoot the filesystem tools are confined to.
...tool-studio.js-sandbox.allow-network-iorelaxed-binding envfalseRaw Java network access in tool JS (the built-in fetch is preferred).
...tool-studio.js-sandbox.allow-file-iorelaxed-binding envfalseRaw Java file access (use safety.fs).
...tool-studio.js-sandbox.allow-native-access / allow-create-threadrelaxed-binding envfalseNative / thread capabilities.
...tool-studio.js-sandbox.max-statementsrelaxed-binding env500000Statement budget before the tool is killed.
...tool-studio.js-sandbox.deny-classes / allow-classesrelaxed-binding env(see yaml)Class allow/deny lists. Deny always wins; lowering these weakens the sandbox.

!!! warning "The sandbox defaults are security-critical" allow-* flags are false and the deny-classes list blocks System, Runtime, ProcessBuilder, reflection, ClassLoader, and more. Don't widen these globally - grant capability per tool instead (which raises that tool's Risk Level).

Data directories & logs { #data }

PathControlled byDefault
App homespring.ai.playground.user-home${user.home}/spring-ai-playground
Logs(derived)<app-home>/logs (rolling file; the mcp-stdio profile detaches the console appender)
Tool specs(derived)<app-home>/tool/save
Vector store(derived)<app-home>/vectorstore/save
Filesystem-tool workspaceSPRING_AI_PLAYGROUND_TOOL_STUDIO_FS_BASE_PATH<app-home>/workspace

The desktop app stores this tree under the OS app-data location (macOS ~/Library/Application Support/spring-ai-playground, Windows %APPDATA%/spring-ai-playground, Linux ~/.config/spring-ai-playground).

The mcp-stdio profile { #mcp-stdio-profile }

For embedding the playground as a stdio MCP server inside another MCP client (Claude Desktop, an IDE), activate mcp-stdio:

  • The MCP JSON-RPC channel becomes process stdout/stdin, so the boot banner and startup-info line are silenced and the console log appender is detached (rolling file logging continues under <app-home>/logs).
  • The Spring web stack and Vaadin UI stay up on 8282 - publish it (-p 8282:8282) to reach the Inspector in a browser alongside the stdio channel.
  • Actuator HTTP endpoints are disabled in this profile.
# Docker, stdio transport (web UI still available on 8282)
docker run -i --rm -p 8282:8282 -e SPRING_PROFILES_INCLUDE=mcp-stdio ghcr.io/spring-ai-community/spring-ai-playground

See Alternative Runtimes for the full Docker / java -jar walkthrough.