Skip to main content

Class 1: Why Spring AI + MCP?

Duration: ~30 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.

Version Baseline (as of 2026-08)
  • MCP spec: the code here targets 2025-11-25, which is the revision Spring AI 2.0.0 and the Java MCP SDK 2.0.0 implement. 2026-07-28 is the current stable revision; where it changes something this course teaches, the lesson says so.
  • Spring AI: 2.0.0 GA (generally available, the stable release).
  • Spring Boot: 4.1.0 GA, which means Spring Framework 7, Jakarta EE 11, Jackson 3, and a Java 17+ baseline.
  • Java: 21.
  • Node.js: 22.12 or later, needed from Class 9 onwards for one MCP server distributed on npm, and from this class for the optional React frontend. Node 20 is end of life, and Vite 7, which the frontend uses, accepts ^20.19.0 || >=22.12.0.

Versions here move quickly: check Maven Central and docs.spring.io before starting your own project.


What We'll Cover

  • Which half of the MCP picture the Java SDK course covered, and which half it did not
  • What Spring AI is and the specific problems it solves
  • How Spring AI and MCP fit together
  • The application we build across the remaining sixteen classes, and how to get it running

You will not write any code in this class, only clone an application and start it, which takes a few minutes.


The Half We Have Not Built Yet

The Java SDK course built MCP servers. A server takes some capability we have, describes it in the terms the protocol uses, and waits for someone to call it. That someone was always an application we did not write.

The other side of the protocol is the application doing the asking: our own code, which sends a question to a model, lets the model decide which tools it needs, and then uses the answers it gets back. An order-processing assistant, a document summariser, or a service answering support questions from internal data all work this way.

The difference is which box we write:

In the Java SDK course, the model's reasoning happened inside a client someone else wrote. Here it happens inside our own process, and we choose the model and the servers it can reach.

The MCP Java SDK does not help us here. It implements the protocol, client-side included, but it does not include any code for calling a model. This course uses Spring AI for that.


What Spring AI Provides

Spring AI is the Spring project for working with AI models from Java. It sits between application code and the provider, in the position JdbcTemplate occupies between application code and a database driver.

ChatClient

ChatClient is the entry point, and the same code works against whichever provider is configured:

@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();
}
}

Changing provider means changing one dependency and one property. The service does not change.

The tool-use loop, handled

When a model decides to call a tool, it does not send back an answer. It sends back a request: run this tool with these arguments. Something then has to run the tool, add the result to the conversation, and call the model again, until the model produces text.

ChatClient does all of that in one call:

The middle four messages repeat as long as the model keeps asking for tools. One request and its reply is a round trip, and our code sees only the first message and the last. Class 7 covers what happens when a tool fails.

MCP support, on both sides

On the client side, the MCP client starter reads connection details from configuration, connects to each server, performs the handshake that 2025-11-25 defines, and calls tools/list. Every discovered tool is exposed through a ToolCallbackProvider bean, which ChatClient accepts directly:

spring:
ai:
mcp:
client:
stdio:
connections:
knowledge-base:
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- ./support-kb

Those lines start the filesystem MCP server as a child process and offer its tools to the model. The server refuses any path outside the directories it is given, so this one can only reach ./support-kb. In Class 9 we use exactly this configuration to give the support agent the team's own notes.

On the server side, a set of annotations turns Spring beans into MCP capabilities. A method annotated with @McpTool becomes a tool, and Spring AI builds the JSON Schema from the method signature:

@McpTool(name = "get_order", description = "Look up an order by its ID.")
public Order getOrder(@McpToolParam(description = "Order ID, ORD-XXXXX") String orderId) {
return orderService.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException(
"No order with ID '%s'. Check the ID and try again.".formatted(orderId)));
}

Matching annotations cover the other things a server can expose, and we use all of them: @McpResource, @McpPrompt and @McpComplete.


How the Pieces Fit

Follow one request down from the user, and the results back up:

Our code talks only to ChatClient. The provider's message format and MCP's JSON-RPC are both below that line.

MCP is a protocol, so a tool written once can be used by any client that speaks it. Spring AI packages that protocol's Java implementation, the MCP Java SDK, for Spring Boot on both sides, and adds the model integration around it. MCP without Spring AI leaves us writing the model integration ourselves, and Spring AI without MCP means defining the tools again in every application that needs them.


What We Build: a Support Desk

The rest of the course builds one order support system, class by class. By the end it does this:

You: Where is order ORD-10001, and can the customer still return it?

Support Desk: It shipped on 12 May with DHL, tracking DHL-88213, and is due on 14 May. Ana Ruiz is a Gold-tier customer, so the returns window is 60 days from delivery rather than the usual 30, which takes her to mid-July.

