Skip to content

Commit 23870c7

Browse files
codebase/exploring-model-context-protocol-with-spring-ai [BAEL-9132] (#18316)
* add MCP server * add MCP client * add test cases * update chatbot configuration * remove base API path * start server before client test cases * replace openai with anthropic model
1 parent b704113 commit 23870c7

File tree

11 files changed

+305
-0
lines changed

11 files changed

+305
-0
lines changed

spring-ai-2/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
<groupId>org.springframework.ai</groupId>
4848
<artifactId>spring-ai-markdown-document-reader</artifactId>
4949
</dependency>
50+
<dependency>
51+
<groupId>org.springframework.ai</groupId>
52+
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.springframework.ai</groupId>
56+
<artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
57+
</dependency>
5058
<dependency>
5159
<groupId>org.springframework.ai</groupId>
5260
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.baeldung.springai.mcp.client;
2+
3+
import org.springframework.ai.chat.client.ChatClient;
4+
import org.springframework.ai.chat.model.ChatModel;
5+
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
import java.util.List;
10+
11+
@Configuration
12+
class ChatbotConfiguration {
13+
14+
@Bean
15+
ChatClient chatClient(ChatModel chatModel, SyncMcpToolCallbackProvider toolCallbackProvider) {
16+
return ChatClient
17+
.builder(chatModel)
18+
.defaultTools(toolCallbackProvider.getToolCallbacks())
19+
.build();
20+
}
21+
22+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.springai.mcp.client;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.web.bind.annotation.PostMapping;
5+
import org.springframework.web.bind.annotation.RequestBody;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@RestController
9+
class ChatbotController {
10+
11+
private final ChatbotService chatbotService;
12+
13+
ChatbotController(ChatbotService chatbotService) {
14+
this.chatbotService = chatbotService;
15+
}
16+
17+
@PostMapping("/chat")
18+
ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest chatRequest) {
19+
String answer = chatbotService.chat(chatRequest.question());
20+
return ResponseEntity.ok(new ChatResponse(answer));
21+
}
22+
23+
record ChatRequest(String question) {
24+
}
25+
26+
record ChatResponse(String answer) {
27+
}
28+
29+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.baeldung.springai.mcp.client;
2+
3+
import org.springframework.ai.chat.client.ChatClient;
4+
import org.springframework.stereotype.Service;
5+
6+
@Service
7+
class ChatbotService {
8+
9+
private final ChatClient chatClient;
10+
11+
ChatbotService(ChatClient chatClient) {
12+
this.chatClient = chatClient;
13+
}
14+
15+
String chat(String question) {
16+
return chatClient
17+
.prompt()
18+
.user(question)
19+
.call()
20+
.content();
21+
}
22+
23+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.springai.mcp.client;
2+
3+
import org.springframework.ai.autoconfigure.bedrock.converse.BedrockConverseProxyChatAutoConfiguration;
4+
import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration;
5+
import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration;
6+
import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration;
7+
import org.springframework.boot.SpringApplication;
8+
import org.springframework.boot.autoconfigure.SpringBootApplication;
9+
import org.springframework.context.annotation.PropertySource;
10+
11+
/**
12+
* Excluding the below auto-configurations to avoid start up
13+
* failure. Their corresponding starters are present on the classpath but are
14+
* only needed by other articles in the shared codebase.
15+
*/
16+
@SpringBootApplication(exclude = {
17+
OllamaAutoConfiguration.class,
18+
OpenAiAutoConfiguration.class,
19+
ChromaVectorStoreAutoConfiguration.class,
20+
BedrockConverseProxyChatAutoConfiguration.class
21+
})
22+
@PropertySource("classpath:application-mcp-client.properties")
23+
class ClientApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(ClientApplication.class, args);
27+
}
28+
29+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.baeldung.springai.mcp.server;
2+
3+
import org.springframework.ai.tool.annotation.Tool;
4+
5+
import java.util.List;
6+
7+
class AuthorRepository {
8+
9+
@Tool(description = "Get Baeldung author details using an article title")
10+
Author getAuthorByArticleTitle(String articleTitle) {
11+
return new Author("John Doe", "[email protected]");
12+
}
13+
14+
@Tool(description = "Get highest rated Baeldung authors")
15+
List<Author> getTopAuthors() {
16+
return List.of(
17+
new Author("John Doe", "[email protected]"),
18+
new Author("Jane Doe", "[email protected]")
19+
);
20+
}
21+
22+
record Author(String name, String email) {
23+
}
24+
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.baeldung.springai.mcp.server;
2+
3+
import org.springframework.ai.tool.ToolCallbackProvider;
4+
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
@Configuration
9+
class MCPServerConfiguration {
10+
11+
@Bean
12+
ToolCallbackProvider authorTools() {
13+
return MethodToolCallbackProvider
14+
.builder()
15+
.toolObjects(new AuthorRepository())
16+
.build();
17+
}
18+
19+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.baeldung.springai.mcp.server;
2+
3+
import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration;
4+
import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration;
5+
import org.springframework.boot.SpringApplication;
6+
import org.springframework.boot.autoconfigure.SpringBootApplication;
7+
import org.springframework.context.annotation.PropertySource;
8+
9+
/**
10+
* Excluding the below auto-configurations to avoid start up
11+
* failure. Their corresponding starters are present on the classpath but are
12+
* only needed by other articles in the shared codebase.
13+
*/
14+
@SpringBootApplication(exclude = {
15+
OpenAiAutoConfiguration.class,
16+
ChromaVectorStoreAutoConfiguration.class
17+
})
18+
@PropertySource("classpath:application-mcp-server.properties")
19+
public class ServerApplication {
20+
21+
public static void main(String[] args) {
22+
SpringApplication.run(ServerApplication.class, args);
23+
}
24+
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
2+
spring.ai.anthropic.chat.options.model=claude-3-5-sonnet-20241022
3+
4+
spring.ai.mcp.client.sse.connections.author-tools-server.url=http://localhost:8081
5+
6+
spring.ai.mcp.client.stdio.connections.filesystem.command=npx
7+
spring.ai.mcp.client.stdio.connections.filesystem.args=-y, @modelcontextprotocol/server-filesystem, ./
8+
9+
spring.ai.mcp.client.stdio.connections.brave-search.command=npx
10+
spring.ai.mcp.client.stdio.connections.brave-search.args=-y, @modelcontextprotocol/server-brave-search
11+
spring.ai.mcp.client.stdio.connections.brave-search.env.BRAVE_API_KEY=${BRAVE_API_KEY}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
server.port=8081

0 commit comments

Comments
 (0)