How to Use Spring AI MCP to Let Large Language Models Call Your Java APIs
This article walks through the complete process of building a Spring AI MCP server and client in Java, covering protocol layers, Maven setup, configuration, tool definition with @Tool, bean registration, client integration, common pitfalls, and the language‑agnostic benefits of the MCP protocol.
What is MCP?
MCP (Model Context Protocol) standardizes interaction between AI models and external tools or resources. It is organized into three layers:
Client/Server layer – MCP Client manages connections, protocol negotiation, capability discovery, and tool calls; MCP Server exposes tools/resources and manages client connections.
Session layer – Handles communication mode and maintains connection state.
Transport layer – Performs message transport and JSON‑RPC serialization; supports STDIO, HTTP/SSE, and Streamable‑HTTP.
Spring AI MCP Support
Server : Dependency spring-ai-starter-mcp-server-webflux. WebFlux implementation, supports SSE, Streamable‑HTTP, stateless.
Client : Dependency spring-ai-starter-mcp-client-webflux. WebFlux implementation, supports SSE and Streamable‑HTTP.
Building the MCP Server
Project structure
java-ai-mcp-server/
├── pom.xml
└── src/main/
├── java/com/why/
│ ├── McpServerApplication.java
│ └── mcp/server/tool/WeatherService.java
└── resources/
└── application.propertiesMaven dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.5</version>
</parent>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>2.0.0-M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>application.properties
server.port=9000
spring.ai.mcp.server.annotation-scanner.enabled=true
spring.ai.mcp.server.name=java-ai-mcp-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=sync
spring.ai.mcp.server.protocol=sseEnabling annotation-scanner.enabled=true allows automatic discovery of methods annotated with @Tool.
Define an MCP tool
@Service
public class WeatherService {
private static final String BASE_URL = "https://restapi.amap.com/v3/weather/weatherInfo";
private final RestClient restClient = RestClient.builder().baseUrl(BASE_URL).build();
@Tool(description = "Get weather for a city")
public String getWeather(String cityCode) {
return restClient.get()
.uri(uriBuilder -> uriBuilder
.queryParam("key", "YOUR_GAODE_API_KEY")
.queryParam("city", cityCode)
.build())
.retrieve()
.body(String.class);
}
}Register the tool
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}After launching, the server is reachable at http://localhost:9000/mcp-server.
Building the MCP Client
Project structure
java-ai-mcp-client/
├── pom.xml
└── src/main/
├── java/com/why/
│ ├── McpClientApplication.java
│ └── controller/ChatClientController.java
└── resources/
└── application.propertiesMaven dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-minimax</artifactId>
</dependency>application.properties
server.port=9001
spring.ai.minimax.base-url=api.minimax.chat
spring.ai.minimax.api-key=YOUR_API_KEY
spring.ai.minimax.chat.options.model=minimax-m2.7
spring.ai.mcp.client.enabled=true
spring.ai.mcp.client.name=java-ai-mcp-client
spring.ai.mcp.client.version=1.0.0
spring.ai.mcp.client.type=sync
spring.ai.mcp.client.sse.connections.java-ai-mcp-server.url=http://localhost:9000/mcp-serverThe client property sse.connections.xxx.url must match the server’s spring.ai.mcp.server.name value.
Use MCP tools in a controller
@Slf4j
@RestController
@RequestMapping("minimax")
public class ChatClientController {
@Resource
private MiniMaxChatModel miniMaxChatModel;
@Resource
private SyncMcpToolCallbackProvider mcpToolCallbackProvider;
@GetMapping(value = "/stream/event", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamEvent(@RequestParam("question") String question) {
ChatClient chatClient = ChatClient.builder(miniMaxChatModel).build();
var toolCallbacks = mcpToolCallbackProvider.getToolCallbacks();
return chatClient.prompt(question)
.toolCallbacks(toolCallbacks)
.stream()
.content();
}
}Common Pitfalls
Protocol mismatch : Server and client must use the same transport protocol (e.g., both SSE). Mismatched protocols cause 400 Bad Request or 404 Not Found errors.
Tool class not scanned : Methods annotated with @Tool must reside in a Spring bean (e.g., a class annotated with @Service) so that the annotation scanner can register them.
Callback creation : Do not instantiate SyncMcpToolCallback manually. Obtain registered callbacks via SyncMcpToolCallbackProvider.getToolCallbacks() for stability.
Summary
Add @Tool to any method you want to expose as an MCP tool.
Configure matching Server and Client properties (name, version, protocol, URL) so that the client can locate the server.
Inject the discovered tool callbacks into a ChatClient using SyncMcpToolCallbackProvider.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Coder Circle
Limited experience, continuously learning and summarizing knowledge, aiming to join a top tech company.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
