当前位置: 首页 > news >正文

基于 Spring AI 的 MCP 客户端/服务端实现

        模型上下文协议(MCP)由Anthropic开源的开放协议,为AI模型与外部数据/工具提供了“标准化桥梁”,通过统一的接口规范,使模型能够动态调用本地文件、数据库、API等资源,实现“上下文感知”的智能交互。MCP的核心价值在于:

  • 标准化集成:告别“一对一”定制开发,通过协议对接即可连接任意兼容MCP的数据源或工具,大幅降低生态构建成本。

  • 安全与灵活性:支持本地部署,数据无需离境,兼顾隐私合规与实时访问需求。

  • 智能体赋能:为AI Agent提供“手脚”,使其能自主执行查询、分析、操作等复杂任务流。

        在 MCP)​的上下文中,STDIO 和 SSE 是两种不同的通信方式,用于实现客户端(如开发工具、IDE 等)与服务器(如语言模型、知识库服务等)之间的交互。

  STDIO 是指通过标准输入(stdin)和标准输出(stdout)进行通信的方式。这是一种常见的进程间通信(IPC)机制,通常用于本地运行的 MCP 服务器与客户端之间的交互。一般用于本地开发工具集成

  SSE(Server-Sent Events)是一种基于 HTTP 的服务器推送技术,用于实现服务器主动向客户端推送数据的通信方式。与 STDIO 不同,SSE 是一种网络通信机制,适用于客户端和服务器分布在不同机器上的场景。

     Spring AI作为Java生态中领先的AI开发框架,通过深度集成MCP协议,为开发者提供了企业级解决方案:其模块化架构、对同步/异步通信的支持、以及与Spring Boot的无缝融合,使得构建本地MCP客户端与服务端变得高效且可靠。无论是快速搭建文件系统的本地数据中台,还是构建与业务系统(如CRM、ERP)的实时联动,Spring AI的声明式配置、注解驱动开发模式极大降低了技术门槛。

代码(SSE模式)

mcp server

创建项目mcp-server,依赖如下:

<?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.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.sqz</groupId><artifactId>mcp-server</artifactId><version>0.0.1-SNAPSHOT</version><name>mcp-server</name><description>mcp-server</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>21</java.version></properties><dependencies><!-- 只支持 STDIO 传输,使用如下两个依赖 --><!--        <dependency>--><!--            <groupId>org.springframework.ai</groupId>--><!--            <artifactId>spring-ai-starter-mcp-server</artifactId>--><!--        </dependency>--><!--        <dependency>--><!--            <groupId>org.springframework</groupId>--><!--            <artifactId>spring-web</artifactId>--><!--        </dependency>--><!-- 支持 SSE 传输,使用如下依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webflux</artifactId></dependency><dependency><groupId>org.json</groupId><artifactId>json</artifactId><version>20210307</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-M7</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository><repository><id>aliyunmaven</id><name>aliyun</name><url>https://maven.aliyun.com/repository/public</url></repository></repositories><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

 定义一个查询天气的工具:

package com.sqz.mcpserver.weather;import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;@Service
@Slf4j
public class WeatherService {private static final String BASE_URL = "http://api.openweathermap.org/data/2.5/weather";@Value("${OPEN_WEATHER_API_KEY}")private String OPEN_WEATHER_API_KEY;public RestTemplate restTemplate = new RestTemplate();@Tool(description = "获取指定城市的当前天气情况,返回格式化后的天气报告")public String getWeather(@ToolParam(description = "城市名称必须是英文,例如:London 或者 Beijing") String city) throws UnsupportedEncodingException {log.info("====调用了查询天气=====");String query = String.format("?q=%s&APPID=%s&units=metric&lang=zh_cn",URLEncoder.encode(city, "UTF-8"),URLEncoder.encode(OPEN_WEATHER_API_KEY, "UTF-8"));ResponseEntity<String> responseEntity = restTemplate.getForEntity(BASE_URL + query, String.class);if (responseEntity.getStatusCode().is2xxSuccessful()) {String body = responseEntity.getBody();JSONObject data = new JSONObject(body);JSONObject main = data.getJSONObject("main");JSONArray weatherArray = data.getJSONArray("weather");JSONObject weather = weatherArray.getJSONObject(0);JSONObject wind = data.getJSONObject("wind");String weatherDescription = weather.optString("description", "无描述");double temperature = main.optDouble("temp", Double.NaN);double feelsLike = main.optDouble("feels_like", Double.NaN);double tempMin = main.optDouble("temp_min", Double.NaN);double tempMax = main.optDouble("temp_max", Double.NaN);int pressure = main.optInt("pressure", 0);int humidity = main.optInt("humidity", 0);double windSpeed = wind.optDouble("speed", Double.NaN);return String.format("""城市: %s 天气描述: %s 当前温度: %.1f°C 体感温度: %.1f°C 最低温度: %.1f°C 最高温度: %.1f°C 气压: %d hPa 湿度: %d%% 风速: %.1f m/s """, data.optString("name", city),weatherDescription,temperature,feelsLike,tempMin,tempMax,pressure,humidity,windSpeed);}return null;}}

