大模型之Spring AI实战系列(二十三):Spring AI + MCP + 自定义MCP服务开发实战

系列篇章💥

No.文章
1大模型之Spring AI实战系列(一):基础认知篇 - 开启智能应用开发之旅
2大模型之Spring AI实战系列(二):Spring Boot + OpenAI 打造聊天应用全攻略
3大模型之Spring AI实战系列(三):Spring Boot + OpenAI 实现聊天应用上下文记忆功能
4大模型之Spring AI实战系列(四):Spring Boot + OpenAI 使用OpenAI Embedding实现文本向量化
5大模型之Spring AI实战系列(五):Spring Boot + OpenAI 构建带角色设定的智能对话系统
6大模型之Spring AI实战系列(六):Spring Boot + OpenAI 利用PromptTemplate构建动态提示词系统
7大模型之Spring AI实战系列(七):Spring Boot + OpenAI 构建结构化输出的AI响应系统
8大模型之Spring AI实战系列(八):Spring Boot + OpenAI 使用Whisper实现语音转文本功能
9大模型之Spring AI实战系列(九):Spring Boot + OpenAI 使用TTS实现文本转语音功能
10大模型之Spring AI实战系列(十):Spring Boot + OpenAI 使用 DALL·E实现文本生成图像功能
11大模型之Spring AI实战系列(十一):Spring Boot + OpenAI 集成本地向量数据库Chroma
12大模型之Spring AI实战系列(十二):Spring Boot + OpenAI 构建基于RAG的智能问答系统
13大模型之Spring AI实战系列(十三):Spring Boot + OpenAI 基于 Tool Calling 实现单个外部工具调用
14大模型之Spring AI实战系列(十四):Spring Boot + OpenAI 支持多个 Tool 的插件化调用实践
15大模型之 Spring AI实战系列(十五):Spring AI Tools 初体验——搭建首个可调用工具
16大模型之 Spring AI实战系列(十六):Spring AI Tools 初级开发——解锁天气查询与数据库操作工具
17大模型之 Spring AI实战系列(十七):Spring AI Tools 高级技巧——异步调用、事务控制与错误处理攻略
18大模型之 Spring AI实战系列(十八):Spring AI Tools 进阶实战——深度集成 RESTful API 联通外部服务
19大模型之Spring AI实战系列(十九):Spring Boot + 智谱AI 一站式开发指南,带你玩转大模型
20大模型之Spring AI实战系列(二十):Spring AI + MCP + Brave Search 实战指南
21大模型之Spring AI实战系列(二十一):Spring AI + MCP + 本地文件系统实战指南
22大模型之Spring AI实战系列(二十二):Spring AI + MCP + SQLite 数据库实战指南
23大模型之Spring AI实战系列(二十三):Spring AI + MCP + 自定义MCP服务开发实战


前言

在上一篇文章中,我们学习了如何使用 Spring AI 集成 Model Context Protocol (MCP),并通过 SQLite 数据库实现自然语言数据库交互。

本文将继续深入实践,介绍如何开发自己的MCP服务,将 天气服务 封装为 LLM 可调用的工具,从而实现:

  • 使用自然语言查询指定地区的天气预报
  • 获取特定州的天气警报信息
  • 构建图文并茂的天气报告
  • 支持 SSE 和 STDIO 两种传输方式

我们将基于 spring-ai-mcp-weather-server 示例项目,演示完整的部署流程、核心代码解析以及实际应用场景,并提供可复用的服务端开发经验。


一、开发环境准备

(一)Java 版本要求

本项目采用Java 17进行编译和运行,请务必确保你的开发环境已成功安装JDK 17。你可以在命令行中输入以下命令进行检查:

java -version

输出应类似如下内容:

openjdk version "17.0.8" 2023-07-18
OpenJDK Runtime Environment (build 17.0.8+7)
OpenJDK 64-Bit Server VM (build 17.0.8+7, mixed mode, sharing)

(二)Maven 构建工具

确保你已安装 Maven 并配置好环境变量:

mvn -v

输出应类似如下内容:

Apache Maven 3.8.8 (4c87b05d9aedce574290d1acc98575ed5eb6cd39)
Maven home: D:\Program Files (x86)\apache-maven-3.8.8
Java version: 17.0.12, vendor: Oracle Corporation, runtime: D:\Program Files\jdk-17.0.12
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

