refactor(mcp): update to use Spring Boot autoconfiguration and rename tool callbacks

Update MCP examples to use Spring Boot autoconfiguration instead of manual configuration.
Replace deprecated defaultTools method with defaultToolCallbacks across all examples.
Update documentation to reflect these changes and provide more detailed configuration examples.

- Update API from defaultTools to defaultToolCallbacks in all MCP examples
- Update README files with more detailed configuration examples

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov
2025-05-02 14:28:17 +03:00
parent 2c9fa4d8fd
commit 234d3ff919
17 changed files with 95 additions and 217 deletions

View File

@@ -1,6 +1,6 @@
# Spring AI - Model Context Protocol (MCP) Brave Search Example
This example demonstrates how to create a Spring AI Model Context Protocol (MCP) client that communicates with the [Brave Search MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search). The application shows how to build an MCP client that enables natural language interactions with Brave Search, allowing you to perform internet searches through a conversational interface. Instead of using Spring Boot autoconfiguration, this example demonstrates how to manually configure the MCP client transport using an `@Bean` definition.
This example demonstrates how to create a Spring AI Model Context Protocol (MCP) client that communicates with the [Brave Search MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search). The application shows how to build an MCP client that enables natural language interactions with Brave Search, allowing you to perform internet searches through a conversational interface. This example uses Spring Boot autoconfiguration to set up the MCP client through configuration files.
When run, the application demonstrates the MCP client's capabilities by asking a specific question: "Does Spring AI supports the Model Context Protocol? Please provide some references." The MCP client uses Brave Search to find relevant information and returns a comprehensive answer. After providing the response, the application exits.
@@ -52,56 +52,46 @@ The application will execute a single query asking about Spring AI's support for
## How it Works
The application integrates Spring AI with the Brave Search MCP server through several components:
The application integrates Spring AI with the Brave Search MCP server through Spring Boot autoconfiguration:
### MCP Client Setup
### MCP Client Configuration
```java
@Bean
public McpSyncClient mcpClient() {
The MCP client is configured using configuration files:
// 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();
1. `application.properties`:
```properties
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json
```
var mcpClient = McpClient.sync(new StdioClientTransport(stdioParams)).build();
var init = mcpClient.initialize();
logger.info("MCP Initialized: {}", init);
return mcpClient;
2. `mcp-servers-config.json`:
```json
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-brave-search"
],
"env": {
}
}
}
}
```
The MCP client is configured to:
1. Use the Brave Search MCP server via npx
2. Pass the Brave API key from environment variables
3. Initialize a synchronous connection to the server
### Function Callbacks
The application automatically discovers and registers available Brave Search tools:
```java
List<McpFunctionCallback> functionCallbacks = mcpClient.listTools(null)
.tools()
.stream()
.map(tool -> new McpFunctionCallback(mcpClient, tool))
.toList();
```
These callbacks enable the ChatClient to:
- Access Brave Search tools during conversations
- Handle function calls requested by the AI model
- Execute search queries against the Brave Search API
This configuration:
1. Uses the Brave Search MCP server via npx
2. The Brave API key is passed from environment variables
3. Initializes a synchronous connection to the server
### Chat Integration
The ChatClient is configured with the Brave Search function callbacks:
The ChatClient is configured with the MCP tool callbacks in the Application class:
```java
var chatClient = chatClientBuilder
.defaultFunctions(functionCallbacks.toArray(new McpFunctionCallback[0]))
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClients))
.build();
```

View File

@@ -11,7 +11,6 @@ import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@@ -20,24 +19,22 @@ public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
SpringApplication.run(Application.class, args).close();
}
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder,
List<McpSyncClient> mcpSyncClients, ConfigurableApplicationContext context) {
List<McpSyncClient> mcpSyncClients) {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(new SyncMcpToolCallbackProvider(mcpSyncClients))
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClients))
.build();
String question = "Does Spring AI supports the Model Context Protocol? Please provide some references.";
logger.info("QUESTION: {}\n", question);
logger.info("ASSISTANT: {}\n", chatClient.prompt(question).call().content());
context.close();
};
}
}

View File

@@ -6,7 +6,7 @@ Follow the [MCP Client Boot Starter](https://docs.spring.io/spring-ai/reference/
## Overview
The project uses Spring Boot and Spring AI to create a command-line application that demonstrates MCP server integration. The application:
The project uses Spring Boot 3.3.6 and Spring AI 1.0.0-SNAPSHOT to create a command-line application that demonstrates MCP server integration. The application:
- Connects to MCP servers using STDIO and/or SSE (HttpClient-based) transports
- Integrates with Spring AI's chat capabilities
- Demonstrates tool execution through MCP servers
@@ -19,7 +19,7 @@ For example, running the application with `-Dai.user.input="Does Spring AI suppo
- Java 17 or later
- Maven 3.6+
- Anthropic API key (Claude) (Get one at https://docs.anthropic.com/en/docs/initial-setup)
- Brave Search API key (Get one at https://brave.com/search/api/)
- Brave Search API key (for the Brave Search MCP server) (Get one at https://brave.com/search/api/)
## Dependencies
@@ -54,6 +54,9 @@ spring.main.web-application-type=none
# AI Provider Configuration
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
# Enable the MCP client tool-callback auto-configuration
spring.ai.mcp.client.toolcallback.enabled=true
```
#### STDIO Transport Properties
@@ -117,10 +120,12 @@ The application demonstrates a simple command-line interaction with an AI model
## Running the Application
1. Set the required environment variable:
1. Set the required environment variables:
```bash
export ANTHROPIC_API_KEY=your-api-key
export BRAVE_API_KEY='your-brave-api-key-here'
# For the Brave Search MCP server
export BRAVE_API_KEY=your-brave-api-key
```
2. Build the application:
@@ -130,10 +135,14 @@ The application demonstrates a simple command-line interaction with an AI model
3. Run the application:
```bash
# Run with the default question from application.properties
java -jar target/mcp-starter-default-client-0.0.1-SNAPSHOT.jar
# Or specify a custom question
java -Dai.user.input='Does Spring AI support MCP?' -jar target/mcp-starter-default-client-0.0.1-SNAPSHOT.jar
```
The application will execute the question "Does Spring AI support MCP?", use the provided brave (or other tools) to answer it, and display the AI assistant's response.
The application will execute the question, use the configured MCP tools to answer it, and display the AI assistant's response.
## Additional Resources

View File

@@ -41,7 +41,7 @@ public class Application {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(tools)
.defaultToolCallbacks(tools)
.build();
System.out.println("\n>>> QUESTION: " + userInput);

View File

@@ -6,20 +6,21 @@ Follow the [MCP Client Boot Starter](https://docs.spring.io/spring-ai/reference/
## Overview
The project uses Spring Boot and Spring AI to create a command-line application that demonstrates MCP server integration with WebFlux. The application:
The project uses Spring Boot 3.3.6 and Spring AI 1.0.0-SNAPSHOT to create a command-line application that demonstrates MCP server integration with WebFlux. The application:
- Connects to MCP servers using STDIO and/or SSE (WebFlux-based) transports
- Integrates with Spring AI's chat capabilities
- Demonstrates tool execution through MCP servers
- Takes a user-defined question via the `-Dai.user.input` command-line property, which is mapped to a Spring `@Value` annotation in the code
For example, running the application with `-Dai.user.input="Does Spring AI support MCP?"` will inject this question into the application through Spring's property injection, and the application will use it to query the MCP server using WebFlux's reactive programming model.
For example, running the application with `-Dai.user.input="What tools are available?"` will inject this question into the application through Spring's property injection, and the application will use it to query the MCP server using WebFlux's reactive programming model.
## Prerequisites
- Java 17 or later
- Maven 3.6+
- Anthropic API key (Claude) (Get one at https://docs.anthropic.com/en/docs/initial-setup)
- Brave Search API key (Get one at https://brave.com/search/api/)
- OpenAI API key (optional, commented out by default) (Get one at https://platform.openai.com/api-keys)
- Brave Search API key (for the Brave Search MCP server) (Get one at https://brave.com/search/api/)
## Dependencies
@@ -35,6 +36,11 @@ The project uses the following main dependencies:
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
<!-- OpenAI dependency is commented out by default but can be enabled -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency> -->
</dependencies>
```
@@ -54,6 +60,10 @@ spring.main.web-application-type=none
# AI Provider Configuration
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
spring.ai.openai.api-key=${OPENAI_API_KEY}
# Enable the MCP client tool-callback auto-configuration
spring.ai.mcp.client.toolcallback.enabled=true
```
#### STDIO Transport Properties
@@ -111,16 +121,19 @@ The application demonstrates a simple command-line interaction with an AI model
1. The application starts and configures multiple MCP Clients (one for each provided STDIO or SSE connection configuration)
2. It builds a ChatClient with the configured MCP tools
3. Sends a predefined question (set vi the `ai.user.input` property) to the AI model
3. Sends a predefined question (set via the `ai.user.input` property) to the AI model
4. Displays the AI's response
5. Automatically closes the application
## Running the Application
1. Set the required environment variable:
1. Set the required environment variables:
```bash
export ANTHROPIC_API_KEY=your-api-key
export BRAVE_API_KEY='your-brave-api-key-here'
# If using OpenAI (uncomment the dependency in pom.xml first)
# export OPENAI_API_KEY=your-openai-api-key
# For the Brave Search MCP server
export BRAVE_API_KEY=your-brave-api-key
```
2. Build the application:
@@ -130,10 +143,14 @@ The application demonstrates a simple command-line interaction with an AI model
3. Run the application:
```bash
# Run with the default question from application.properties
java -jar target/mcp-starter-webflux-client-0.0.1-SNAPSHOT.jar
# Or specify a custom question
java -Dai.user.input='Does Spring AI support MCP?' -jar target/mcp-starter-webflux-client-0.0.1-SNAPSHOT.jar
```
The application will execute the question "Does Spring AI support MCP?", use the provided brave (or other tools) to answer it, and display the AI assistant's response.
The application will execute the question, use the configured MCP tools to answer it, and display the AI assistant's response.
## Additional Resources

View File

@@ -41,7 +41,7 @@ public class Application {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(tools)
.defaultToolCallbacks(tools)
.build();
System.out.println("\n>>> QUESTION: " + userInput);

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2024 - 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.client;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Christian Tzolov
*/
public class ClientSse {
public static void main(String[] args) {
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
new SampleClient(transport).run();
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2024 - 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.client;
import java.io.File;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
/**
* With stdio transport, the MCP server is automatically started by the client.
* But you
* have to build the server jar first:
*
* <pre>
* ./mvnw clean install -DskipTests
* </pre>
*/
public class ClientStdio {
public static void main(String[] args) {
System.out.println(new File(".").getAbsolutePath());
var stdioParams = ServerParameters.builder("java")
.args("-Dspring.ai.mcp.server.stdio=true", "-Dspring.main.web-application-type=none",
"-Dlogging.pattern.console=", "-jar",
"model-context-protocol/weather/starter-webflux-server/target/mcp-weather-starter-webflux-server-0.0.1-SNAPSHOT.jar")
.build();
var transport = new StdioClientTransport(stdioParams);
new SampleClient(transport).run();
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2024 - 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.mcp.sample.client;
import java.util.Map;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
/**
* @author Christian Tzolov
*/
public class SampleClient {
private final McpClientTransport transport;
public SampleClient(McpClientTransport transport) {
this.transport = transport;
}
public void run() {
var client = McpClient.sync(this.transport).build();
client.initialize();
client.ping();
// List and demonstrate tools
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult weatherForcastResult = client.callTool(new CallToolRequest("getWeatherForecastByLocation",
Map.of("latitude", "47.6062", "longitude", "-122.3321")));
System.out.println("Weather Forcast: " + weatherForcastResult);
CallToolResult alertResult = client.callTool(new CallToolRequest("getAlerts", Map.of("state", "NY")));
System.out.println("Alert Response = " + alertResult);
client.closeGracefully();
}
}

View File

@@ -10,6 +10,7 @@ The MCP Sampling Client:
- Integrates with both OpenAI and Anthropic models
- Demonstrates how to use model hints to select the appropriate LLM
- Combines creative responses from multiple LLMs into a single result
- Provides logging capabilities for MCP operations
## MCP Sampling Implementation
@@ -20,8 +21,13 @@ MCP Sampling is a powerful capability that allows an MCP server to delegate cert
```java
@Bean
McpSyncClientCustomizer samplingCustomizer(Map<String, ChatClient> chatClients) {
return (name, spec) -> {
spec.sampling(llmRequest -> {
return (name, mcpClientSpec) -> {
mcpClientSpec = mcpClientSpec.loggingConsumer(logingMessage -> {
System.out.println("MCP LOGGING: [" + logingMessage.level() + "] " + logingMessage.data());
});
mcpClientSpec.sampling(llmRequest -> {
var userPrompt = ((McpSchema.TextContent) llmRequest.messages().get(0).content()).text();
String modelHint = llmRequest.modelPreferences().hints().get(0).name();
@@ -39,6 +45,7 @@ McpSyncClientCustomizer samplingCustomizer(Map<String, ChatClient> chatClients)
return CreateMessageResult.builder().content(new McpSchema.TextContent(response)).build();
});
System.out.println("Customizing " + name);
};
}
```
@@ -61,11 +68,10 @@ public Map<String, ChatClient> chatClients(List<ChatModel> chatModels) {
4. **Integration with Spring AI**: The client leverages Spring AI's auto-configuration to set up the necessary components:
```java
var mcpToolProvider = new SyncMcpToolCallbackProvider(
mcpClientsProvider.stream().flatMap(List::stream).toList());
var mcpToolProvider = new SyncMcpToolCallbackProvider(mcpClients);
ChatClient chatClient = ChatClient.builder(openAiChatModel)
.defaultTools(mcpToolProvider)
.defaultToolCallbacks(mcpToolProvider)
.build();
```
@@ -113,6 +119,9 @@ spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080
# Logging configuration
logging.level.io.modelcontextprotocol.client=WARN
logging.level.io.modelcontextprotocol.spec=WARN
# Uncomment to disable MCP tool callbacks
# spring.ai.mcp.client.toolcallback.enabled=false
```
## How It Works
@@ -161,8 +170,8 @@ When you run the application, you'll see output similar to:
```
> USER: What is the weather in Amsterdam right now?
Please incorporate all creative responses from all LLM providers.
After the other providers add a poem that synthesizes the the poems from all the other providers.
Please incorporate all createive responses from all LLM providers.
After the other providers add a poem that synthesizes the poems from all the other providers.
> ASSISTANT: I checked the current weather in Amsterdam for you. Here are the creative responses from different AI providers:

View File

@@ -49,7 +49,7 @@ public class McpClientApplication {
var mcpToolProvider = new SyncMcpToolCallbackProvider(mcpClients);
ChatClient chatClient = ChatClient.builder(openAiChatModel).defaultTools(mcpToolProvider).build();
ChatClient chatClient = ChatClient.builder(openAiChatModel).defaultToolCallbacks(mcpToolProvider).build();
String userQuestion = """
What is the weather in Amsterdam right now?

View File

@@ -34,7 +34,7 @@ public class Application {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(new SyncMcpToolCallbackProvider(mcpClients))
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpClients))
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.build();

View File

@@ -30,7 +30,7 @@ public class Application {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(new SyncMcpToolCallbackProvider(mcpClients))
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpClients))
.build();
System.out.println("Running predefined questions with AI model responses:\n");

View File

@@ -119,7 +119,7 @@ The chatbot is implemented using Spring AI's ChatClient with MCP tool integratio
```java
var chatClient = chatClientBuilder
.defaultSystem("You are a useful assistant, expert in AI and Java.")
.defaultTools((Object[]) mcpToolAdapter.toolCallbacks())
.defaultToolCallbacks((Object[]) mcpToolAdapter.toolCallbacks())
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.build();
```

View File

@@ -9,8 +9,6 @@ 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.SyncMcpToolCallbackProvider;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -30,7 +28,7 @@ public class Application {
var chatClient = chatClientBuilder
.defaultSystem("You are useful assistant and can perform web searches Brave's search API to reply to your questions.")
.defaultTools(new SyncMcpToolCallbackProvider(mcpSyncClients))
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClients))
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.build();

View File

@@ -149,7 +149,7 @@ public class Application {
List<ToolCallback> tools, ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(tools)
.defaultToolCallbacks(tools)
.build();
String question = "Does Spring AI support the Model Context Protocol? Please provide some references.";
@@ -213,7 +213,7 @@ public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilde
List<ToolCallback> tools, ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(tools)
.defaultToolCallbacks(tools)
.build();
String question = "Does Spring AI support the Model Context Protocol? "

View File

@@ -41,7 +41,7 @@ public class Application {
return args -> {
var chatClient = chatClientBuilder
.defaultTools(new SyncMcpToolCallbackProvider(mcpSyncClients))
.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClients))
.build();
String question = "Does Spring AI support the Model Context Protocol? Please provide some references.";