The support desk answers from three sources, and only one is a server we did not write:

The order, the shipment and the returns policy above all come from order-service, the server we build in Classes 2 to 5. In Class 9 we connect the agent to the third, an npm package that serves the support team's own notes. Most MCP systems are built this way, so the course covers both sides.

The course also covers the rest of what the protocol offers. order-service exposes orders as resources, so they can be attached to a conversation without the model having to look for them. It also offers a prompt that drafts a refund email, reports progress while rechecking delivery estimates for every order still in transit, and asks a person to confirm before cancelling an order.

Where each part is built:

ClassWhat we add
2The first @McpTool and the MCP endpoint
3The rest of the tools, their schemas, and their behavioural hints
4Resources: order://{orderId}, policy://returns and policy://shipping
5A prompt that drafts a refund email, with order-ID completion
6A second application, support-agent, connecting as an MCP client
7A model, so support-agent answers questions in English
8Reading resources and prompts from the client side
9An MCP server we did not write, serving the support team's notes
10A second knowledge-base server, and colliding tool names
11Progress and log messages during a long job
12Asking a person to confirm before cancelling an order
13Roots, change notifications and sampling
14Tests without calling a model
15The same service, reactive and stateless
16Health, metrics, retries and timeouts
17The same service in Claude Desktop

Classes 2 to 6 do not need an API key or a model: order-service is a working MCP server long before a model is involved, and we check it with curl at every step.


Getting the Starter Running

The application already exists as a plain Spring Boot service without AI. Clone it and start it:

You run this
git clone https://github.com/the-mcp-guy/spring-ai-mcp-course.git
cd spring-ai-mcp-course
mvn -pl order-service spring-boot:run

The repository is a multi-module Maven build: the pom.xml at the top has <packaging>pom</packaging>, listing the modules and holding the versions they share, without any code of its own. The application lives in order-service. -pl is short for --projects, and it names the module the goal runs for instead of every project in the build.

Leaving it out stops at that top-level project, which does not contain an application class to start:

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:4.1.0:run (default-cli) on project support-desk: Unable to find a suitable main class, please add a 'mainClass' property

That is why every run command in this course names a module. In Class 6 we add a second module, support-agent, and from there the two run simultaneously in separate terminals.

The starter is an order service with JPA entities, repositories, a business service, a REST controller, and seed data for 200 orders across 42 customers. None of it is taught in this course: it is here so the classes can spend their time on MCP.

Check that it started:

You run this
curl http://localhost:8080/api/orders/ORD-10001
{
"orderId": "ORD-10001",
"status": "SHIPPED",
"customer": {
"customerId": "CUST-42",
"name": "Ana Ruiz",
"email": "[email protected]",
"tier": "GOLD"
},
"items": [
{ "productId": "PROD-001", "productName": "Wireless Keyboard", "quantity": 1, "unitPrice": 89.99 },
{ "productId": "PROD-002", "productName": "USB-C Hub", "quantity": 2, "unitPrice": 45.00 }
],
"totalAmount": 179.99,
"createdAt": "2026-05-10T09:00:00Z",
"lastUpdated": "2026-05-12T14:30:00Z",
"shipment": {
"carrier": "DHL",
"trackingNumber": "DHL-88213",
"shippedOn": "2026-05-12",
"estimatedDelivery": "2026-05-14"
}
}

In Class 2, we expose the same order through MCP. The REST endpoint stays where it is, leaving two entry points: one for the applications that call it today, one for a model.

The browser view

The repository also has a small React frontend. Start it if you want it:

You run this
cd frontend
npm install
npm run dev

It runs on http://localhost:5173 and shows the orders, with a panel for asking questions, which starts working in Class 7.

We don't teach the frontend, and we don't change it. It ships with the course project to make the later classes easier to follow. The refund-email button on each order starts working in Class 8, the progress bar in Class 11, and the confirmation dialog before a cancellation in Class 12. Every class also works from the command line, so the browser is optional.


A Note on Providers

Class 7 is the first class that needs a model. The examples use Anthropic's Claude because Anthropic designed MCP and Claude's tool use is well documented. Spring AI's abstraction means the same code runs against OpenAI, Google, Mistral, or a model on your own machine.

In Class 7 we configure Anthropic, provide the OpenAI equivalent, and set up Ollama with qwen3:8b so the course can be followed without an account or API key. We then run the application against that local model and show what came back, including where a model of that size struggles.

Where anything is provider-specific, this course says so.


Next: Class 2: From a REST Application to an MCP Server. Add the server starter, write the first tool, and call it over curl.


Further Reading

Sources