(三)OpenAI API 密钥

你需要注册 OpenAI 官网 获取 API Key(国内可以直接使用中转地址),并设置为环境变量:

export OPENAI_API_KEY="your_openai_api_key"
export OPENAI_API_URL="https://api.openai.com/v1"

注意:出于安全考虑,不建议将密钥硬编码在代码中,推荐使用环境变量或配置中心进行管理。

(四)Node.js 和 npx 命令

MCP 客户端默认通过 npx 启动 Brave Search 服务,因此需要安装 Node.js 并配置 npmnpx
访问:https://nodejs.org 下载Node.js ,安装node 时注意勾选npm ;配置环境变量。
npx 是 npm 自带的工具,用于快速执行包中的命令。安装完成后,npx 也会自动可用。
检查版本信息:

node -v
npm -v
npx -v

配置 npm 全局模块路径(可选)
打开安装目录,新建两个文件夹:node_global 和 node_cache。
以管理员身份打开命令提示符,输入以下命令(将路径替换为你创建的文件夹路径):
npm config set prefix “你的路径\node_global”
npm config set cache “你的路径\node_cache”


(五)安装uvx 命令

MCP 客户端默认通过 uvx 启动 SQLite 服务,因此需要安装 Python 的 uv 包管理器:

pip install uv

验证是否安装成功:

uv --version

二、Maven 依赖配置

(一)MCP服务端依赖配置

你需要添加如下依赖以启用 MCP 服务端功能:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency>

该依赖提供了:

  • 基于 HTTP 的传输(SSE)
  • 自动配置的 MCP 端点
  • 支持 STDIO 模式
  • 包含 @Tool 注解支持与自动注册机制

(二)SpringAI相关依赖包版本管理

版本管理

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
</dependencyManagement>

部分通过引入spring-ai-bom,实现了对SpringAI相关依赖包版本的统一管理。这样,在项目中使用Spring AI相关依赖时,无需在每个依赖声明中单独指定版本号,只需遵循spring-ai-bom中定义的版本即可,极大地简化了依赖管理工作,同时确保了项目中依赖版本的一致性和稳定性。

三、Properties配置文件

server.port=8080
# spring.main.web-application-type=none

# 请注意:为了使STDIO传输正常工作,必须禁用横幅和控制台日志记录!
spring.main.banner-mode=off
# logging.pattern.console=
# spring.ai.mcp.server.stdio=false

# MCP服务名称、版本配置
spring.ai.mcp.server.name=my-mcp-weather-server
spring.ai.mcp.server.version=0.0.1

logging.file.name=./spring-ai-mcp-cookbook/spring-ai-mcp-weather-server/target/spring-ai-mcp-weather-server.log

四、MCP服务端代码解析:

(一)启动类:[Application.java]

使用 MethodToolCallbackProvider 构建器将 WeatherService 注册为一个工具对象,这样它就可以通过 Spring AI 的工具调用机制被调用。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ToolCallbackProvider weatherTools(WeatherService weatherService){
      return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
    }
}

(二)服务工具类封装[WeatherService.java]

该类是核心业务逻辑所在,使用 @Tool 注解将方法注册为 LLM 可调用函数。

@Service
public class WeatherService {

    private final RestClient restClient;

    public WeatherService() {
        this.restClient = RestClient.builder()
                .baseUrl("https://api.weather.gov")
                .defaultHeader("Accept", "application/geo+json")
                .defaultHeader("User-Agent", "WeatherApiClient/1.0 (your@email.com)")
                .build();
    }

    @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);

        return formatForecast(forecast);
    }

    @Tool(description = "Get weather alerts for a US state. Input is Two-letter US state code (e.g., CA, NY)")
    public String getAlerts(String state) {
        Alert alert = restClient.get().uri("/alerts/active/area/{state}", state).retrieve().body(Alert.class);
        return formatAlert(alert);
    }

}

✅ 核心能力说明:

方法描述
getWeatherForecastByLocation()根据经纬度获取天气预报
getAlerts()获取某个地方的天气警报信息

✅ 技术要点:

  • 使用 RestClient 发起 HTTP 请求;
  • JSON 解析使用 Jackson 的 @JsonProperty
  • 工具方法使用 @Tool 注解注册;
  • 结果格式化返回自然语言描述。

五、工具注册与 MCP 服务启动

