MCP & Spring AI
Adding an MCP Server to a Spring Boot Platform: Lessons from Production
To add a Model Context Protocol (MCP) server to a Spring Boot platform, use Spring AI's MCP Server Boot Starter: add the dependency, annotate service methods with @Tool, pick a network transport, and any MCP-capable client — Claude, an agent framework, your own chat service — can discover and call your platform's capabilities. That part takes an afternoon. What the tutorials don't cover is what happens after: how you design a tool surface an agent can actually navigate, what guardrails write-tools need before you let a language model invoke them, and why tool descriptions become a line item in your token budget. I run a 30-tool MCP server inside Saathratri, my 30+ microservice hospitality platform — this article is the basics plus the production lessons.
The basics: Spring AI's MCP server starter
Spring AI ships MCP server support as a Boot starter. Expose a capability by annotating a method:
@Service
public class WeatherTools {
@Tool(description = "Get the current weather for a city.")
public WeatherReport getCurrentWeather(String city) {
return weatherService.current(city);
}
}
The starter generates JSON schemas from your method signatures, handles the MCP handshake and tool discovery, and validates incoming arguments against the schema before your method runs. Tools, resources (read-only context), and prompts are all supported; tools are where platforms get the most value, because they let an agent act.
Which MCP transport should a platform use?
MCP supports STDIO (the client launches your process locally) and HTTP-based transports (SSE, and more recently Streamable HTTP). For a desktop developer tool, STDIO is fine. For a platform, run the MCP server as a network service over HTTP — in Saathratri it's a reactive Spring Boot service exposing an SSE-based MCP endpoint, registered in Eureka with health checks like every other microservice. That means the same deployment pipeline, the same monitoring, the same service discovery — an MCP server is not a special snowflake; it's a microservice whose consumers happen to be language models. Front it with your gateway and require authentication (Saathratri's sits behind a gateway API key): an MCP endpoint is an API surface, and an unauthenticated one is an incident waiting for a caller.
Designing a tool surface an agent can navigate
A production MCP server is a curated API for a non-human consumer, and agents fail in different ways than developers do. Three rules from running one:
- Give the agent a way to orient itself. In a multi-tenant platform, nearly every tool needs a tenant identifier. Saathratri's flows typically start with a
listAccessibleHotelstool that resolves which organizations the caller may act on; every domain tool then takes thatorganizationIdexplicitly. Without an orientation tool, agents guess — and a guessed tenant ID is either an error loop or, far worse, a cross-tenant action. - Pair every write with its read. If there's a
sendSmsMessagetool, there must be a conversation-lookup tool; if there's a work-order creation tool, there must be search and status tools. Agents reason in loops of observe → act → verify; a write-only surface forces them to act blind. - Group tools by workflow, not by table. Saathratri's 30 tools cluster into capabilities — knowledge lookup (backed by pgvector similarity search), weather, places, messaging, reservations and calendar reads, and a maintenance-inspection workflow that walks from room lookup through checklist to work-order creation. The tool list is the agent's mental model of your platform; make it read like one.
Guardrails: what write tools need before an LLM can call them
The moment a model can trigger side effects — send an SMS, dispatch an email, print a document, open a work order — you are one hallucinated argument away from a real-world mess. Every AI-invoked write tool in Saathratri gets, at minimum:
- Input validation independent of the schema. JSON-schema validation checks shape, not sense. Validate that tenant IDs are well-formed UUIDs the caller may use, and reject blanks explicitly — models are excellent at producing plausible-looking nonsense that passes a type check.
- Rate limits scoped to the blast radius. Messaging tools are rate-limited per organization on a rolling window (20/min by default); globally-scarce resources like printing are limited globally. Agents retry with enthusiasm; without limits, a confused loop becomes a hundred SMS messages.
- Authentication in front, authorization inside. The gateway key establishes who is calling; tool code still checks what that caller may touch. Defense in depth matters more, not less, when the immediate caller is a model relaying a human's request.
The cost nobody mentions: tool descriptions are prompt tokens
Every tool's name, description, and parameter schema is serialized into the model's context on every turn. With 30 tools, verbose descriptions become a measurable per-conversation cost — and the temptation is real, because descriptions double as the agent's instructions. In Saathratri I ended up auditing the longest descriptions and trimming them while keeping the critical sequencing guidance (“call listAccessibleHotels first to resolve the organization”). Treat tool descriptions like an API's public docs with a per-request tax: every sentence must earn its tokens. This also caps how many tools one server should carry — past a few dozen, both the token bill and the agent's tool-selection accuracy degrade, and it's time to split servers or expose a coarser-grained workflow tool.
Know what to keep out of the server
Not every automation belongs in your platform's MCP server. Saathratri also runs scheduled operational jobs in a different runtime; bridging those into the reactive Java MCP server would be fragile and architecturally wrong. Instead, they live behind their own MCP servers, and clients compose them: the Java server owns platform data and actions, other servers own their own domains. MCP's design assumes clients connect to multiple servers — use that instead of building a mega-server.
The other side: being an MCP client too
A platform rarely stops at serving tools. Saathratri also runs an MCP client service, so its own chat services and agents consume the same tool surface external assistants do. That symmetry is worth designing for early: if your internal chatbot uses the same MCP tools as external agents, you have one capability surface to test, secure, and document instead of two.
FAQ
What's the minimum to add an MCP server to Spring Boot?
Add Spring AI's MCP Server Boot Starter (WebMVC or WebFlux flavor), annotate methods with @Tool(description = ...), and configure the HTTP transport. Spring AI generates the schemas and handles the protocol; an MCP client can then list and call your tools. Secure the endpoint before anything else.
Should I use STDIO or HTTP transport?
STDIO is for locally-launched, single-user tools. A platform should expose MCP over HTTP (SSE or Streamable HTTP), deployed and discovered like any other microservice — gateway in front, health checks, service registry.
How many tools should one MCP server expose?
Saathratri's sits at 30 and that's near the practical ceiling: every tool's description and schema costs context tokens on every turn, and tool-selection accuracy drops as the list grows. Prefer several focused servers (platform data, browser automation, batch jobs) composed by the client over one server that does everything.
How do you stop an AI agent from misusing write tools?
Layered guardrails: gateway authentication, per-tool input validation beyond the JSON schema, tenant-scoped rate limits on messaging-style tools, global limits on shared resources, and read tools paired with every write so the agent can verify before and after acting.