写在前面
前面我们学习了MCP以及其中的三个角色,包含客户端、服务端和资源端,对应的解释如下:
(1)客户端:可理解为是Ai应用层,如聊天对话框。用户输入信息后,由客户端接口接收,再由客户端调用服务端调配资源处理;
(2)服务端:可理解为注册中心,请注意所有的工具都是注册在服务端;
(3)资源端:资源端就是大模型解析之后的用户需求的服务,即实际的处理逻辑。资源端可以和服务端部署在一起,也可以单独部署。
资源端
第一步,新建一个名为ai-mcp-server的SpringBoot项目,注意SpringBoot版本为3.4.2,JDK版本为17。之后pom.xml文件信息如下所示:
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
| <?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.4.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.gutsyzhan</groupId> <artifactId>ai-mcp-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ai-mcp-server</name> <description>ai-mcp-server</description> <properties> <java.version>17</java.version> <spring-ai.version>1.0.0-M6</spring-ai.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
第二步,新建一个名为service的包,我们在里面定义两个类,分别用于实现数据采集和数据生成功能。新建一个名为DataAcquisitionService的类,里面的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Service public class DataAcquisitionService { @Tool(description = "数据采集工具,从一个源头数据源中采集数据到目标数据源") public String acquisition( @ToolParam(description = "源头数据源")String sourceDB, @ToolParam(description = "目标数据源")String targetDB, @ToolParam(description = "源头数据表")String sourceTable, @ToolParam(description = "目标数据表")String targetTable, @ToolParam(description = "采集策略")String type ){ //采集逻辑 System.out.println("数据采集信息为:"+"\n,源头数据源=" + sourceDB + "\n,目标数据源="+ targetDB + "\n,源头数据表=" + sourceTable + "\n,目标数据表=" + targetTable + "\n,采集策略=" + type); return "数据采集成功"; } }
|
这里我们模拟数据采集的逻辑,从源头数据源的数据表中,根据指定的策略,将数据采集到目标数据源的表中。接着再新建一个名为DataInterfaceService的类,里面的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Service public class DataInterfaceService { @Tool(description = "数据服务工具,用于生成一个数据接口") public String produceMsg( @ToolParam(description = "服务名称")String interfaceName, @ToolParam(description = "数据源")String dataSource, @ToolParam(description = "查询语句")String sql, @ToolParam(description = "入参")String inParams, @ToolParam(description = "出参")String outParams ){ //生成逻辑 System.out.println("数据生成信息为:"+"\n,服务名称=" + interfaceName + "\n,数据源="+ dataSource + "\n,查询语句=" + sql + "\n,入参=" + inParams + "\n,出参=" + outParams); return "数据生成成功"; } }
|
同样这里我们模拟数据生成的逻辑,定义一个服务名称,根据传入的参数和查询语句,从源数据源中查询得到出参数据。
服务端
这里我们将服务端和资源端写在一个项目中,在applicaiton.yml配置文件中新增如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| server: port: 8009 # 服务端定义 spring: ai: mcp: server: name: ai-mcp-server version: 0.0.1 # 服务方式(同步或者异步) type: SYNC # MCP有两种通讯方式stdio(内部进程通讯)和Server-Sent Events(SSE服务器发送事件) stdio: false sse-message-endpoint: /mcp/message enabled: true # 变化通知 resource-change-notification: true tool-change-notification: true prompt-change-notification: true
|
当然,如果使用的是MCP的内部进程通讯方式,需要添加如下两行配置:
1 2
| spring.main.banner-mode=off logging.file.name=D:/logs/server.log
|
定义一个名为CustomServer的类,里面的代码如下:
1 2 3 4 5 6 7 8 9
| @Component public class CustomServer { @Bean public ToolCallbackProvider myTools(DataAcquisitionService dataAcquisitionService, DataInterfaceService dataInterfaceService){ return MethodToolCallbackProvider.builder().toolObjects(dataAcquisitionService ,dataInterfaceService).build(); } }
|
此处就是将工具注册进来,方便后续客户端调用。
客户端
客户端笔者建议另起一个新的项目来写,第一步,新建一个名为ai-mcp-client的SpringBoot项目,注意SpringBoot版本为3.4.2,JDK版本为17。之后pom.xml文件信息如下所示:
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
| <?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.4.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.gutsyzhan</groupId> <artifactId>ai-mcp-client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ai-mcp-client</name> <description>ai-mcp-client</description> <properties> <java.version>17</java.version> <spring-ai.version>1.0.0-M6</spring-ai.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
<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-ollama-spring-boot-starter</artifactId> </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.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
| spring: application: name: ai-mcp-client main: web-application-type: none ai: ollama: base-url: http://127.0.0.1:11434 chat: options: model: qwen3:1.7b temperature: 0.7 mcp: client: sse: connections: server1: url: http://127.0.0.1:8009 server: port: 8010 # ai.user.input表示用户的需求 ai: user: input: 第一步,将prod数据源中的t_order表中的数据全量采集到test数据源的t_order表中,第二步再生成一个数据接口,用于获取所有的用户,从prod数据源中查询全量的用户数据,入参为default,出参为用户名和时间
|
第三步,新建一个名为client的包,并在里面定义一个名为CustomClient的类,里面的代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Component public class CustomClient { @Value("${ai.user.input}") private String userInput;
@Bean public CommandLineRunner solveQuestion(ChatClient.Builder chatBuilder, ToolCallbackProvider tools, ConfigurableApplicationContext context){ return (args -> { ChatClient chatClient = chatBuilder.defaultTools(tools).build(); System.out.println("\n>>> 问题为:" + userInput); System.out.println("\n>>> 参考方法为:" + chatClient.prompt(userInput).call().content()); context.close(); }); } }
|
可以看到这里我们将用户输入的信息直接写在了配置文件中,通过@Value注解进行获取。项目在启动时,会将其传入给服务端,服务端将用户输入和注册的工具列表一起传给大模型。大模型会根据需求来进行工具匹配,匹配到对应工具之后,再看参数是否对应的上,如果对应的上,则执行具体的逻辑,如果匹配不上,则不执行对应逻辑。
第四步,先启动服务端项目,再启动客户端,可以发现此时客户端输出如下信息:

接着再去看服务端,可以看到客户端也输出如下信息:

小结
本篇通过一个实战项目学习了如何使用SpringBoot和MCP实现本地工具调用。请注意,如果开发者存在多个工具进行编排的需求,如何保证工具调用的顺序呢?实际上我们只需在问题描述中,写清楚你每一步需要做什么,然后大模型就会找到对应的工具类,并按照顺序进行调用,所不同的是,这种编排动作是在输入内容中完成的。
看到这里你可能会有疑问,MCP方式和注入Dify配置工作流这种方式有什么区别?我们知道MCP它是大模型上下文协议,提供了一种大模型调用本地工具,使用本地文件的一种规范,开发者可以使用这种方式将本地逻辑发布为工具,以满足用户对需求不明的诉求,如果大模型找不到满足要求的工具,则会给出一个合适的回复,这样用户也能接受。