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

从 0 开始学习大模型应用开发(加餐二)- 使用Spring AI开发MCP系统

项目源码地址:

GitHub - brianxiadong/llm-langchain-project: llm-langchain-project

路过的朋友请帮忙点个star

一、什么是 Spring AI

Spring AI (https://spring.io/projects/spring-ai)]是 Spring 官方于 2023 年推出的 AI 应用开发框架,它如同 AI 世界的"Spring 生态连接器",致力于简化开发集成了 AI 功能的应用程序。它主要解决两大核心问题:

  1. 统一接口:消除不同 AI 服务(如 OpenAI、智谱 AI、DeepSeek 等)的 API 差异,允许用户灵活地在多个模型之间切换。
  2. 生态整合:将 AI 能力无缝融入 Spring 技术栈(如 Spring Boot、Spring MVC)。

在这里插入图片描述

如上图所示,Spring AI 成为了连接企业数据以及 API 和生成式 AI 的桥梁。

🛠️ 核心功能矩阵

Spring AI 具备的能力如下:

它具备以下特点:

  1. 即插即用:通过更换 application.yml 配置即可切换大模型供应商。
  2. 响应式支持:原生集成 Reactor Streams,支持背压控制的流式响应。
  3. 扩展性强:自定义 ChatClient 实现可接入任意 AI 服务。

😎Spring AI 能做什么?

那么 Spring AI 到底能做什么呢?这里我给出几个常见案例,在咱们这套课程中也会带着大家完成这些案例:

案例 1:智能客服系统

  • 场景:集成自然语言处理的客服机器人,处理用户咨询、订单查询和售后问题。

  • AI 价值:

    • 减少人工客服成本,24/7 响应客户需求。
    • 通过意图识别提升问题解决效率(准确率可达 85%+)。
  • Spring AI 实现:
    使用 Spring AI 的对话模型接口(如 OpenAI 或 智谱 AI),快速构建企业级对话流。


案例 2:智能数据分析平台

  • 场景:自动分析企业销售数据、用户行为日志,生成可视化报告和预测建议。

  • AI 价值:

    • 通过时间序列预测优化库存管理(降低 20%-30% 滞销风险)。
    • 实时异常检测(如金融反欺诈)。
  • Spring AI 实现:
    结合 Spring AI 和 MCP 组件,对数据库进行访问,实现数据预处理与预测 API。


案例 3:自动化文档处理

  • 场景:合同、发票的自动分类、关键词提取和合规性审查。

  • AI 价值:

    • 节省 90% 人工文档处理时间。
    • 通过 OCR 实现非结构化数据标准化。
  • Spring AI 实现:
    调用 Spring AI 的多模态接口,实现文档解析流水线。


案例 4:智能营销内容生成

  • 场景:自动化生成广告文案、社交媒体推文、邮件营销内容,支持多语言适配。

  • AI 价值:

    • 缩短 70% 的创意内容生产周期。
    • 通过 A/B 测试数据反馈优化生成策略(点击率提升 15%-25%)。
  • Spring AI 实现:
    调用 大模型接口,结合企业品牌风格指南定制生成规则。


案例 5:语音客服系统

  • 场景:通过语音交互处理用户来电(如银行催收、快递查询、政务热线),支持多语言、方言识别和情感分析。

  • AI 价值:

    • 成本降低:替代 60% 以上重复性语音服务(如账单查询)。
    • 效率提升:语音响应速度 <1 秒(传统 IVR 需 5-10 秒菜单导航)。
  • Spring AI 实现:

    1. 语音识别
      • 集成 Whisper 模型,将用户语音转为文本。
      • 支持实时流式传输(降低延迟)。
    2. 自然语言处理(NLP)
      • 使用 Spring AI 的对话模型解析用户意图,生成响应文本。
      • 情感分析:识别用户情绪(愤怒/焦虑),触发人工坐席接管。
    3. 语音合成(TTS)
      • 调用 AI 模型生成拟人化语音反馈。

二、MCP的三种类型

1. stdio(标准输入输出)
工作原理:
基于进程间通信,客户端启动 MCP Server 子进程,通过标准输入(stdin)接收请求,标准输出(stdout)返回响应,消息格式为 JSON-RPC 2.0。

技术特点:

  • 低延迟:无需网络开销,适合实时性要求高的本地场景(如 IDE 插件与进程交互)。
  • 强耦合性:客户端与服务器需运行在同一主机,呈 1:1 绑定关系。
  • 安全性高:数据不经过网络传输,可避免中间人攻击等风险。
    适用场景:
    主要用于本地或容器化环境下的进程间通信,简化部署流程,实现跨平台轻量高效部署。

2. sse(Server-Sent Events)
工作原理:
基于 HTTP 请求,服务器到客户端的通信使用 SSE,客户端到服务器的通信使用 POST 请求,消息格式为 JSON-RPC 2.0。
技术特点:

  • 适合远程连接:是 MCP 在网络环境中的主要实现技术,适用于云服务和远程 API 调用。
  • 长连接特性:依赖长连接维持通信,服务器需保持连接不中断,属于有状态连接。
  • 并发处理需求:HTTP 服务器需具备并发能力(如通过进程池提升并行处理能力)。

适用场景:
适用于服务器向客户端实时推送消息的远程场景(如实时数据更新、服务器状态通知),但并发管理较复杂。

3. streamable http
工作原理:
扩展的 HTTP 协议,允许服务器生成数据时逐步发送给客户端,可动态将普通 HTTP 请求升级为 SSE 流(用于通知或请求)。所有客户端到服务器的消息通过统一的 /message 端点传输,客户端通过头部提供 session id。
技术特点:

  • 灵活性高:服务器可自主决定是否支持流式传输,无需单独的 /sse 端点。
  • 支持无状态服务器:消除持久连接需求,资源在请求处理后释放,适合高并发场景。
  • 兼容性强:基于标准 HTTP,可无缝集成 CDN、API 网关、负载均衡器等,支持在 Vercel、Cloudflare、AWS Lambda 等云平台部署。

适用场景:
适用于各种网络环境,尤其适合对服务器灵活性、可扩展性和兼容性要求高的场景,适配现代分布式系统和高并发应用。


三、使用Spring AI搭建stdio的MCP服务端程序

官方文档:
https://docs.spring.io/spring-ai/reference/1.1-SNAPSHOT/api/mcp/mcp-server-boot-starter-docs.html
官方案例:
https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server

环境要求

  • Java 17 或更高版本
  • Maven 3.6 或更高版本
  • Spring Boot 3.4.5
  • Spring AI 1.1.0-SNAPSHOT

项目结构

spring-ai-mcp-weather/
├── pom.xml
├── src/
│   └── main/
│       ├── java/
│       │   └── org/springframework/ai/mcp/saample/server/
│       │       ├── McpServerApplication.java
│       │       └── WeatherService.java
│       └── resources/
│           └── application.properties
├── start-mcp-server.sh
└── mcp-config-example.json

步骤1:创建Maven项目

pom.xml 配置

<?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 http://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.5</version><relativePath /></parent><groupId>com.example</groupId><artifactId>spring-ai-mcp-weather</artifactId><version>0.0.1-SNAPSHOT</version><name>Spring AI MCP Weather Server</name><description>Spring AI MCP Weather STDIO Server</description><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.1.0-SNAPSHOT</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><!-- Spring AI MCP Server Starter --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server</artifactId></dependency><!-- Spring Web for RestClient --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency><!-- Test Dependencies --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><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></repositories>
</project>

步骤2:配置应用属性

application.properties

# 禁用Web应用类型,使用STDIO传输
spring.main.web-application-type=none# 重要:必须禁用banner和控制台日志以使STDIO传输正常工作
spring.main.banner-mode=off
logging.pattern.console=# MCP服务器配置
spring.ai.mcp.server.name=weather-mcp
spring.ai.mcp.server.version=0.0.1# 日志配置
logging.file.name=./logs/mcp-weather-server.log
logging.level.org.springframework.ai.mcp=DEBUG

步骤3:创建主应用类

McpServerApplication.java

package org.springframework.ai.mcp.sample.server;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();}
}