在启动类里面注册工具:

package com.sqz.mcpserver;import com.sqz.mcpserver.weather.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.config.EnableWebFlux;@SpringBootApplication
@EnableWebFlux
public class McpServerApplication {public static void main(String[] args) {SpringApplication.run(McpServerApplication.class, args);}@Beanpublic ToolCallbackProvider weatherTools(WeatherService weatherService) {return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();}
}

配置文件:

spring.application.name=mcp-server
#指定 MCP 服务器的名称为 spring-ai-mcp-weather
spring.ai.mcp.server.name=spring-ai-mcp-weather#配置应用监听的端口为 8081
server.port=8081#禁用 Spring Boot 启动时的横幅(Banner)显示,对于使用 STDIO 传输的 MCP 服务器,禁用横幅有助于避免输出干扰。
spring.main.banner-mode=off#如下参数启用并设置为空,将禁用控制台日志输出格式,减少输出干扰
logging.pattern.console=#配置日志文件的输出路径,将日志写入指定的文件中
logging.file.name=D:/java-workspace/ai-learning-java-repository/mcp-server/mcp-weather-stdio-server.log#访问 OpenWeather API 的密钥,自己获取每天可免费调用100次
OPEN_WEATHER_API_KEY=#设置日志的根级别为 INFO
logging.level.root=INFO

mcp client

创建项目mcp-server,依赖如下:

<?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.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.sqz</groupId><artifactId>mcp-client</artifactId><version>0.0.1-SNAPSHOT</version><name>mcp-client</name><description>mcp-client</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><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><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client</artifactId></dependency><!-- 导入 Spring AI - Anthropic 依赖--><!--        <dependency>--><!--            <groupId>org.springframework.ai</groupId>--><!--            <artifactId>spring-ai-starter-model-anthropic</artifactId>--><!--        </dependency>--><!-- 导入 Spring AI - OpenAI 依赖--><!--        <dependency>--><!--            <groupId>org.springframework.ai</groupId>--><!--            <artifactId>spring-ai-starter-model-openai</artifactId>--><!--        </dependency>--><!-- 导入Spring AI Alibaba - 通义千问依赖 --><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M6.1</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-M7</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>

使用CommandLineRunner启动聊天机器人,代码:

