王炸!Spring AI+MCP 三步實現智能體開發
環境:SpringBoot3.4.2
1. 簡介
1.1 什么是MCP
Model Context Protocol(MCP)模型上下文協議是一種標準化協議,它讓大模型能夠更容易地和外部的數據、工具連接起來。你可以把MCP想象成一個通用的插頭或者接口,就像USB-C一樣,不管是什么設備,只要插上這個接口,就能和電腦、充電器等連接起來。
注意,它連接的不是物理設備,而是AI模型和外部的數據源、工具等。有了MCP,AI模型就能更方便地獲取外部的信息,完成更多的任務。比如,通過MCP,AI模型可以操作電腦讀寫文件,或者模擬瀏覽器操作等。
1.2 為什么需要MCP
首先,MCP提供了一個標準化的接口,使得AI模型能夠輕松地與各種外部工具和數據源進行交互,無需為每個工具或數據源單獨開發集成代碼。
其次,MCP還解決了數據孤島問題,通過統一協議連接分散的數據源,使AI模型能夠實時訪問和利用最新的數據。
總的來說,MCP就像是一個橋梁,讓AI模型與外部世界更好地連接起來,從而發揮出更大的價值和潛力。
1.3 Java與MCP架構
- 客戶端/服務器層:McpClient負責處理客戶端操作,而McpServer則管理服務器端協議操作。兩者都利用McpSession來進行通信管理。
- 會話層(McpSession):通過DefaultMcpSession實現來管理通信模式和狀態。
- 傳輸層(McpTransport):處理JSON-RPC消息的序列化和反序列化,并支持多種傳輸實現。
MCP Client
MCP客戶端是模型上下文協議(MCP)架構中的關鍵組件,負責建立和管理與MCP服務器的連接。它實現了協議的客戶端部分,如下圖所示:
Java MCP Client Architecture
MCP Server
MCP服務器是模型上下文協議(MCP)架構中的基礎組件,它為客戶端提供工具、資源和功能。它實現了協議的服務器端部分,如下圖所示:
Java MCP Server Architecture
Spring AI 提供了相對應的Spring Boot staters來非常方便的進行 MCP 的集成。接下來,我們將詳細的完成一個完整的MCP應用案例。
2. 實戰案例
2.1 服務端開發
我們將在 MCP 服務端提供2個外部功能:查詢天氣預報、獲取IP地址詳細信息。
引入依賴
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
</dependency>
說明:
- 引入alibaba-starter我們將使用阿里的大模型
- 引入mcp-server-webflux,以支持基于 Spring WebFlux 的 SSE(服務器發送事件)服務器傳輸
配置文件
spring:
ai:
dashscope:
api-key: sk-xxxooo
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
chat:
options:
stream: true
model: qwen-turbo
---
spring:
ai:
mcp:
server:
enabled: true
name: ai_mcp_server
version: 1.0.0
type: ASYNC
sse-message-endpoint: /mcp/message
以上我們就完成了基本的配置。接下來,我們需要提供2個外部工具。
工具編寫
獲取天氣預報
public class CommonTool {
@Tool(description = "獲取當前天氣預報")
WeatherResponse getCurrentWeather(WeatherRequest request) {
System.err.printf("準備查詢【%s】天氣預報%n", request.city()) ;
RestClient client = RestClient.create(URI.create("https://api.vvhan.com")) ;
Map<?, ?> result = client.get()
.uri("/api/weather?city={0}", request.city())
.retrieve()
.body(Map.class) ;
try {
return new WeatherResponse(new ObjectMapper().writeValueAsString(result)) ;
} catch (JsonProcessingException e) {
throw new RuntimeException(e) ;
}
}
@Tool(description = "獲取IP地址詳細信息")
String getIpAddressInfo(String ip) {
System.err.printf("準備查詢【%s】詳細信息%n", ip) ;
RestClient client = RestClient.create(URI.create("https://api.vvhan.com")) ;
Map<?, ?> result = client.get()
.uri("/api/ipInfo?ip={0}", ip)
.retrieve()
.body(Map.class) ;
try {
return new ObjectMapper().writeValueAsString(result) ;
} catch (JsonProcessingException e) {
throw new RuntimeException(e) ;
}
}
}
注冊工具
@Configuration
public class ToolsConfig {
@Bean
ToolCallbackProvider tools() {
ToolCallback[] toolCallbacks = ToolCallbacks.from(new CommonTool()) ;
return ToolCallbackProvider.from(toolCallbacks) ;
}
}
如上所述,我們就成功構建了一個僅包含兩個外部工具的MCP服務器。
啟動服務
圖片
- 默認開啟了一個/see端點(其實,還有一個消息傳輸的端點)
- 提示注冊了2個工具(也不知道給個空格的)
接下來,我們進行客戶端的開發。
2.2 客戶端開發
引入依賴
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
注意,這里引入的是mcp-client包。
配置文件
spring:
ai:
mcp:
client:
enable: true
name: ai-mcp-client
initialized: true
type: ASYNC
sse:
connections:
server1:
url: http://localhost:8888
我們配置了一個MCP服務端地址,你可以配置多個按照上面的方式。
完成以上的配置后,我們就可以配置ChatClient,然后進行接口的調用了。
@RestController
@RequestMapping("/tools")
public class ToolController {
private final ChatClient chatClient ;
public ToolController(ChatClient.Builder aiClientBuilder, ToolCallbackProvider mcpTools) {
this.chatClient = aiClientBuilder
.defaultTools(mcpTools)
.build() ;
}
@GetMapping("/weather")
public ResponseEntity<String> getCurrentWeather(String prompt) {
System.err.println(prompt) ;
String response = this.chatClient
.prompt(prompt)
.call().content() ;
return ResponseEntity.ok(response) ;
}
@GetMapping("/ip")
public ResponseEntity<String> getIpAddressInfo(String prompt) {
System.err.println(prompt) ;
String response = this.chatClient
.prompt(prompt)
.call().content() ;
return ResponseEntity.ok(response) ;
}
}
在構造函數中,我們直接注入了ToolCallbackProvider,當我們系統啟動時會自動的從配置的服務端進行查找有哪些可用的工具。
啟動服務
控制臺將輸出如下從MCP Server獲取的工具:
i.m.s.M Line:151 - Received JSON message:
{
"jsonrpc": "2.0",
"id": "66d12dae-1",
"result": {
"tools": [
{
"name": "getCurrentWeather",
"description": "獲取當前天氣預報",
"inputSchema": {
"type": "object",
"properties": {
"request": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市"
}
},
"required": ["city"]
}
},
"required": ["request"],
"additionalProperties": false
}
},
{
"name": "getIpAddressInfo",
"description": "獲取IP地址詳細信息",
"inputSchema": {
"type": "object",
"properties": {
"ip": {
"type": "string"
}
},
"required": ["ip"],
"additionalProperties": false
}
}
]
}
}
測試結果
圖片
圖片
成功!!!