步骤4:实现天气服务

WeatherService.java

package org.springframework.ai.mcp.sample.server;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;@Service
public class WeatherService {private static final String BASE_URL = "https://api.weather.gov";private final RestClient restClient;public WeatherService() {this.restClient = RestClient.builder().baseUrl(BASE_URL).defaultHeader("Accept", "application/geo+json").defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)").build();}// 数据传输对象@JsonIgnoreProperties(ignoreUnknown = true)public record Points(@JsonProperty("properties") Props properties) {@JsonIgnoreProperties(ignoreUnknown = true)public record Props(@JsonProperty("forecast") String forecast) {}}@JsonIgnoreProperties(ignoreUnknown = true)public record Forecast(@JsonProperty("properties") Props properties) {@JsonIgnoreProperties(ignoreUnknown = true)public record Props(@JsonProperty("periods") List<Period> periods) {}@JsonIgnoreProperties(ignoreUnknown = true)public record Period(@JsonProperty("number") Integer number,@JsonProperty("name") String name,@JsonProperty("startTime") String startTime,@JsonProperty("endTime") String endTime,@JsonProperty("isDaytime") Boolean isDayTime,@JsonProperty("temperature") Integer temperature,@JsonProperty("temperatureUnit") String temperatureUnit,@JsonProperty("temperatureTrend") String temperatureTrend,@JsonProperty("probabilityOfPrecipitation") Map probabilityOfPrecipitation,@JsonProperty("windSpeed") String windSpeed,@JsonProperty("windDirection") String windDirection,@JsonProperty("icon") String icon,@JsonProperty("shortForecast") String shortForecast,@JsonProperty("detailedForecast") String detailedForecast) {}}@JsonIgnoreProperties(ignoreUnknown = true)public record Alert(@JsonProperty("features") List<Feature> features) {@JsonIgnoreProperties(ignoreUnknown = true)public record Feature(@JsonProperty("properties") Properties properties) {}@JsonIgnoreProperties(ignoreUnknown = true)public record Properties(@JsonProperty("event") String event,@JsonProperty("areaDesc") String areaDesc,@JsonProperty("severity") String severity,@JsonProperty("description") String description,@JsonProperty("instruction") String instruction) {}}/*** 获取指定经纬度的天气预报*/@Tool(description = "Get weather forecast for a specific latitude/longitude")public String getWeatherForecastByLocation(double latitude, double longitude) {var points = restClient.get().uri("/points/{latitude},{longitude}", latitude, longitude).retrieve().body(Points.class);var forecast = restClient.get().uri(points.properties().forecast()).retrieve().body(Forecast.class);String forecastText = forecast.properties().periods().stream().map(p -> {return String.format("""%s:Temperature: %s %sWind: %s %sForecast: %s""", p.name(), p.temperature(), p.temperatureUnit(), p.windSpeed(), p.windDirection(), p.detailedForecast());}).collect(Collectors.joining());return forecastText;}/*** 获取美国各州的天气警报*/@Tool(description = "Get weather alerts for a US state. Input is Two-letter US state code (e.g. CA, NY)")public String getAlerts(@ToolParam(description = "Two-letter US state code (e.g. CA, NY") String state) {Alert alert = restClient.get().uri("/alerts/active/area/{state}", state).retrieve().body(Alert.class);return alert.features().stream().map(f -> String.format("""Event: %sArea: %sSeverity: %sDescription: %sInstructions: %s""", f.properties().event(), f.properties().areaDesc(), f.properties().severity(), f.properties().description(), f.properties().instruction())).collect(Collectors.joining("\n"));}
}

步骤5:构建和运行

构建项目

mvn clean package

创建启动脚本

创建 start-mcp-server.sh 文件:

#!/bin/bash# JAR文件路径
JAR_FILE="target/spring-ai-mcp-weather-0.0.1-SNAPSHOT.jar"# 检查JAR文件是否存在
if [ ! -f "$JAR_FILE" ]; thenecho "错误: JAR文件不存在: $JAR_FILE"echo "请先运行: mvn clean package"exit 1
fi# 检查Java是否安装
if ! command -v java &> /dev/null; thenecho "错误: 未找到Java运行环境"echo "请安装Java 17或更高版本"exit 1
fiecho "正在启动MCP服务器..."
java -jar "$JAR_FILE"

运行服务器

chmod +x start-mcp-server.sh
./start-mcp-server.sh

步骤6:MCP客户端配置

Claude Desktop配置

创建 mcp-config.json 文件:

{"mcpServers": {"weather": {"command": "java","args": ["-jar","全路径/spring-ai-mcp-weather-0.0.1-SNAPSHOT.jar"],"env": {"JAVA_OPTS": "-Xmx512m"}}}
}

关键配置说明

1. STDIO传输配置

MCP使用STDIO进行通信,因此必须:

  • 设置 spring.main.web-application-type=none
  • 禁用banner:spring.main.banner-mode=off
  • 清空控制台日志格式:logging.pattern.console=

2. Tool注解

  • @Tool:标记方法为可调用的工具
  • @ToolParam:为参数提供描述信息
  • 方法必须是public且返回String类型

3. 依赖管理

  • 使用Spring AI BOM管理版本
  • 核心依赖:spring-ai-starter-mcp-server
  • Web客户端:spring-web

测试工具

可以通过以下方式测试MCP服务器:

  1. 直接调用:在WeatherService中添加main方法进行测试
  2. MCP客户端:配置Claude Desktop或其他MCP客户端
  3. 单元测试:编写Spring Boot测试用例

扩展功能

添加新工具

  1. 在服务类中添加带@Tool注解的方法
  2. 在主应用类中注册ToolCallbackProvider
  3. 重新构建和部署

错误处理

@Tool(description = "Safe weather forecast with error handling")
public String getSafeWeatherForecast(double latitude, double longitude) {try {return getWeatherForecastByLocation(latitude, longitude);} catch (RestClientException e) {return "Weather service temporarily unavailable: " + e.getMessage();}
}

常见问题

  1. STDIO通信失败:检查是否正确禁用了banner和控制台日志
  2. 工具未注册:确保在主应用类中正确配置了ToolCallbackProvider
  3. API调用失败:检查网络连接和API密钥配置

总结

通过以上步骤,您已经成功创建了一个完整的Spring AI MCP服务端程序。该服务器提供了天气预报和天气警报两个工具,可以被任何支持MCP协议的AI助手调用。

http://www.xdnf.cn/news/9610.html

相关文章:

  • Java 事务管理:在分布式系统中实现可靠的数据一致性
  • Micro-CT扫描成像的样本处理与样本要求技术指南
  • 浅谈国企数字化转型
  • 2025年5月通信科技领域周报(5.19-5.25):太赫兹通信规模商用启动 空天地一体化网络加速落地
  • “从复眼到智慧”:观测云2025发布会专访—— CEO 蒋烁淼
  • Python兴趣匹配算法:从理论到实战的进阶指南
  • Echarts实现3D地图(多层geo)同步缩放
  • LangChain + Redis:实现持久化的聊天历史记录管理
  • 华为认证是什么?网络工程师的华为认证考试详解
  • ActiveMQ 可观测性最佳实践
  • 日立HDS G350存储Dynamic Link Manager(HDLM)在linux系统多路径绑定
  • ChatGPT + 知网 + 知乎,如何高效整合信息写出一篇专业内容?
  • 多元素纳米颗粒:开启能源催化新纪元
  • C语言_编译和链接
  • 基于stm32风速风向温湿度和瓦斯检测(仿真+代码)
  • 如何在python3.8环境中安装pytorch
  • 子网的划分
  • 深圳南柯电子|交流电机EMC整改:电磁兼容问题为何成为核心挑战
  • 程序员出海手册
  • 国际数字影像产业园:以科技赋能,打造文创产业升级新引擎
  • DAY 36神经网络加速器easy
  • 遥控器主副控设计运行要点分析!
  • AI任务相关解决方案2-基于WOA-CNN-BIGRU-Transformer模型解决光纤通信中的非线性问题
  • MMR-Mamba:基于 Mamba 和空间频率信息融合的多模态 MRI 重建|文献速递-深度学习医疗AI最新文献
  • 简述多路复用与多路分解
  • linux系统(centos7为例)将jar配置成服务操作教程
  • 专业课复习笔记 10
  • Allegro X PCB设计小诀窍--05.如何在Allegro X中实现隐藏电源飞线效果
  • 如何解决幻读问题?
  • 生成圆锥的点云图像