Spring AI MCP 案例-WebFlux SSE传输模式 (九)

在该案例中,我们会创建MCP Server 和MCP Client 两个SpringBoot项目,MCP Server项目中会创建一个getWeather工具,该工具通过OpenWeather可以查询某个城市天气情况;MCP Client 项目中创建相应的Controller,根据配置通过WebFlux SSE方式与MCP Server进行通信,实现调用天气工具。

Mcp Server开发

按照如下步骤创建MCP Server对应的SpringBoot项目。**WebFlux SSE传输模式中创建的Mcp Server相比于STDIO传输模式中创建的MCPServer 只是在pom.xml中引入的依赖不同而已。**

  1. 创建SpringBoot项目

SpringBoot项目命名为SpringAIMCPStdioServer,设置使用的JDK为17版本。

  1. 在项目中加入如下Maven依赖
html 复制代码
<?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.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>SpringAIMCPSseServer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringAIMCPSseServer</name>
    <description>SpringAIMCPSseServer</description>
    <properties>
        <java.version>17</java.version>
    </properties>

    <!-- 导入 Spring AI BOM,用于统一管理 Spring AI 依赖的版本,
    引用每个 Spring AI 模块时不用再写 <version>,只要依赖什么模块 Mavens 自动使用 BOM 推荐的版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-web</artifactId>-->
<!--        </dependency>-->

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

        <!-- 依赖的json 包-->
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20210307</version>
        </dependency>
    </dependencies>

    <!-- 打包插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.4.4</version>
                <configuration>
                    <mainClass>com.example.springaimcpstdioserver.SpringAimcpStdioServerApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <!-- 声明仓库, 用于获取 Spring AI 以及相关预发布版本-->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <name>Central Portal Snapshots</name>
            <id>central-portal-snapshots</id>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

注意:

在该pom.xml中引入了"spring-ai-starter-mcp-server-webflux"MCP 依赖包,该包只支持SSE传输;

一个SpringBoot项目MVC 和 WebFlux 通常二选其一使用,不要引入"spring-boot-starter-web"依赖包,该包会启用 Spring MVC + 嵌入式 Tomcat,相当于是一套HTTP服务器组件,与Spring WebFlux + Reactor Netty 这套HTTP 服务组件冲突了,导致后续客户端HTTP请求报错。

java 复制代码
package com.example.springaimcpsseserver.service;


import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

/**
 * 天气服务类,用于获取指定城市的天气信息
 * @Service 标记为 Spring 服务层组件
 */

@Service
public class WeatherService {
    private static final Logger logger = LoggerFactory.getLogger(WeatherService.class);

    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;

    /**
     * 根据城市名称获取天气信息(使用 OpenWeatherMap)
     * @param city 城市名称,如 "Beijing"
     * @return 天气信息文本
     */
    @Tool(description = "获取指定城市的当前天气情况,格式化后的天气报告字符串。")
    public String getWeather(@ToolParam(description = "城市名称,必须是英文格式,比如 London 或 Beijing") String city) {

        logger.info("====== 调用了getWeather工具 ======");

        try {
            String charset = "UTF-8";

            String query = String.format(
                    "q=%s&appid=%s&units=metric&lang=zh_cn",
                    URLEncoder.encode(city, charset),
                    URLEncoder.encode(OPEN_WEATHER_API_KEY, charset)
            );

            URL url = new URL(BASE_URL + "?" + query);
            logger.info("====== 访问URL: ======"+url.toString());

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), charset));

            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();

            JSONObject data = new JSONObject(response.toString());

            if (data.getInt("cod") == 404) {
                return "未找到该城市的天气信息。";
            }

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

        } catch (Exception e) {
            return "获取天气信息时出错: " + e.getMessage();
        }
    }

    public static void main(String[] args) {
        //测试方法
        WeatherService client = new WeatherService();
        String beijing = client.getWeather("Beijing");
        System.out.println(beijing);
    }
}

Mcp Client 开发

按照如下步骤创建MCP Client的SpringBoot项目。该MCP Client 项目中可以使用不同的LLM模型,只需要在对应配置文件中引入不同的模型对应的apikey及相关依赖即可。

WebFlux SSE传输模式中创建的Mcp Client相比于STDIO传输模式中创建的MCP Client 有两点不同: 在pom.xml中引入的依赖不同 、在application.properties中配置远程Server 。

  1. 创建SpringBoot项目

SpringBoot项目命名为SpringAIMCPSseClient,设置使用的JDK为17版本。

2) 在项目中加入如下Maven依赖

java 复制代码
<?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.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>SpringAIMCPSseClient</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringAIMCPSseClient</name>
    <description>SpringAIMCPSseClient</description>


    <properties>
        <java.version>17</java.version>
    </properties>

    <!-- 导入 Spring AI BOM,用于统一管理 Spring AI 依赖的版本,
    引用每个 Spring AI 模块时不用再写 <version>,只要依赖什么模块 Mavens 自动使用 BOM 推荐的版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 基于 WebFlux 的 SSE 传输实现的依赖 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
        </dependency>

        <!-- DeepSeek 模型依赖 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-deepseek</artifactId>
        </dependency>
    </dependencies>


    <!-- 声明仓库, 用于获取 Spring AI 以及相关预发布版本-->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <name>Central Portal Snapshots</name>
            <id>central-portal-snapshots</id>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>
相关推荐
rabbit_pro1 小时前
SpringBoot3集成Langchain4j使用Ollama
java·开发语言
酿情师1 小时前
小龙虾(OpenClaw)本地部署详细教学:从 0 到跑通 AI 本地助手
人工智能
极品小學生2 小时前
从零到一:打造属于自己的高可用 AI 接口中转站
人工智能
解决问题no解决代码问题2 小时前
JAVA GC
java·开发语言·jvm
HIT_Weston2 小时前
83、【Agent】【OpenCode】bash 工具提示词(commit 注意事项)(一)
人工智能·agent·opencode
波动几何2 小时前
Capability Pipeline OS - 通用能力管线操作系统
人工智能
F2的AI学习笔记2 小时前
下一代键盘,可能戴在手腕上
人工智能
wyg_0311132 小时前
codex features
人工智能