(一)工具注册机制

通过 MethodToolCallbackProviderWeatherService 注册为可用工具:

@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
    return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}

Spring AI 会自动扫描所有带有 @Tool 注解的方法,并将其注册为 MCP 工具。


(二)启动 MCP 服务

你可以采用以下方式或者直接在idea启动运行,启动一个MCP服务:

java -jar target/spring-ai-mcp-weather-server-0.0.1-SNAPSHOT.jar

服务将在 http://localhost:8080 启动,支持 SSE 推送。

如果需要调整配置参数可参考:

java -Dspring.ai.mcp.server.stdio=true \
     -Dspring.main.web-application-type=none \
     -jar target/spring-ai-mcp-weather-server-0.0.1-SNAPSHOT.jar

六、MCP客户端:[spring-ai-mcp-weather-client]

(一)核心代码解析

@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	/**
	 * 注入用户输入的提示内容。
	 * 通过 application.yml 配置文件中的 ai.user.input 属性设置。
	 */
	@Value("${ai.user.input}")
	private String userInput;

	/**
	 * 定义一个 CommandLineRunner Bean,用于在应用启动后执行预定义的提问逻辑。
	 *
	 * @param chatClientBuilder ChatClient 构建器,用于创建聊天客户端实例
	 * @param tools 工具回调提供者,用于注册和调用工具函数
	 * @param context Spring 应用上下文,用于管理 Bean 生命周期
	 * @return CommandLineRunner 返回一个可执行的命令行任务
	 */
	@Bean
	public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools,
												 ConfigurableApplicationContext context) {

		return args -> {

			// 创建 ChatClient 实例,设置默认的工具回调
			var chatClient = chatClientBuilder
					.defaultToolCallbacks(tools)
					.build();

			// 输出当前的问题
			System.out.println("\n>>> QUESTION: " + userInput);

			// 发送问题给 AI 模型并获取回答
			System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content());

			// 关闭 Spring 应用上下文
			context.close();
		};
	}
}

(二)客户端配置

# 设置应用程序的启动端口
server.port=8001

# 设置 Spring 应用程序的名称
spring.application.name=mcp

# 指定应用程序不启动 Web 容器(非 Web 应用)
spring.main.web-application-type=none

# 配置 OpenAI 的 API 密钥和基础 URL
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.base-url=${OPENAI_API_URL}

# MCP 客户端传输模式配置:
# 使用 stdio 传输模式(默认禁用,需要时取消注释)
# spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json

# 使用 SSE (Server-Sent Events) 传输模式(启用并连接本地 MCP 天气服务)
spring.ai.mcp.client.sse.connections.my-mcp-weather-server.url=http://localhost:8080

# 日志级别设置为 WARN,减少不必要的调试信息输出
logging.level.io.modelcontextprotocol.client=WARN
logging.level.io.modelcontextprotocol.spec=WARN

# 用户输入的提示内容(用于 CommandLineRunner 示例)
ai.user.input=What tools are available?

# 启用 MCP 工具回调功能(确保客户端能正确注册工具)
spring.ai.mcp.client.toolcallback.enabled=true

(三)调用MCP服务(STDIO传输)

1. 创建 [mcp-servers-config.json]

{
  "mcpServers": {
    "my-mcp-weather-server": {
      "command": "java",
      "args": [
        "-Dspring.ai.mcp.server.stdio=true",
        "-Dspring.main.web-application-type=none",
        "-Dlogging.pattern.console=",
        "-jar",
        "/absolute/path/to/spring-ai-mcp-weather-server-0.0.1-SNAPSHOT.jar"
      ]
    }
  }
}

2. 使用 STDIO 传输启动客户端

在spring-ai-mcp-weather-client的properties中,使用 STDIO 传输启动客户端:

# MCP 客户端传输模式配置:
# 使用 stdio 传输模式(默认禁用,需要时取消注释)
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json

# 使用 SSE (Server-Sent Events) 传输模式(启用并连接本地 MCP 天气服务)
# spring.ai.mcp.client.sse.connections.my-mcp-weather-server.url=http://localhost:8080

3. 启动客户端程序:spring-ai-mcp-weather-client

使用命令:java -jar spring-ai-mcp-weather-client-0.0.1-SNAPSHOT.jar
启动客户端后,将看到以下输出:

