245 lines
8.8 KiB
Markdown
245 lines
8.8 KiB
Markdown
# Spring AI MCP Weather Server Sample with WebMVC Starter and Sampling
|
|
|
|
This sample project demonstrates how to create an MCP server using the Spring AI MCP Server Boot Starter with WebMVC transport. It implements a weather service that exposes tools for retrieving weather information using the Open-Meteo API and showcases MCP Sampling capabilities.
|
|
|
|
For more information, see the [MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) reference documentation.
|
|
|
|
## Overview
|
|
|
|
The sample showcases:
|
|
- Integration with `spring-ai-mcp-server-webmvc-spring-boot-starter`
|
|
- Support for both SSE (Server-Sent Events) and STDIO transports
|
|
- Automatic tool registration using Spring AI's `@Tool` annotation
|
|
- MCP Sampling implementation that demonstrates LLM provider routing
|
|
- Weather tool that retrieves temperature data and generates creative responses using multiple LLMs
|
|
|
|
## MCP Sampling Implementation
|
|
|
|
This project demonstrates the MCP Sampling capability, which allows an MCP server to delegate certain requests to LLM providers. The implementation includes:
|
|
|
|
1. **Server-side Sampling**: The `WeatherService` class implements a `callMcpSampling` method that:
|
|
- Extracts the `McpSyncServerExchange` from the tool context
|
|
- Creates two separate message requests with different model preferences:
|
|
- One targeting OpenAI models with `ModelPreferences.builder().addHint("openai").build()`
|
|
- One targeting Anthropic models with `ModelPreferences.builder().addHint("anthropic").build()`
|
|
- Sends both requests to generate creative poems about the weather data
|
|
- Combines the responses into a single result
|
|
|
|
2. **Client-side Sampling**: The companion client project (`mcp-sampling-client`) implements:
|
|
- A `McpSyncClientCustomizer` that handles sampling requests
|
|
- Logic to route requests to the appropriate LLM based on model hints
|
|
- Integration with both OpenAI and Anthropic models
|
|
|
|
This approach demonstrates how MCP can be used to leverage multiple LLM providers within a single application, allowing for creative content generation and model comparison.
|
|
|
|
## Dependencies
|
|
|
|
The project requires the Spring AI MCP Server WebMVC Boot Starter:
|
|
|
|
```xml
|
|
<dependency>
|
|
<groupId>org.springframework.ai</groupId>
|
|
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
|
|
</dependency>
|
|
```
|
|
|
|
This starter provides:
|
|
- HTTP-based transport using Spring MVC (`WebMvcSseServerTransport`)
|
|
- Auto-configured SSE endpoints
|
|
- Optional STDIO transport
|
|
- Included `spring-boot-starter-web` and `mcp-spring-webmvc` dependencies
|
|
|
|
## Building the Project
|
|
|
|
Build the project using Maven:
|
|
```bash
|
|
./mvnw clean install -DskipTests
|
|
```
|
|
|
|
## Running the Server
|
|
|
|
The server supports two transport modes:
|
|
|
|
### WebMVC SSE Mode (Default)
|
|
```bash
|
|
java -jar target/mcp-sampling-weather-server-0.0.1-SNAPSHOT.jar
|
|
```
|
|
|
|
### STDIO Mode
|
|
To enable STDIO transport, set the appropriate properties:
|
|
```bash
|
|
java -Dspring.ai.mcp.server.stdio=true -Dspring.main.web-application-type=none -jar target/mcp-sampling-weather-server-0.0.1-SNAPSHOT.jar
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Configure the server through `application.properties`:
|
|
|
|
```properties
|
|
# Server identification
|
|
spring.ai.mcp.server.name=my-weather-server
|
|
spring.ai.mcp.server.version=0.0.1
|
|
|
|
# Server type (SYNC/ASYNC)
|
|
spring.ai.mcp.server.type=SYNC
|
|
|
|
# Transport configuration
|
|
spring.ai.mcp.server.stdio=false
|
|
spring.ai.mcp.server.sse-message-endpoint=/mcp/message
|
|
|
|
# Change notifications
|
|
spring.ai.mcp.server.resource-change-notification=true
|
|
spring.ai.mcp.server.tool-change-notification=true
|
|
spring.ai.mcp.server.prompt-change-notification=true
|
|
|
|
# Logging (required for STDIO transport)
|
|
spring.main.banner-mode=off
|
|
logging.file.name=./target/starter-webmvc-server.log
|
|
```
|
|
|
|
## Available Tools
|
|
|
|
### Weather Temperature Tool
|
|
- Name: `getTemperature`
|
|
- Description: Get the temperature (in celsius) for a specific location
|
|
- Parameters:
|
|
- `latitude`: double - The location latitude
|
|
- `longitude`: double - The location longitude
|
|
- `toolContext`: ToolContext - Automatically provided by Spring AI
|
|
|
|
This tool not only retrieves the current temperature from the Open-Meteo API but also uses MCP Sampling to generate creative poems about the weather from both OpenAI and Anthropic models.
|
|
|
|
## Server Implementation
|
|
|
|
The server uses Spring Boot and Spring AI's tool annotations for automatic tool registration:
|
|
|
|
```java
|
|
@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();
|
|
}
|
|
}
|
|
```
|
|
|
|
The `WeatherService` implements the weather tool using the `@Tool` annotation and includes MCP Sampling functionality:
|
|
|
|
```java
|
|
@Service
|
|
public class WeatherService {
|
|
@Tool(description = "Get the temperature (in celsius) for a specific location")
|
|
public String getTemperature(double latitude, double longitude, ToolContext toolContext) {
|
|
// Retrieve weather data from Open-Meteo API
|
|
WeatherResponse weatherResponse = restClient
|
|
.get()
|
|
.uri("https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}¤t=temperature_2m",
|
|
latitude, longitude)
|
|
.retrieve()
|
|
.body(WeatherResponse.class);
|
|
|
|
// Use MCP Sampling to generate creative responses
|
|
String responseWithPoems = callMcpSampling(toolContext, weatherResponse);
|
|
|
|
return responseWithPoems;
|
|
}
|
|
|
|
public String callMcpSampling(ToolContext toolContext, WeatherResponse weatherResponse) {
|
|
// Implementation that calls both OpenAI and Anthropic models
|
|
// to generate poems about the weather
|
|
}
|
|
}
|
|
```
|
|
|
|
## MCP Clients
|
|
|
|
You can connect to the weather server using either STDIO or SSE transport:
|
|
|
|
### Manual Clients
|
|
|
|
#### WebMVC SSE Client
|
|
|
|
For servers using SSE transport:
|
|
|
|
```java
|
|
var transport = new HttpClientSseClientTransport("http://localhost:8080");
|
|
var client = McpClient.sync(transport).build();
|
|
```
|
|
|
|
#### STDIO Client
|
|
|
|
For servers using STDIO transport:
|
|
|
|
```java
|
|
var stdioParams = ServerParameters.builder("java")
|
|
.args("-Dspring.ai.mcp.server.stdio=true",
|
|
"-Dspring.main.web-application-type=none",
|
|
"-Dspring.main.banner-mode=off",
|
|
"-Dlogging.pattern.console=",
|
|
"-jar",
|
|
"target/mcp-sampling-weather-server-0.0.1-SNAPSHOT.jar")
|
|
.build();
|
|
|
|
var transport = new StdioClientTransport(stdioParams);
|
|
var client = McpClient.sync(transport).build();
|
|
```
|
|
|
|
The sample project includes example client implementations:
|
|
- [SampleClient.java](src/test/java/org/springframework/ai/mcp/sample/client/SampleClient.java): Manual MCP client implementation
|
|
- [ClientStdio.java](src/test/java/org/springframework/ai/mcp/sample/client/ClientStdio.java): STDIO transport connection
|
|
|
|
### Sampling Client
|
|
|
|
The companion project `mcp-sampling-client` demonstrates how to implement a client that handles MCP Sampling requests:
|
|
|
|
```java
|
|
@Bean
|
|
McpSyncClientCustomizer samplingCustomizer(Map<String, ChatClient> chatClients) {
|
|
return (name, spec) -> {
|
|
spec.sampling(llmRequest -> {
|
|
var userPrompt = ((McpSchema.TextContent) llmRequest.messages().get(0).content()).text();
|
|
String modelHint = llmRequest.modelPreferences().hints().get(0).name();
|
|
|
|
// Find the appropriate chat client based on the model hint
|
|
ChatClient hintedChatClient = chatClients.entrySet().stream()
|
|
.filter(e -> e.getKey().contains(modelHint)).findFirst()
|
|
.orElseThrow().getValue();
|
|
|
|
// Generate response using the selected model
|
|
String response = hintedChatClient.prompt()
|
|
.system(llmRequest.systemPrompt())
|
|
.user(userPrompt)
|
|
.call()
|
|
.content();
|
|
|
|
return CreateMessageResult.builder().content(new McpSchema.TextContent(response)).build();
|
|
});
|
|
};
|
|
}
|
|
```
|
|
|
|
To run the sampling client:
|
|
|
|
1. Start the MCP server
|
|
2. Set the required environment variables:
|
|
```bash
|
|
export OPENAI_API_KEY=your-openai-key
|
|
export ANTHROPIC_API_KEY=your-anthropic-key
|
|
```
|
|
3. Run the client:
|
|
```bash
|
|
java -jar target/mcp-sampling-client-0.0.1-SNAPSHOT.jar
|
|
```
|
|
|
|
## Additional Resources
|
|
|
|
* [Spring AI Documentation](https://docs.spring.io/spring-ai/reference/)
|
|
* [MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html)
|
|
* [MCP Client Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-client-docs.html)
|
|
* [Model Context Protocol Specification](https://modelcontextprotocol.github.io/specification/)
|
|
* [Spring Boot Auto-configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration)
|