refactor: migrate to spring-ai-mcp-client-spring-boot-starter

- Replace spring-ai-mcp dependency with spring-ai-mcp-client-spring-boot-starter
- Update import statements from org.springframework.ai.mcp.* to io.modelcontextprotocol.client.*
- Replace McpFunctionCallback with SyncMcpToolCallbackProvider
- Update Spring AI version from 1.0.0-M5 to 1.0.0-SNAPSHOT in multiple projects
- Enable tool callback auto-configuration with spring.ai.mcp.client.toolcallback.enabled

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov
2025-03-24 17:41:02 +01:00
parent 04269db217
commit 6bb3243ad1
14 changed files with 78 additions and 109 deletions

View File

@@ -1,20 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.6</version>
<relativePath/> <!-- lookup parent from repository -->
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>ai-mcp-brave</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring AI - Model Context Protocol - Brave</name>
<description>Simple AI Application using MCP client to use Brave for Internet search</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>
</properties>
<dependencyManagement>
@@ -22,7 +25,7 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -40,13 +43,11 @@
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp</artifactId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
@@ -78,4 +79,4 @@
</repositories>
</project>
</project>

View File

@@ -1,14 +1,13 @@
package org.springframework.ai.mcp.samples.brave;
import io.modelcontextprotocol.client.McpClient;
import java.util.List;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.SyncMcpToolCallback;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -26,16 +25,12 @@ public class Application {
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder,
McpSyncClient mcpSyncClient, ConfigurableApplicationContext context) {
List<McpSyncClient> mcpSyncClients, ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(mcpSyncClient.listTools(null)
.tools()
.stream()
.map(tool -> new SyncMcpToolCallback(mcpSyncClient, tool))
.toArray(SyncMcpToolCallback[]::new))
.defaultTools(new SyncMcpToolCallbackProvider(mcpSyncClients))
.build();
String question = "Does Spring AI supports the Model Context Protocol? Please provide some references.";
@@ -45,20 +40,4 @@ public class Application {
context.close();
};
}
@Bean
public McpSyncClient mcpClient() {
// https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search
var stdioParams = ServerParameters.builder("npx")
.args("-y", "@modelcontextprotocol/server-brave-search")
.addEnvVar("BRAVE_API_KEY", System.getenv("BRAVE_API_KEY"))
.build();
var mcpClient = McpClient.sync(new StdioClientTransport(stdioParams)).build();
var init = mcpClient.initialize();
logger.info("MCP Initialized: {}", init);
return mcpClient;
}
}

View File

@@ -3,6 +3,8 @@ spring.main.web-application-type=none
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json
# Logging configuration
logging.level.root=INFO
logging.level.org.springframework.ai.mcp=WARN

View File

@@ -0,0 +1,13 @@
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-brave-search"
],
"env": {
}
}
}
}

View File

@@ -14,4 +14,6 @@ spring.ai.mcp.client.stdio.connections.brave-search.args=-y,@modelcontextprotoco
logging.level.io.modelcontextprotocol.client=WARN
logging.level.io.modelcontextprotocol.spec=WARN
ai.user.input=What tools are available?
ai.user.input=What tools are available?
spring.ai.mcp.client.toolcallback.enabled=true

View File

@@ -13,4 +13,7 @@ spring.ai.mcp.client.stdio.connections.brave-search.args=-y,@modelcontextprotoco
logging.level.io.modelcontextprotocol.client=WARN
logging.level.io.modelcontextprotocol.spec=WARN
ai.user.input=What tools are available?
ai.user.input=What tools are available?
# Enable the mcp client tool-callback auto-configuration
spring.ai.mcp.client.toolcallback.enabled=true

View File

@@ -22,7 +22,7 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M5</version>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -41,9 +41,8 @@
</dependency>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-ai-mcp</artifactId>
<version>0.6.0</version>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
</dependencies>

View File

