SpringBoot搭建MCPServer

MCP Server

  • MCP 服务器是模型上下文协议 (MCP) 架构中的基础组件,可为客户提供工具、资源和功能。它实现协议的服务器端,负责:
    • 服务器端协议作实现
      • 工具曝光和发现
      • 使用基于 URI 的访问进行资源管理
      • 及时的模板配置和处理
      • 与客户的能力协商
      • 结构化日志记录和通知
    • 并发客户端连接管理
    • 同步和异步 API 支持
    • Transport 实现:
      • 基于 Stdio 的传输,用于基于进程的通信
      • 基于 Servlet 的 SSE 服务器传输
      • 用于反应式 HTTP 流的 WebFlux SSE 服务器传输
      • 用于基于 servlet 的 HTTP 流的 WebMVC SSE 服务器传输

导入pom依赖

  • 完整的 MCP 服务器功能支持基于 Spring WebFlux 的 SSE(服务器发送事件)服务器传输和可选的 STDIO 传输。
  • 启动器激活 McpWebFluxServerAutoConfiguration 和 McpServerAutoConfiguration 自动配置以提供:
    • 使用 Spring WebFlux 的反应式传输 WebFluxSseServerTransportProvider
    • 自动配置的反应式 SSE 终端节点
    • 可选的 STDIO 传输(通过设置spring.ai.mcp.server.stdio=true启用 )
    • 包括spring-boot-starter-webfluxmcp-spring-webflux依赖项
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.ai.bom.version>1.0.0-SNAPSHOT</spring.ai.bom.version>
</properties>

<!--版本管理-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring.ai.bom.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

</dependencies>

<!--添加spring的里程碑和快照仓库,下载Alibaba相关包-->
    <repositories>
        <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>

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

配置文件介绍

  • application.yml文件中可配置以下MCP服务器属性
  • 所有属性统一前缀为spring.ai.mcp.server
Property Description Default
enabled 启用/禁用 MCP 服务器 true
stdio 启用/禁用 stdio 传输 false
name 用于标识的服务器名称 mcp-server
version 服务器版本 1.0.0
type 服务器类型 (SYNC/ASYNC SYNC
resource-change-notification 启用资源更改通知 true
prompt-change-notification 启用提示更改通知 true
tool-change-notification 启用工具更改通知 true
tool-response-mime-type (可选)每个工具名称的响应 MIME 类型。例如 spring.ai.mcp.server.tool-response-mime-type.generateImage=image/png ,将 image/png mime 类型与 generateImage() 工具名称相关联
sse-message-endpoint 客户端用于发送消息的 Web 传输的自定义 SSE 消息终端节点路径 /mcp/message
sse-endpoint 用于 Web 传输的自定义 SSE 终端节点路径 /sse
base-url 可选 URL 前缀。例如,base-url=/api/v1 表示客户端应访问 /api/v1 + sse-endpoint 的 sse 终端节点,消息终端节点为 /api/v1 + sse-message-endpoint

配置文件示例

server:
  port: 8801

spring:
  ai:
    mcp:
      server:
        name: my-weather-server
        version: 1.0.0
        sse-endpoint: /sse

查询天气MCP Server

查询天气工具编写

@Service
@Slf4j
public class OpenMeteoWeatherService {

    private final WebClient weatherClient;
    private final WebClient airQualityClient;


    public OpenMeteoWeatherService(WebClient.Builder webClientBuilder) {
        this.weatherClient = webClientBuilder.baseUrl("https://api.open-meteo.com/v1").build();
        this.airQualityClient = webClientBuilder.baseUrl("https://air-quality-api.open-meteo.com/v1").build();
    }


    @Tool(description = "根据经纬度获取天气预报")
    public String getWeatherForecastByLocation(
            @ToolParam(description = "纬度,例如:39.9042") String latitude,
            @ToolParam(description = "经度,例如:116.4074") String longitude) {
        log.info("latitude:{}, longitude = {}", latitude, longitude);

        try {
            latitude = Optional.ofNullable(latitude).orElse("39.9042");
            longitude = Optional.ofNullable(longitude).orElse("116.4074");
            String finalLatitude = latitude;
            String finalLongitude = longitude;
            String response = weatherClient.get()
                    .uri(uriBuilder -> uriBuilder
                            .path("/forecast")
                            .queryParam("latitude", finalLatitude)
                            .queryParam("longitude", finalLongitude)
                            .queryParam("current", "temperature_2m,wind_speed_10m")
                            .queryParam("timezone", "auto")
                            .build())
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();

            // 解析响应并返回格式化的天气信息
            // 这里简化处理,实际应用中应该解析JSON
            return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的天气信息:\n" + response;
        } catch (Exception e) {
            return "获取天气信息失败:" + e.getMessage();
        }
    }


    @Tool(description = "根据经纬度获取空气质量信息")
    public String getAirQuality(
            @ToolParam(description = "纬度,例如:39.9042") String latitude,
            @ToolParam(description = "经度,例如:116.4074") String longitude) {

        try {
            String response = airQualityClient.get()
                    .uri(uriBuilder -> uriBuilder
                            .path("/air-quality")
                            .queryParam("latitude", latitude)
                            .queryParam("longitude", longitude)
                            .queryParam("hourly", "pm10,pm2_5")
                            .queryParam("timezone", "auto")
                            .build())
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();

            // 解析响应并返回格式化的天气信息
            // 这里简化处理,实际应用中应该解析JSON
            return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")空气质量信息:\n" + response;
        } catch (Exception e) {
            return "获取空气质量信息失败:" + e.getMessage();
        }

    }

}

注册工具

@Configuration
public class McpServerConfig {


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

//    @Bean
//    public WebClient.Builder webClientBuilder() {
//        return WebClient.builder();
//    }
}
posted @ 2025-04-18 09:19  dev-yze  阅读(1937)  评论(0)    收藏  举报