Model Context Protocol (MCP)
In lesson 5 you gave the ShopKart agent tools by wiring @Tool methods directly into the ChatClient. That works beautifully - inside your one Spring Boot app. But what if you wanted Claude Desktop to search the ShopKart catalog? Or an internal IDE assistant to check stock? Or your app to use a shipping-rate tool that another team owns? You would be writing a new custom integration every single time.
The Model Context Protocol (MCP) solves exactly this. It is an open standard - introduced by Anthropic in late 2024 and since adopted across the industry - that defines a common way for AI applications to connect to tools and data. Expose your tools once as an MCP server, and any MCP-compatible client can discover and call them. No bespoke glue.
The N-times-M problem
Without a standard, every AI client needs a custom connector to every tool source. Ten clients and ten tool sources is a hundred integrations. MCP collapses that: each client speaks MCP, each tool source speaks MCP, and they interoperate.
How MCP works, briefly
MCP uses a client-server architecture over JSON-RPC 2.0. A server exposes three kinds of capability:
- Tools - functions the client's model can call (this is what we focus on - it maps directly to your
@Toolmethods). - Resources - read-only data the model can pull in, addressed by URI.
- Prompts - reusable prompt templates the server offers.
Transports vary by deployment. STDIO is used when the client launches the server as a local subprocess (great for desktop tools). SSE and Streamable HTTP are used when the server runs as a web service that clients connect to over the network - which is what we want for a ShopKart server other apps reach over HTTP.
Exposing ShopKart tools as an MCP server
Here is the elegant part: the @Tool methods you wrote in lesson 5 need no changes. Spring AI's MCP server starter can publish any ToolCallbackProvider as MCP tools. Add the server starter (WebMVC transport, so it serves over HTTP):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
Configure the server identity in application.properties:
spring.ai.mcp.server.name=shopkart-catalog
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
Then declare a ToolCallbackProvider bean that wraps your existing ShopKartTools. MethodToolCallbackProvider turns each @Tool method into a callback the MCP server publishes.
package com.shopkart.mcp;
import com.shopkart.agent.ShopKartTools;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.*;
@Configuration
public class McpServerConfig {
@Bean
public ToolCallbackProvider shopKartToolProvider(ShopKartTools shopKartTools) {
return MethodToolCallbackProvider.builder()
.toolObjects(shopKartTools)
.build();
}
}
That is it. On startup, Spring AI publishes searchProducts, checkStock, and placeOrder as MCP tools over HTTP. Any MCP client - Claude Desktop, another team's app, an IDE - can now connect, discover those three tools, and call them. You wrote the tools once for your own agent in lesson 5, and now they are shareable infrastructure.
> Newer Spring AI versions also offer a more declarative @McpTool annotation for methods that are MCP-only. For 1.0 GA, wrapping your existing @Tool methods in a ToolCallbackProvider is the standard approach and has the nice property that the same tool object serves both your in-process agent and the MCP server.
Consuming an external MCP server
The other half of MCP is being a client. Say the shipping team runs an MCP server exposing a getDeliveryEstimate(pincode) tool. Your ShopKart agent can use it without you writing any shipping code. Add the MCP client starter:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
Point it at the remote server in configuration. On the Spring AI 1.0 stack the client uses the SSE transport (matching the WebMVC SSE server you configured above); the connection key names the server and gives its base URL (the client appends the default /sse endpoint).
spring.ai.mcp.client.sse.connections.shipping.url=http://shipping-svc:8090
The client starter connects on startup, discovers the remote tools, and auto-configures them as a ToolCallbackProvider. Attach that provider to your ChatClient and the remote tools join your local ones in the same agent loop:
package com.shopkart.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.context.annotation.*;
@Configuration
public class ChatConfig {
// This app has TWO ToolCallbackProvider beans - our own MCP-server provider
// (shopKartToolProvider, a MethodToolCallbackProvider) and the MCP client's
// auto-configured one. Injecting the generic ToolCallbackProvider type would
// be ambiguous (NoUniqueBeanDefinitionException), so we inject the client's
// concrete type, SyncMcpToolCallbackProvider, which resolves it cleanly.
@Bean
public ChatClient chatClient(ChatModel chatModel,
com.shopkart.agent.ShopKartTools localTools,
SyncMcpToolCallbackProvider mcpTools) { // remote tools from MCP client
return ChatClient.builder(chatModel)
.defaultSystem(SYSTEM_PROMPT)
.defaultTools(localTools) // your @Tool methods
.defaultToolCallbacks(mcpTools) // tools from the MCP shipping server
.build();
}
private static final String SYSTEM_PROMPT = "You are the ShopKart shopping assistant...";
}
Now when a customer asks "when will these earbuds reach 560001?", the agent can call the remote getDeliveryEstimate tool - a tool your codebase does not contain - exactly as if it were local. From the model's point of view, local and MCP tools are indistinguishable.
The full ShopKart MCP wiring
Putting both sides together, ShopKart is simultaneously an MCP server (publishing its catalog tools) and an MCP client (consuming the shipping team's tools).
Reach for MCP when a tool needs to be reused across more than one AI application, or when it is owned by another team. For a single app talking to its own database, plain in-process @Tool methods (lesson 5) are simpler - do not add a network hop you do not need. And treat an MCP server like any other public surface: authenticate callers, and be extremely careful about exposing write tools like placeOrder to clients you do not fully control. MCP standardizes access; it does not grant it.
Key takeaways
- MCP is an open, JSON-RPC-based protocol that standardizes how AI apps connect to tools and data - expose once, consume from any MCP client.
- MCP servers expose tools, resources, and prompts; transports include STDIO (local subprocess) and SSE / Streamable HTTP (networked).
- Expose existing
@Toolmethods as an MCP server by addingspring-ai-starter-mcp-server-webmvcand declaring aToolCallbackProviderviaMethodToolCallbackProvider- no changes to the tools. - Consume remote tools by adding
spring-ai-starter-mcp-client, configuring the server URL, and attaching the auto-configuredToolCallbackProviderto the ChatClient. - Use MCP for reuse across apps and teams; keep single-app tools in-process. Secure any write tools you publish.
Next up: Streaming Responses to React - stream the agent's answer token by token for a live typing effect.