Two options, each with a cost
A Spring Boot service that runs its own agent loop on enterprise APIs has two ways to give the model tools today.
- Hand-write
@Toolmethods. The tool surface lives in Java. It ships with the service, which is convenient, but it is reviewed as code, and whether a tool is safe to call lives in a developer’s head or a Javadoc comment. - Run an MCP server next to the service and consume it with Spring AI’s MCP client. The surface is now declarative and governed, but the service pays for a second process, credential plumbing to its own tool layer, one more failure point and two traces to stitch together.
Spring AI Ikanos adds a third option: declare the tools in reviewable YAML, and run them in the same JVM as the agent.
The value proposition
- One artifact, two deployments. The tools come from the
exposes: type: mcpblock of an Ikanos capability. The same file can be served as a remote MCP server or embedded in-process. Moving between the two is a deployment decision, not a rewrite. - No extra moving parts. Each tool becomes a Spring AI
ToolCallback. No MCP server is started, no port is bound and no second process runs. Calls go straight from the tool loop to the engine, then out to the upstream API. - Same view for the model. Tool definitions are built with Spring AI’s own
McpToolUtils, and results are serialized likeSyncMcpToolCallbackdoes. A parity test checks that a tool’s definition, output and annotations are identical in-JVM and from an Ikanos MCP server. - Risk hints that are actually enforced. MCP tool annotations such as
readOnlyHintanddestructiveHintare usually just metadata. Here they drive an approval gate that fails closed. - Plain Spring AI. The project uses
ToolCallback,ToolCallbackProviderand Boot auto-configuration unchanged, and it works withToolSearchToolCallingAdvisor.
When should you pick in-JVM over remote MCP? Look at lifecycle coupling:
| Agent and tools share… | Use |
|---|---|
| Team, release cadence, deployment unit | In-JVM, with Spring AI Ikanos |
| Nothing: tools are shared or owned by another team | Remote MCP, an Ikanos MCP server |
The project does not compete with Spring AI’s MCP support. It gives the same declarative artifact a second deployment option.
One caveat on the remote side, stated plainly: Ikanos MCP servers speak MCP protocol 2026-07-28, and the MCP Java SDK in current Spring AI releases does not yet. Support has landed in SDK 2.2.0-M1, so the gap closes when Spring AI adopts it. Until then, Spring AI’s own MCP client cannot connect to an Ikanos server, and the in-JVM bridge is the way to use Ikanos tools from Spring AI.
How it works
You declare a tool once, in a capability:
exposes:
- type: mcp
namespace: fleet
tools:
list-ships:
description: List ships by operational status
hints:
readOnly: true
inputParameters:
status:
type: string
required: true
call: registry.list-ships
with:
status: fleet.status
You add the starter, point it at your capabilities, and attach the tools to a ChatClient:
spring:
ai:
ikanos:
capabilities:
- classpath:capabilities/*.yml
@Bean
ChatClient chatClient(ChatClient.Builder builder, IkanosToolCallbackProvider ikanosTools) {
return builder.defaultTools(ikanosTools).build();
}
At startup, the starter loads each capability, starts only its outbound HTTP clients, and publishes one ToolCallbackProvider. When the model calls list-ships, Spring AI’s tool loop invokes the callback, which hands the arguments to the Ikanos engine in the same heap. The engine maps the inputs, calls the upstream API, shapes the response, and returns structured JSON to the model.
Startup fails fast on mistakes you would otherwise find in production: a location that matches nothing, two tools with the same name, or a tool that may be destructive with no one to approve it.
An approval gate built on MCP annotations
Every tool is gated unless its capability declares readOnly: true or destructive: false. That follows the MCP defaults, so a write tool with no hints is gated rather than waved through. Before a gated tool runs, a ToolApprovalHandler decides:
@Bean
ToolApprovalHandler toolApprovalHandler() {
return request -> hasChangeTicket(request)
? ToolApprovalDecision.approve()
: ToolApprovalDecision.deny("needs a change ticket");
}
A denial goes back to the model as the tool result, so it can explain or choose another path. If no handler exists, the application does not start. The gate is a ToolCallback decorator rather than an advisor, so it composes with whatever advisor drives the loop.
The approval module has no Ikanos dependency. What stops it from gating remote MCP tools today is that Spring AI’s MCP callbacks keep the annotations a server declares to themselves. We have proposed a small, vendor-neutral addition that exposes them: spring-projects/spring-ai#7239. With it, the same annotations could drive our gate, or the tool call confirmation coming in Spring AI 2.1, for every MCP tool, not only Ikanos ones.
Where it stands, and how you can help
The project is a working proof of concept, public at naftiko/spring-ai-ikanos under Apache 2.0. It has a core bridge, the approval gate, Boot auto-configuration, a starter and an example application. Its 57 tests pass against ikanos-engine 1.0.0-beta8 from Maven Central and Spring AI 2.0.2. It requires Java 21 and Spring Boot 4.
Our goal is to propose it as a project of the Spring AI Community, alongside other vendor-backed projects. Approval there is unanimous, so the case rests on value to Spring AI users, not to us. In the interest of transparency: Naftiko maintains Ikanos and sells commercial offerings around it.
Here is how you can help:
- Try it on a real Spring AI agent, and tell us where it breaks or where the YAML falls short of a hand-written
@Tool. - Open an issue or discussion on the repository with your use case. Concrete users make a stronger proposal than any design document.
- Star the repository if you would like to see it in the Spring AI Community.
- Join as a maintainer. The community asks for committed developers, and we are looking for a second one.
Declarative tools should not force you to run a second process. With Spring AI Ikanos, they don’t have to.
The takeaway
- Spring AI Ikanos runs the tools of an Ikanos capability as Spring AI
ToolCallbacks, in the same JVM as the agent, with no MCP server and no second process. - The same YAML can be embedded or served as a remote MCP server. Choose by lifecycle coupling, not by rewriting.
- MCP
ToolAnnotationsbecome an approval gate that fails closed: unannotated tools are gated, and the app does not start without a handler. - The project is a public, Apache 2.0 proof of concept, headed for the Spring AI Community. Your use cases and feedback decide how strong that proposal is.
References
- 💻 naftiko/spring-ai-ikanos — source, README and the
examples/fleetapplication - 💻 naftiko/ikanos — the Ikanos engine, published on Maven Central as
io.ikanos:ikanos-engine - 💬 spring-projects/spring-ai#7239 — our proposal to expose MCP tool annotations from Spring AI’s MCP callbacks
- 🌱 Spring AI Community — Get Involved and the Project Creation Guide
- 📖 Spring AI Reference — Tool Calling and MCP
- 📖 Model Context Protocol — Tool annotations