Spring Ai 集成 MCP

52 0 0 0

MCP是什么

MCP 是一个开放协议,它为应用程序向 LLM 提供上下文的方式进行了标准化。你可以将 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 为设备连接各种外设和配件提供了标准化的方式一样,MCP 为 AI 模型连接各种数据源和工具提供了标准化的接口。

Spring AI已经支持MCP服务和客户端。引入对应的依赖,就可以开发自己的MCP服务

Spring AI文档

MCP架构

MCP 采用客户端-服务器架构:

  • MCP 客户端(Client):通常是 AI 应用程序(如 Claude Desktop 或其他 LLM 工具),负责发起请求并与服务器通信。
  • MCP 服务器(Server):轻量级程序,负责暴露特定的数据源或工具功能,并通过标准化协议与客户端交互。

下面介绍如何搭建Spring AI MCP

1. 新建项目

项目结构
image.png
父级pom配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?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" 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.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.hk</groupId> <artifactId>springAi</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springAi</name> <description>springAi</description> <packaging>pom</packaging> <properties> <java.version>21</java.version> <spring-ai.version>1.0.0</spring-ai.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

新建Server子模块

该模块用来模拟演示提供城市天气数据

pom配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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" 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>com.hk</groupId> <artifactId>springAi</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.hk.springAiServer</groupId> <artifactId>springAiServer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springAiServer</name> <description>springAiServer</description> <properties> <java.version>21</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
# 端口 server: port: 8080 spring: application: name: springAiServer # MCP 服务 ai: mcp: server: name: hk-mcp-server

IHkMcpService

1
2
3
public interface IHkMcpService { String selectTq(String city); }

HkMcpServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.hk.springaiserver.service.IHkMcpService; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; /** * @Author hk * @Description * @date 2025/8/7 9:38 */ @Service public class HkMcpServiceImpl implements IHkMcpService { @Tool(description = "根据城市名称获取天气预报") @Override public String selectTq(@ToolParam(description = "城市信息")String city) { System.out.println("查询城市:" + city); return String.format("为您查询到%s今天天气:多云", city); } }

image.png

注册MCP方法

1
2
3
4
5
@Bean public ToolCallbackProvider gzhRecommendTools(IHkMcpService service) { System.out.println("初始化MCP工具"); return MethodToolCallbackProvider.builder().toolObjects(service).build(); }

启动服务端

启动后日志输出注入一个方法
image.png

新建client子模块

pom配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?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" 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>com.hk</groupId> <artifactId>springAi</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.hk.springAiClient</groupId> <artifactId>springAiClient</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springAiClient</name> <description>springAiClient</description> <properties> <java.version>21</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-ollama</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server: # 应用服务 WEB 访问端口 port: 8081 spring: application: name: springAiClient ai: ollama: # Ollama配置 base-url: http://v-kun.com:11434 chat: model: qwen3 options: temperature: 0.7 mcp: client: # MCP客户端的配置 name: mcp-client sse: connections: server1: # 要连接的服务地址 url: http://localhost:8080 toolcallback: # 默认情况下,工具回调功能处于禁用状态,需要开启 enabled: true

controller

处理用户请求调用LLM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClient.CallResponseSpec; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author hk * @Description * @date 2025/8/7 9:49 */ @RestController public class AiController { @Autowired private OllamaChatModel ollamaChatModel; @Autowired private ToolCallbackProvider toolCallbackProvider; @GetMapping("/chat") public String chat(String msg){ ChatClient chatClient = ChatClient.builder(ollamaChatModel).build(); CallResponseSpec call = chatClient.prompt(msg).toolCallbacks(toolCallbackProvider.getToolCallbacks()).call(); return call.content(); } }

启动客户端

image.png

测试MCP服务

调用接口 http://localhost:8081/chat?msg=查询武汉天气

调用接口返回结果:
好的,用户之前让我查询武汉的天气,现在工具返回了结果。我需要把结果用自然的中文告诉他。返回的信息是“为您查询到武汉今天天气:多云”,看起来是正确的。我应该直接转述这个信息,保持简洁明了。用户可能只需要知道今天的天气情况,所以不需要额外添加其他内容。确认回复没有错误后,发送给用户。 为您查询到武汉今天天气:多云。

image.png

可以看到日志调用了我们自己写的工具

集成千问

pom添加依赖

1
2
3
4
5
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency>

application.yml

添加千问配置,spring.ai.openai.api-key不添加启动会报错

1
2
3
4
5
6
7
8
9
spring: ai: openai: # 不添加启动会报错 api-key: key qwen: api-key: key endpoint: https://dashscope.aliyuncs.com/compatible-mode model: qwen-turbo

注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Value("${spring.ai.qwen.api-key}") private String qwenApiKey; @Value("${spring.ai.qwen.endpoint}") private String qwenEndpoint; @Value("${spring.ai.qwen.model}") private String qwenModel; @Bean(name = "qwenChatModel") public ChatModel qwenChatModel() { OpenAiApi openAiApi = OpenAiApi.builder() .baseUrl(qwenEndpoint) .apiKey(qwenApiKey) .build(); OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder() .model(qwenModel) .temperature(0.7) .build(); return OpenAiChatModel.builder() .openAiApi(openAiApi) .defaultOptions(openAiChatOptions) .build(); }

调用

1
2
3
4
5
6
7
8
9
@Resource(name = "qwenChatModel") private ChatModel qwenModel; @GetMapping("/ali") public String ali(String msg){ ChatClient chatClient = ChatClient.builder(qwenModel).build(); CallResponseSpec call = chatClient.prompt(msg).toolCallbacks(toolCallbackProvider.getToolCallbacks()).call(); return call.content(); }

遇到问题

多个@Tool的name都是中文报错

server多个工具
image.png
client启动时报:Multiple tools with the same name
因为注册工具时把中文过滤掉了导致提示名称重复
影响版本:spring-ai-mcp 1.0.0
image.png

解决办法

用英文名称或者中文名称加上唯一id,例如:查询天气(cxtq)

目录