package com.sqz.mcpclient;import io.modelcontextprotocol.client.McpSyncClient;
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.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.util.List;
import java.util.Scanner;@SpringBootApplication
public class McpClientApplication {public static void main(String[] args) {SpringApplication.run(McpClientApplication.class, args);}/*** 启动聊天机器人* CommandLineRunner 是 Spring Boot 提供的一个接口,用于在应用程序启动完成后执行一些初始化任务或自定义逻辑。它非常适合用于演示、测试或预加载数据等场景。* 当 Spring Boot 应用程序启动时,它会扫描应用程序中的 CommandLineRunner 实现类,并按照它们的顺序执行它们的 run 方法。* 这样,你可以在应用程序启动时执行一些初始化操作,而不需要在代码中显式地调用这些操作。* 例如,你可以使用 CommandLineRunner 来加载数据库数据、预加载缓存、执行一些一次性任务等。* 这使得应用程序的启动过程更加简洁和可维护,同时也方便了测试和调试。* @param chatClientBuilder 用于构建聊天客户端的构建器**/@Beanpublic CommandLineRunner chatbot(ChatClient.Builder chatClientBuilder, List<McpSyncClient> mcpSyncClients) {return args -> {// 构建聊天客户端ChatClient chatClient = chatClientBuilder//设置系统提示,引导 AI 的行为和角色.defaultSystem("你是一个可以查询天气的助手,可以调用工具回答用户关于天气相关问题。")//配置工具回调提供者,使 AI 能调用外部工具.defaultTools(new SyncMcpToolCallbackProvider(mcpSyncClients))//设置对话记忆,使用内存存储对话历史,保持上下文.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory())).build();// 开始聊天循环System.out.println("\n我是你的AI助手。\n");try (Scanner scanner = new Scanner(System.in)) {while (true) {System.out.print("\n用户: ");String content = scanner.nextLine();System.out.println("\n助手: " +chatClient.prompt(content) // chatClient.prompt(...):将用户输入作为提示发送给 LLM.call().content());//.call().content():调用 LLM 模型并获取响应内容}}};}}

配置:

spring.application.name=mcp-client#指定应用类型为非 Web 应用,适用于命令行工具、批处理任务或仅作为客户端运行的场景
spring.main.web-application-type=none#使用 Claude LLM,需要在pom.xml中引入对应依赖
#spring.ai.anthropic.api-key=your_anthropic_api_key
#spring.ai.anthropic.chat.options.model=claude-3-haiku-20240307#使用 Deepseek LLM,需要在pom.xml中引入对应依赖
#spring.ai.openai.api-key=your_deepseek_api_key
#spring.ai.openai.base-url=https://api.deepseek.com
#spring.ai.openai.chat.options.model=deepseek-chat#使用 通义千问 LLM,需要在pom.xml中引入对应依赖
spring.ai.dashscope.api-key=sk-d7805feb808941c8a929b96414725274
spring.ai.dashscope.chat.options.model=qwen-plus#STDIO 模式,指定 MCP 客户端的服务器配置文件路径
#spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json#SSE 模式,配置名为 server1 的 MCP 服务器连接,远程连接到指定的服务器地址
spring.ai.mcp.client.sse.connections.server1=http://localhost:8081

启动server再启动client,在控制台输入指令测试:

STDIO模式

在mcp-client项目执行打包mvn clean package -DskipTests,然后在resources下面新建文件mcp-servers-config.json:

{"mcpServers": {"spring-ai-mcp-weather": {
#java命令注意版本及以上"command": "C:\\Program Files\\Java\\jdk-21\\bin\\java.exe","args": ["-Dspring.ai.mcp.server.transport=STDIO","-jar",
#自己serverjar包"D:\\java-workspace\\ai-learning-java-repository\\mcp-server\\target\\mcp-server-0.0.1-SNAPSHOT.jar"]}}
}

修改mcp-client配置文件:

#指定应用类型为非 Web 应用,适用于命令行工具、批处理任务或仅作为客户端运行的场景
spring.main.web-application-type=none#使用 Claude LLM,需要在pom.xml中引入对应依赖
#spring.ai.anthropic.api-key=your_anthropic_api_key
#spring.ai.anthropic.chat.options.model=claude-3-haiku-20240307#使用 Deepseek LLM,需要在pom.xml中引入对应依赖
#spring.ai.openai.api-key=your_deepseek_api_key
#spring.ai.openai.base-url=https://api.deepseek.com
#spring.ai.openai.chat.options.model=deepseek-chat#使用 通义千问 LLM,需要在pom.xml中引入对应依赖
spring.ai.dashscope.api-key=sk-d7805feb808941c8a929b96414725274
spring.ai.dashscope.chat.options.model=qwen-plus#STDIO 模式,指定 MCP 客户端的服务器配置文件路径
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json#SSE 模式,配置名为 server1 的 MCP 服务器连接,远程连接到指定的服务器地址
#spring.ai.mcp.client.sse.connections.server1=http://localhost:8081

修改mcp-server依赖:

 <!-- 只支持 STDIO 传输,使用如下两个依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency><!-- 支持 SSE 传输,使用如下依赖 --><!--       <dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webflux</artifactId></dependency>-->

mcp-server启动类去掉webflux:

package com.sqz.mcpserver;import com.sqz.mcpserver.weather.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class McpServerApplication {public static void main(String[] args) {SpringApplication.run(McpServerApplication.class, args);}@Beanpublic ToolCallbackProvider weatherTools(WeatherService weatherService) {return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();}
}

不用启动mc-server,直接启动mcp-client,mcp-client会自动启动mcp-server测试如下:

相关文章:

  • 前端项目主题切换
  • 统计学习—有监督part
  • 数组方法_join()+_concat()+_reverse()+ _indexOf()
  • java大文件分段下载
  • 论数据分流部署模式
  • 组织结构图软件:数据驱动的可视化架构管理工具
  • UE5 读取配置文件
  • ue5.5 landscape paint模式下 layers出不来
  • [论文阅读] 算法 | 布谷鸟算法在声源定位中的应用研究
  • 布尔字段命名陷阱:避免序列化错误的关键
  • 个人网站图片托管存储桶迁移全记录
  • 【Qt】输入类控件 QLineEdit、QTextEdit、QComboBox、QSpinBox、QDateTimeEdit、QDial、QSlider
  • 使用OpenCV和Python进行图像掩膜与直方图分析
  • 高频面试之5Kafka
  • MySQL虚拟列:一个被低估的MySQL特性
  • SDXL 和 SDXL-Turbo 的区别
  • C语言数据结构笔记6:使用宏和指针来设置和操作嵌套在结构体中的联合体数组的特定位
  • SpringBoot学习day1-SpringBoot的简介与搭建
  • SD-WAN 技术如何助力工业物联网(IIoT)数据传输?深度解析传统方案对比与应用实践
  • 解码 K-Means 聚类:开启数据星河的炫酷聚类新纪元
  • 淄博高端网站设计/永久免费自助建站系统
  • wordpress 高级搜索/合肥关键词优化平台
  • 吕梁网站建设kuyiso/友情链接源码
  • 腾讯企点怎么删除聊天记录/seo云优化方法
  • 网站建设 模版选择中心/互联网销售公司
  • 成都网站优化seo/网站内链优化