SafetyCard Request a review

Spring AI

Spring application framework for AI engineering with tool calling and MCP

github.com/spring-projects/spring-ai · 2026-10-03 · 46d05c2

Defense-in-depth score

3.0 / 10

Spring AI runs every tool call the model requests, automatically and without any approval step, inside your Spring application with all of its credentials and data access. Nothing in the framework separates untrusted content (web pages, documents, MCP results) from instructions, so a prompt injection can drive any registered tool. It does ship sensible defaults elsewhere: no tools unless you attach them, a per-turn cap of 150 tool calls, and content-free telemetry. Treat approvals, authorization, and isolation as work you must add yourself.

Safest setup

Spring AI attaches no tools unless you pass them, caps each turn at 150 tool calls, and keeps prompt and tool content out of telemetry by default. Every requested tool call otherwise runs automatically with the application's full credentials, with no approval step and no record unless you add one, so the safest setup narrows those credentials, takes over the tool loop for approval and builds containment around the application.

SetupHowWhat it gives you
A least-privileged service identityRun the application with database, cloud and service credentials scoped to what its tools need, read-only where the task allowsThe systems themselves refuse anything outside that grant, whatever a tool call asks for C1
Per-call approvalSet spring.ai.chat.client.tool-calling.enabled=false (or AdvisorParams.toolCallingAdvisorAutoRegister(false) per call) and run each requested call through ToolCallingManager after a person approves itA person sees each tool call before it runs, including calls to MCP tools C2
Only attached toolsKeep the default spring.ai.tools.resolution.fallback.enabled=false and attach only the tools each request needsThe model can call only the tools you passed for that request C3
Tool context kept localRegister a ToolContextToMcpMetaConverter bean that returns ToolContextToMcpMetaConverter.noOp()Data you pass to your own tools stays out of requests to MCP servers C7
Tool-call capsKeep spring.ai.tools.limits.max-calls-per-tool-default at 40 and spring.ai.tools.limits.max-total-tool-calls at 150, or lowerEvery turn stops after a fixed number of tool calls, a limit the model cannot change C10
Reviewed, pinned MCP serversConfigure only MCP servers you have reviewed, with an exact version in each launch commandThe server code and tools that start with the application are the ones you checked C7
Its own container or VMRun the application in a container or VM whose network reaches only the model provider and the hosts its tools needA narrower path for data to leave and an outer boundary around in-process tools and stdio MCP servers C5
A tool-call recordProvide an ObservationRegistry bean with a tracing exporter to durable storageA span for every tool call, MCP tools included, with name, call id and timing C9

Approving with confidence

  • Show the exact tool name and arguments, then read each prompt and approve calls one at a time.
  • Web pages, documents and MCP results can carry instructions, so a call that follows reading them is worth a closer look.

Handling data

  • Prompt, completion and tool content stay out of observations while spring.ai.chat.observations.log-prompt, spring.ai.chat.observations.log-completion and spring.ai.tools.observations.include-content are false, the default.
  • Chat-memory stores keep messages in plain text under the conversation id your application supplies; tie that id to the signed-in user.

Key gaps (2)

  1. Tools run in-process with the Spring application's full credentials and no authorization check, so a hijacked agent inherits everything the application can reach. C1 · Identity & least privilege
  2. Untrusted tool, MCP and RAG content flows into an ungated tool loop, so a prompt injection can exfiltrate data and take irreversible actions with no human involved (C5-WORSTCASE). C5 · Untrusted input blast radius

Criteria

C1 Identity & least privilege

0.00 / 1.00

Spring AI has no identity or authorization layer of its own. Every tool a developer registers runs as an ordinary Java method or function inside the Spring application, with whatever database connections, service credentials and network access that application holds, and nothing checks a tool call against a policy or against the user who asked. A ToolContext map lets developers pass a tenant or user id to their tools, but enforcing it is entirely up to the tool. The MCP server starter exposes every ToolCallback bean over HTTP with no authentication of its own unless the developer adds Spring Security. If the agent is hijacked, the attacker gets the full authority of the application.

C2 Approval gates

0.00 / 1.00

Spring AI ships no approval gate. The auto-registered tool-calling advisor runs every tool call the model requests, immediately and in a loop, until the model stops asking. The documentation tells developers who want approval to write their own advisor or drive the loop themselves, but the framework provides no primitive that pauses on a tool, shows the exact call, or records a decision. The OpenAI-hosted MCP tool even rejects the provider's own 'always require approval' setting because the round-trip is not implemented. There is no undo or checkpoint for actions already taken.