@@ -2,14 +2,14 @@ package org.springframework.ai.mcp.samples.filesystem;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.client.McpClient;
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.ai.mcp.client.transport.ServerParameters;
import org.springframework.ai.mcp.client.transport.StdioClientTransport;
import org.springframework.ai.mcp.spring.McpFunctionCallback;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -25,22 +25,22 @@ public class Application {
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder,
List<McpFunctionCallback> functionCallbacks, ConfigurableApplicationContext context) {
McpSyncClient mcpClient, ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultFunctions(functionCallbacks.toArray(new McpFunctionCallback[0]))
.defaultTools(new SyncMcpToolCallbackProvider(mcpClient))
.build();
System.out.println("Running predefined questions with AI model responses:\n");
// Question 1
String question1 = "Can you explain the content of the spring-ai-mcp-overview.txt file?";
String question1 = "Can you explain the content of the target/spring-ai-mcp-overview.txt file?";
System.out.println("QUESTION: " + question1);
System.out.println("ASSISTANT: " + chatClient.prompt(question1).call().content());
// Question 2
String question2 = "Pleses summarize the content of the spring-ai-mcp-overview.txt file and store it a new summary.md as Markdown format?";
String question2 = "Pleses summarize the content of the target/spring-ai-mcp-overview.txt file and store it a new target/summary.md as Markdown format?";
System.out.println("\nQUESTION: " + question2);
System.out.println("ASSISTANT: " +
chatClient.prompt(question2).call().content());
@@ -50,17 +50,6 @@ public class Application {
};
}
@Bean
public List<McpFunctionCallback> functionCallbacks(McpSyncClient mcpClient) {
var callbacks = mcpClient.listTools(null)
.tools()
.stream()
.map(tool -> new McpFunctionCallback(mcpClient, tool))
.toList();
return callbacks;
}
@Bean(destroyMethod = "close")
public McpSyncClient mcpClient() {

View File

@@ -44,17 +44,16 @@ public class McpClientApplication {
@Bean
public CommandLineRunner predefinedQuestions(OpenAiChatModel openAiChatModel,
ConfigurableApplicationContext context, ObjectProvider<List<McpSyncClient>> mcpClientsProvider) {
ConfigurableApplicationContext context, List<McpSyncClient> mcpClients) {
return args -> {
var mcpToolProvider = new SyncMcpToolCallbackProvider(
mcpClientsProvider.stream().flatMap(List::stream).toList());
var mcpToolProvider = new SyncMcpToolCallbackProvider(mcpClients);
ChatClient chatClient = ChatClient.builder(openAiChatModel).defaultTools(mcpToolProvider).build();
String userQuestion = """
What is the wather in Amsterdam right now?
What is the weather in Amsterdam right now?
Please incorporate all createive responses from all LLM providers.
After the other providers add a poem that synthesizes the the poems from all the other providers.
""";
@@ -69,8 +68,8 @@ public class McpClientApplication {
@Bean
McpSyncClientCustomizer samplingCustomizer(Map<String, ChatClient> chatClients) {
return (name, spec) -> {
spec.sampling(llmRequest -> {
return (name, mcpClientSpec) -> {
mcpClientSpec.sampling(llmRequest -> {
var userPrompt = ((McpSchema.TextContent) llmRequest.messages().get(0).content()).text();
String modelHint = llmRequest.modelPreferences().hints().get(0).name();

View File

@@ -11,3 +11,6 @@ spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080
logging.level.io.modelcontextprotocol.client=WARN
logging.level.io.modelcontextprotocol.spec=WARN
# spring.ai.mcp.client.toolcallback.enabled=false

View File

@@ -22,7 +22,7 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M5</version>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -41,9 +41,8 @@
</dependency>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-ai-mcp</artifactId>
<version>0.6.0</version>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
</dependencies>

View File

@@ -5,14 +5,15 @@ import java.time.Duration;
import java.util.List;
import java.util.Scanner;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.mcp.client.McpClient;
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.ai.mcp.client.transport.ServerParameters;
import org.springframework.ai.mcp.client.transport.StdioClientTransport;
import org.springframework.ai.mcp.spring.McpFunctionCallback;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -28,12 +29,12 @@ public class Application {
@Bean
public CommandLineRunner interactiveChat(ChatClient.Builder chatClientBuilder,
List<McpFunctionCallback> functionCallbacks,
List<McpSyncClient> mcpClients,
ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultFunctions(functionCallbacks.toArray(new McpFunctionCallback[0]))
.defaultTools(new SyncMcpToolCallbackProvider(mcpClients))
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.build();
@@ -61,16 +62,6 @@ public class Application {
};
}
@Bean
public List<McpFunctionCallback> functionCallbacks(McpSyncClient mcpClient) {
var callbacks = mcpClient.listTools(null)
.tools()
.stream()
.map(tool -> new McpFunctionCallback(mcpClient, tool))
.toList();
return callbacks;
}
@Bean(destroyMethod = "close")
public McpSyncClient mcpClient() {

View File

@@ -22,7 +22,7 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M5</version>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -41,9 +41,8 @@
</dependency>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-ai-mcp</artifactId>
<version>0.6.0</version>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
</dependencies>

View File

@@ -4,12 +4,13 @@ import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.client.McpClient;
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.ai.mcp.client.transport.ServerParameters;
import org.springframework.ai.mcp.client.transport.StdioClientTransport;
import org.springframework.ai.mcp.spring.McpFunctionCallback;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -25,11 +26,11 @@ public class Application {
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder,
List<McpFunctionCallback> functionCallbacks, ConfigurableApplicationContext context) {
List<McpSyncClient> mcpClients, ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultFunctions(functionCallbacks.toArray(new McpFunctionCallback[0]))
.defaultTools(new SyncMcpToolCallbackProvider(mcpClients))
.build();
System.out.println("Running predefined questions with AI model responses:\n");
@@ -59,17 +60,6 @@ public class Application {
};
}
@Bean
public List<McpFunctionCallback> functionCallbacks(McpSyncClient mcpClient) {
var callbacks = mcpClient.listTools(null)
.tools()
.stream()
.map(tool -> new McpFunctionCallback(mcpClient, tool))
.toList();
return callbacks;
}
@Bean(destroyMethod = "close")
public McpSyncClient mcpClient() {