Skip to main content

Module 1: Why Spring AI + MCP?

Duration: ~25 minutes | Level: Intermediate | Prerequisites: Completed MCP Fundamentals, and comfortable with Spring Boot. Building MCP Servers in Java is recommended for depth, but not required: Spring AI's starters cover the plumbing this course needs.


What You'll Understand After This Module

  • Why raw LLM SDKs plus the MCP Java SDK leave a gap in the stack
  • What Spring AI provides and why it fills that gap cleanly
  • How Spring AI and MCP relate to each other conceptually
  • The architecture of a Spring AI + MCP application
  • What you'll build across the remaining modules

The Gap in the Stack

The Java SDK course taught you to build MCP servers. You can write Java code, wrap it in an McpServer, and any MCP-compatible client (Claude Desktop, Cursor, GitHub Copilot) can discover and invoke your tools.

That's a complete solution for the server side of the MCP story.

But what if your application isn't just a passive tool-provider? What if your Spring Boot application needs to call an AI model itself? What if you want to build an order-processing agent, a code-review assistant, or a document-summarisation service, where the AI reasoning happens inside your application, not in a third-party client?

This is where the stack has a gap.

The MCP Java SDK is designed to make tools accessible to AI clients. It doesn't help you make AI calls from your application. For that, you need to integrate with an AI provider directly.

What Integrating with an AI Provider Means Without Spring AI

Suppose you decide to call Anthropic's API directly from your Spring Boot service:

// Without Spring AI: what the "just use the SDK" path looks like
@Service
public class DocumentService {

private final AnthropicClient anthropic; // Anthropic Java SDK

public String summarise(String document) {
// 1. Build the message
Message userMessage = Message.builder()
.role(Role.USER)
.content(List.of(ContentBlock.ofText("Summarise this:\n" + document)))
.build();

// 2. Define tools (if you want the model to use any)
Tool searchTool = Tool.builder()
.name("search_related")
.description("Search for related documents")
.inputSchema(buildJsonSchema()) // write JSON schema by hand
.build();

// 3. Make the call
Message response = anthropic.messages().create(MessageCreateParams.builder()
.model("claude-sonnet-4-6")
.maxTokens(1024)
.messages(List.of(userMessage))
.tools(List.of(searchTool))
.build());

// 4. Parse the response: is it a tool_use block or text?
for (ContentBlock block : response.content()) {
if (block.type().equals("tool_use")) {
// Execute the tool, feed result back, call the model AGAIN
// ...loop until we get a text response...
}
}

return response.content().stream()
.filter(b -> b.type().equals("text"))
.map(ContentBlock::text)
.findFirst()
.orElseThrow();
}
}

That's a simplification. The real tool-use loop is more complex. And this code is hardwired to Anthropic. Switch to OpenAI and you rewrite the whole thing: different SDK, different message format, different tool schema format, different response structure.

Every AI provider has its own SDK with its own conventions. If you integrate directly, you're betting on a vendor and buying technical debt.

The Specific Problems

1. Vendor lock-in. Anthropic, OpenAI, Google, Mistral, Cohere all have different APIs. Choosing one means rewriting if you switch.

2. The tool-use loop. When a model decides to call a tool, you don't get a final answer. You get a tool request. You run the tool, send the result back, and the model continues. This loop can go several rounds. Implementing it correctly and handling errors at each step is non-trivial boilerplate that has nothing to do with your business logic.

3. No Spring integration. No autoconfiguration, no application.properties binding, no health indicators, no Micrometer metrics, no Spring Security hooks. You're wiring everything manually.

4. Conversation history. Maintaining a multi-turn conversation means tracking message history, managing token budgets, and deciding when to summarise old messages. None of the provider SDKs handle this for you.


What Spring AI Provides

Spring AI is the Spring ecosystem's official abstraction layer for working with AI models. It was designed specifically to solve these problems.

Provider-Agnostic ChatClient

Spring AI's ChatClient is to AI models what Spring's JdbcTemplate is to databases: a clean abstraction that hides provider-specific details while giving you direct access to the underlying capabilities when you need them.

// Works with ANY configured AI provider: Anthropic, OpenAI, Gemini, Ollama
@Service
public class DocumentService {

private final ChatClient chatClient;

DocumentService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}

public String summarise(String document) {
return chatClient.prompt()
.user("Summarise the following document:\n" + document)
.call()
.content();
}
}