C3 Tool & action scoping

0.50 / 1.00

Spring AI converts a tool call's JSON arguments into the Java parameter types of the developer's method or function, which rejects malformed input but is type checking rather than an allowlist; schema rules such as enums or numeric ranges are not enforced. MCP tool arguments are only parsed into a map and forwarded unchanged. On the positive side, a ChatClient has no tools unless the developer passes them, and by default a call to a tool that was not attached to the request is rejected rather than resolved from the application context. The framework ships no path, URL, or query validation helpers, so a registered tool's reach is whatever the developer gave it.

C4 Code-execution isolation

N/A · full credit 1.00 / 1.00

Spring AI itself has no code-execution feature: no shell tool, no script engine, no evaluation of model output, and no process spawning in its own source. Tools are Java methods the developer writes. Two adjacent paths are scored elsewhere or run off-host: local MCP servers configured by the operator are launched as subprocesses by the MCP Java SDK (scored under third-party extensions), and the OpenAI code-interpreter and Anthropic code-execution tools, when a developer enables them, run model-written code in the model provider's own containers rather than on the application host.

C5 Untrusted input blast radius

0.00 / 1.00

Tool results, MCP results, and retrieved documents are fed back to the model as ordinary tool or user messages, and nothing in the framework tracks which content is untrusted or restricts what the agent may do after reading it. MCP tool descriptions from any connected server are passed to the model verbatim, and the RAG advisor pastes retrieved documents into the user's own message. Because the documentation shows web-searching MCP tools combined with the application's own tools in one ChatClient, with no approval gate, a successful prompt injection can both leak data and trigger state-changing tools without a human.

C6 Memory, context & configuration integrity

0.23 / 1.00

Spring AI loads no instruction files or .env files from a workspace, and memory is only used when the developer adds a chat-memory advisor. When they do, every user message, tool result, and model reply is saved without validation and replayed into later requests of the same conversation, where it can steer tool calls; the default store keeps the last 20 messages in memory, and JDBC, Redis, Mongo, Cassandra, and Neo4j stores make it durable. Conversations are separated by a conversation id that the application must supply and that every repository query filters on, but the id is not tied to an authenticated user. The vector-store memory advisor escapes stored text but still inserts it into the system prompt.

C7 Third-party extensions

0.23 / 1.00

Spring AI's runtime extension path is MCP. Once the MCP client starter is on the classpath, it connects at application start to every server listed in configuration, launches stdio servers with the configured command, and exposes every tool each server advertises. Nothing is enabled until the operator configures a server, but there is no version pinning, hash check, or re-approval: the official example launches an unpinned package with 'npx -y', and when a server announces changed tools they are picked up automatically. The default adapter also forwards the application's whole ToolContext to each MCP server.

C8 Secrets & sensitive-data protection

0.38 / 1.00

Spring AI sends no telemetry of its own, and its observation and logging features exclude prompt, completion, and tool-argument content by default, logging a loud warning when a developer turns content capture on. API-key objects mask their value when printed. But nothing redacts secrets or personal data from what is sent to the model provider or to MCP servers: tool exception messages are returned to the model verbatim, the default MCP adapter forwards the whole ToolContext map, and saved chat memory is stored in plain text. Provider keys are long-lived and readable by any tool code in the same process.

C9 Audit & traceability

0.35 / 1.00

Every tool call goes through one manager that wraps it in a Micrometer observation carrying the tool name, call id, and timing, plus arguments and results if content capture is enabled; this covers MCP tools too. But the default observation registry is a no-op, so unless the application provides Micrometer tracing and an exporter, nothing is recorded beyond a debug log line with the tool name. Even when enabled, records do not say which user requested an action, and there are no approvals to record.

C10 Limits & kill switch

0.30 / 1.00

The tool-calling loop runs until the model stops requesting tools, but since this version a default cap stops a turn after 40 calls to any one tool or 150 tool calls in total, enforced in code; operators can raise these or set them to unlimited, and the model cannot. There is no wall-clock limit, no token or cost budget, and no timeout on local tool calls (MCP requests time out after 20 seconds). There is no cancel API for blocking calls, and in streaming mode tool execution runs on a worker thread that is not interrupted when the subscriber cancels. Tools executed by the model provider, such as hosted MCP or web search, are outside these counters.