(四)调用MCP服务(SSE传输)

1. 启动 spring-ai-mcp-weather-server

java -jar spring-ai-mcp-weather-server-0.0.1-SNAPSHOT.jar

该命令将在 8080 端口启动 MCP 服务器。

2. 使用 SSE 传输启动客户端

在spring-ai-mcp-weather-client的properties中,使用 SSE 传输启动客户端:

# MCP 客户端传输模式配置:
# 使用 stdio 传输模式(默认禁用,需要时取消注释)
# spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json

# 使用 SSE (Server-Sent Events) 传输模式(启用并连接本地 MCP 天气服务)
spring.ai.mcp.client.sse.connections.my-mcp-weather-server.url=http://localhost:8080

3. 启动客户端程序:spring-ai-mcp-weather-client

使用命令:java -jar spring-ai-mcp-weather-client-0.0.1-SNAPSHOT.jar

启动客户端后,将看到以下输出:

(五)服务提问测试

调整问题参数,启动客户端:

java -Dai.user.input='纽约的天气怎么样?' -jar spring-ai-mcp-weather-client-0.0.1-SNAPSHOT.jar

控制台输出:

>>> QUESTION: '纽约的天气怎么样?'

>>> ASSISTANT: 纽约的天气预报如下:

**今晚:**
- 温度:62°F (约16°C)
- 风速:7 mph (约11 km/h) 东风
- 预报:有降雨的可能,天气多云,最低温度约62°F。降水概率为50%。

**星期一:**
- 温度:68°F (约20°C)
- 风速:8 mph (约13 km/h) 东风
- 预报:有轻微降雨的可能,天气多云,最高温度约68°F,下午气温会降至约66°F。降水概率为20%。

**星期一晚上:**
- 温度:64°F (约18°C)
- 风速:5到8 mph (约8到13 km/h) 东风
- 预报:有轻微降雨的可能,天气多云,最低温度约64°F。降水概率为20%。

**星期二:**
- 温度:69°F (约21°C)
- 风速:6到9 mph (约10到14 km/h) 东风
- 预报:早上8点前有轻微降雨的可能,然后可能有小雨,天气多云,最高温度约69°F。降水概率为20%。

**星期二晚上:**
- 温度:67°F (约19°C)
- 风速:6到9 mph (约10到14 km/h) 东南风
- 预报:可能有降雨,天气多云,最低温度约67°F。降水概率为60%。可能的新降水量在四分之一到半英寸之间。

**星期三:**
- 温度:81°F (约27°C)
- 风速:5到8 mph (约8到13 km/h) 南风
- 预报:上午11点前可能有降雨,然后可能有小雨和雷暴,天气部分晴朗,最高温度约81°F。降水概率为60%。可能的新降水量在十分之一到四分之一英寸之间。

**星期三晚上:**
- 温度:73°F (约23°C)
- 风速:8 mph (约13 km/h) 西南风
- 预报:晚上8点前可能有降雨和雷暴,天气部分多云,最低温度约73°F。降水概率为30%。

**星期四:**
- 温度:87°F (约31°C)
- 风速:7到15 mph (约11到24 km/h) 西南风
- 预报:下午2点后可能有降雨和雷暴,天气大部分晴朗,最高温度约87°F。降水概率为40%。

**星期四晚上:**
- 温度:72°F (约22°C)
- 风速:12到15 mph (约19到24 km/h) 西风
- 预报:凌晨2点前可能有降雨和雷暴,天气部分多云,最低温度约72°F。降水概率为40%。

**星期五:**
- 温度:80°F (约27°C)
- 风速:13 mph (约21 km/h) 西风
- 预报:天气晴朗,最高温度约80°F。

**星期五晚上:**
- 温度:67°F (约19°C)
- 风速:7到12 mph (约11到19 km/h) 西北风
- 预报:天气部分多云,最低温度约67°F。

**星期六:**
- 温度:81°F (约27°C)
- 风速:7到12 mph (约11到19 km/h) 西风
- 预报:天气晴朗,最高温度约81°F。

**星期六晚上:**
- 温度:72°F (约22°C)
- 风速:8到12 mph (约13到19 km/h) 西南风
- 预报:天气部分多云,最低温度约72°F。

**星期日:**
- 温度:84°F (约29°C)
- 风速:8到12 mph (约13到19 km/h) 西南风
- 预报:天气大部分晴朗,最高温度约84°F。