Swap providers by changing one dependency and one property file entry. Your service code doesn't change.

Automatic Tool-Use Loop

Spring AI implements the tool-use loop for you. When you tell ChatClient which tools are available, it automatically:

  1. Sends the tools to the model with your message
  2. Detects when the model requests a tool call
  3. Executes the tool
  4. Sends the result back to the model
  5. Repeats until the model produces a final text response

You get a String back. The loop that may have involved three round-trips to the LLM and two tool executions is completely invisible to your service code.

First-Class MCP Integration

Spring AI has native, first-class support for MCP. This is where things get compelling.

Instead of writing custom tool definitions and registration code, Spring AI reads your application.properties, auto-configures connections to any MCP server, and exposes every discovered tool through a single auto-configured SyncMcpToolCallbackProvider bean. You hand that one bean to ChatClient and every tool from every connected server becomes available to the model.

spring:
ai:
mcp:
client:
toolcallback:
enabled: true
stdio:
connections:
filesystem:
command: npx
args: -y,@modelcontextprotocol/server-filesystem,/tmp

That configuration handles the plumbing. Spring AI starts the filesystem MCP server as a child process, performs the MCP handshake, and discovers its tools via the MCP protocol. One line in your service code, builder.defaultTools(mcpTools), registers them with ChatClient; Module 4 shows exactly this. From then on, when the model needs to list files or read a file, ChatClient handles the MCP tool call automatically.

Production Infrastructure

Spring AI is built on Spring Boot. That means you get everything Spring Boot brings: autoconfiguration, externalized config, health indicators, Micrometer metrics, Spring Security integration. These aren't afterthoughts. They're the foundation.


The Architecture of a Spring AI + MCP Application

Here's how the pieces fit together:

The critical insight is that Spring AI sits between your application code and everything else. Your service calls ChatClient. Spring AI handles the model protocol, the tool-use loop, and the MCP connections. You never touch JSON-RPC or provider-specific message formats.


Why This Combination Matters

Spring AI without MCP still requires you to define and register tools explicitly for each application. MCP without Spring AI leaves you writing manual LLM integration code.

Together, they form a complete stack:

  • MCP provides a standard protocol so tools are defined once and work everywhere
  • Spring AI provides the client-side plumbing to connect to those tools and use them in AI-powered applications
  • Spring Boot provides the production infrastructure around all of it

Any MCP server (whether you built it in the Java SDK course, whether it's a community package from npm, or whether it's an existing service that another team maintains) is immediately usable in your Spring AI application with a single configuration entry.

This is the architectural vision MCP was designed for: a standard protocol that decouples tool providers from AI consumers. Spring AI is the consumer-side implementation of that vision for the Java ecosystem.


What You'll Build in This Course

Across the remaining modules, you'll build four progressively sophisticated applications:

Module 3 (First Connection): A Spring Boot application that connects to an MCP server, lists its available tools, and verifies the connection.

Module 4 (ChatClient with Tools): A command-line AI agent that uses MCP tools transparently. You ask questions in plain English, the agent uses tools to answer them.

Module 5 (Multi-Server Agents): An agent connected to two MCP servers simultaneously, with tools specialised for different tasks.

Module 6 (Spring as an MCP Server): A Spring Boot application that exposes its own business logic as MCP tools using the @McpTool annotation, turning any Spring service into a tool provider.

Module 7 (Testing): How to test AI-integrated applications without calling the LLM API on every test run, including deterministic integration tests and isolated unit tests for tool methods.

Module 8 (Production): Externalized configuration, health indicators, Micrometer metrics, retry logic, and everything else you need to ship a Spring AI + MCP application to production.

All code in this course is in the companion repository at projects/mcp-spring-ai-course/, a multi-module Maven project where each module corresponds to a course module.


A Note on AI Provider Choice

The examples in this course use Anthropic Claude as the AI provider. There are two reasons for this:

  1. Anthropic created MCP. Using Claude with MCP is using the tools exactly as their creator intended.
  2. Claude's tool-use capabilities are mature and well-documented.

That said, Spring AI's abstraction means the code works identically with OpenAI, Google, Mistral, or any other supported provider. Module 2 shows how to configure either Anthropic or OpenAI, and wherever a provider-specific detail appears, we'll note the OpenAI equivalent.


Next: Module 2: Environment Setup. Build the Maven project, configure dependencies, and verify your first AI call.