如果你需要更多信息,请告诉我!

通过控制台输出,我可以看到服务MCP服务调用成功,成功的返回了天气服务查询到的天气信息

七、结语

通过本文的实践,我们完成了 Spring AI 与 MCP 协议的集成,掌握了以下关键技能:

✅ 如何在 Spring Boot 中集成 MCP 服务端
✅ 如何将 RESTful 天气接口封装为 LLM 可调用工具
✅ 如何构建自然语言问答流程
✅ 如何调用 MCP 服务工具获取实时天气数据
✅ 完整部署与测试流程

这是 Spring AI 集成 MCP 协议的重要一步,后续我们将继续深入,介绍如何构建企业级 MCP 工具平台、支持多源搜索、动态工具注册等内容。


若您对本文介绍的技术内容感兴趣,希望进一步探索和实践,欢迎关注我,通过私信的方式与我联系,获取完整的项目代码,开启您的 Spring AI 开发之旅。


在这里插入图片描述

🎯🔖更多专栏系列文章:AI大模型提示工程完全指南AI大模型探索之路(零基础入门)AI大模型预训练微调进阶AI大模型开源精选实践AI大模型RAG应用探索实践🔥🔥🔥 其他专栏可以查看博客主页📑

😎 作者介绍:资深程序老猿,从业10年+、互联网系统架构师,目前专注于AIGC的探索(CSDN博客之星|AIGC领域优质创作者)
📖专属社群:欢迎关注【小兵的AI视界】公众号或扫描下方👇二维码,回复‘入群’ 即刻上车,获取邀请链接。
💘领取三大专属福利:1️⃣免费赠送AI+编程📚500本,2️⃣AI技术教程副业资料1套,3️⃣DeepSeek资料教程1套🔥(限前500人)
如果文章内容对您有所触动,别忘了点赞、⭐关注,收藏!加入我们,一起携手同行AI的探索之旅,开启智能时代的大门!

### Spring AIMCP 的集成概述 Spring AIMCP (Management Control Program 或其他可能含义) 可能涉及两种不同技术栈之间的协作。以下是关于如何实现两者之间潜在集成的一些分析: #### 技术背景 - **Spring AI**: 这是一个由 Spring 社区推出的框架,用于支持 Java 开发人员构建人工智能应用程序[^2]。它通过提供一系列工具和库来简化机器学习模型的训练、部署以及与其他系统的交互。 - **MCP Hosts**: 根据描述,MCP 主机可以指代运行特定程序(如 Claude Desktop、IDE 工具或其他 AI 工具)并允许这些程序访问数据的服务或平台[^1]。 #### 集成可能性 为了使 Spring AI 能够与 MCP 成功集成,通常需要考虑以下几个方面: ##### 数据交换模式 一种常见的做法是利用消息传递模式来进行通信。例如,在企业级架构设计中提到的消息队列模式可以帮助解决异构系统间的数据传输问题[^3]。具体来说,可以通过以下方式实现: - 使用 AMQP 协议或者 JMS 接口作为中间件连接 Spring AI 应用和服务端的 MCP 实体; - 设计标准化的消息格式以便于双方解析处理; ##### API 层面的支持 如果目标环境提供了 RESTful Web Service,则可以直接调用其公开接口完成所需操作。对于这种情况下的开发流程如下所示: ```java // 创建 RestTemplate 对象实例化 HTTP 请求客户端 RestTemplate restTemplate = new RestTemplate(); // 构建 URL 地址指向远程服务资源位置 String url = "http://example.com/mcp/resource"; // 发起 GET 方法请求获取响应实体对象 ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); // 打印返回状态码及内容主体部分 System.out.println(response.getStatusCode()); System.out.println(response.getBody()); ``` ##### 插件扩展机制 当上述方案不适用时还可以探索插件形式加载额外功能模块的可能性。比如某些 IDE 平台就允许第三方开发者为其增添新特性从而增强整体用户体验效果。这种情况下就需要仔细阅读官方文档了解相关规范要求然后按照指引编写自定义组件提交审核上线发布供最终用户选用安装激活启用等功能选项卡界面布局调整等等细节都需要遵循既定规则执行才能确保兼容性和稳定性达到预期水平。 ---
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻道AI小兵

🐳 感谢你的巨浪支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值