Springboot AI 创建MCP Server

使用springboot AI实现一个伪查询天气的MCP Server,协议是Stdio。

系统:win11

JDK:17

代码结构:

McpServerDemoApplication.java:

复制代码
package com.david.mcpserverdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class McpServerDemoApplication {

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

}

McpServerConfig.java:

复制代码
package com.david.mcpserverdemo;

import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MCP Server 配置类
 */
@Configuration
public class McpServerConfig {

    /**
     * 注册 WeatherService 中的工具方法
     */
    @Bean
    public ToolCallbackProvider weatherToolProvider(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

WeatherService.java:

复制代码
package com.david.mcpserverdemo;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 天气服务类,提供模拟的城市天气查询功能
 */
@Service
public class WeatherService {

    private static final Map<String, WeatherInfo> WEATHER_CACHE = new ConcurrentHashMap<>();

    static {
        // 初始化一些城市的模拟天气数据
        WEATHER_CACHE.put("beijing", new WeatherInfo("北京", "晴", 25, 45, "西北风 2 级"));
        WEATHER_CACHE.put("shanghai", new WeatherInfo("上海", "多云", 22, 68, "东南风 3 级"));
        WEATHER_CACHE.put("guangzhou", new WeatherInfo("广州", "小雨", 28, 85, "南风 1 级"));
        WEATHER_CACHE.put("shenzhen", new WeatherInfo("深圳", "雷阵雨", 30, 90, "西南风 2 级"));
        WEATHER_CACHE.put("chengdu", new WeatherInfo("成都", "阴", 20, 55, "北风 1 级"));
        WEATHER_CACHE.put("hangzhou", new WeatherInfo("杭州", "晴", 24, 60, "东风 2 级"));
        WEATHER_CACHE.put("wuhan", new WeatherInfo("武汉", "多云", 26, 50, "南风 2 级"));
        WEATHER_CACHE.put("xian", new WeatherInfo("西安", "晴", 23, 40, "西北风 3 级"));
    }

    /**
     * 查询城市天气
     * @param city 城市名称(小写)
     * @return 天气信息
     */
    @Tool(description = "查询指定城市的天气情况,返回城市名称、天气状况、温度、湿度和风力信息")
    public String getWeather(@ToolParam(description = "城市名称,必须是小写英文,例如:beijing, shanghai") String city) {
        if (city == null || city.trim().isEmpty()) {
            return "错误:城市名称不能为空";
        }

        String cityKey = city.toLowerCase().trim();
        WeatherInfo weather = WEATHER_CACHE.get(cityKey);

        if (weather == null) {
            // 如果缓存中没有,生成一个模拟的随机天气
//            weather = generateRandomWeather(city);
            return "错误:未找到该城市的天气信息";
        }

        return String.format(
            "城市:%s\n天气:%s\n温度:%d°C\n湿度:%d%%\n风力:%s",
            weather.getCityName(),
            weather.getCondition(),
            weather.getTemperature(),
            weather.getHumidity(),
            weather.getWind()
        );
    }

    /**
     * 为未知城市生成模拟天气数据
     */
    private WeatherInfo generateRandomWeather(String city) {
        String[] conditions = {"晴", "多云", "阴", "小雨", "中雨", "大雨", "雷阵雨", "小雪", "大雪"};
        String[] windDirections = {"东", "南", "西", "北", "东南", "东北", "西南", "西北"};
        
        int temp = 15 + (int)(Math.random() * 20); // 15-35 度
        int humidity = 30 + (int)(Math.random() * 60); // 30-90%
        String condition = conditions[(int)(Math.random() * conditions.length)];
        String wind = windDirections[(int)(Math.random() * windDirections.length)] + "风 " + (1 + (int)(Math.random() * 5)) + "级";

        WeatherInfo weather = new WeatherInfo(
            city.substring(0, 1).toUpperCase() + city.substring(1),
            condition,
            temp,
            humidity,
            wind
        );
        
        // 缓存生成的天气数据
        WEATHER_CACHE.put(city.toLowerCase(), weather);
        
        return weather;
    }

    /**
     * 天气信息内部类
     */
    private static class WeatherInfo {
        private final String cityName;
        private final String condition;
        private final int temperature;
        private final int humidity;
        private final String wind;

        public WeatherInfo(String cityName, String condition, int temperature, int humidity, String wind) {
            this.cityName = cityName;
            this.condition = condition;
            this.temperature = temperature;
            this.humidity = humidity;
            this.wind = wind;
        }

        public String getCityName() {
            return cityName;
        }

        public String getCondition() {
            return condition;
        }

        public int getTemperature() {
            return temperature;
        }

        public int getHumidity() {
            return humidity;
        }

        public String getWind() {
            return wind;
        }
    }
}

application.properties:

复制代码
spring.application.name=mcp-server-demo

# MCP Server 配置
spring.ai.mcp.server.name=weather-mcp-server
spring.ai.mcp.server.type=SYNC

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 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>4.0.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.david</groupId>
    <artifactId>mcp-server-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mcp-server-demo</name>
    <description>mcp-server-demo</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.0.0</spring-ai.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc</artifactId>
        </dependency>

        <!-- Spring AI MCP Server -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1、编译打包出jar包

2、使用CherryStudio测试MCP服务

2.1、安装CherryStudio(https://www.cherry-ai.com/

2.2、配置大模型(本地Ollama或者DeepseekApiKey都可以)

2.3、配置MCP服务

左下角设置-MCP服务-MCP服务-新增一个

命令:java

参数:参数有2个,需要换行,如下代码块

复制代码
-jar
D:\\IdeaProjects\\mcp-server-demo\\target\\mcp-server-demo-0.0.1-SNAPSHOT.jar

点击保存后开启开关,如果失败了开关是无法开启的

2.4、聊天选择使用MCP服务

点下面的"小锤子",不同版本之间图标可能会不同,鼠标悬浮可以看到说明,然后选择手动。

这里一定要看到后面打勾了才算是生效了。

2.5、聊天促发MCP调用

tip:还有另一种测试方式,使用 MCP Inspector 需要有node.js环境

复制代码
npx @modelcontextprotocol/inspector

cmd运行,会自动打开一个网页(http://localhost:6274/

左边的参数都和CherryStudio一样

相关推荐
前端技术2 小时前
华为余承东:鸿蒙终端设备数突破5500万
java·前端·javascript·人工智能·python·华为·harmonyos
notfound40432 小时前
解决SpringCloudGateway用户请求超时导致日志未记录情况
java·spring boot·spring·gateway·springcloud
xiami_world2 小时前
国内外4大流程图工具深度横评(2026年):从架构、协作、AI能力看选型决策
人工智能·ai·信息可视化·流程图
Adellle2 小时前
Java 异步回调
java·开发语言·多线程
RestCloud2 小时前
2026年数据集成三大趋势:从批处理到实时事件驱动
ai·etl·cdc·数据传输·数据同步·数据集成平台·集成平台
SamDeepThinking2 小时前
如何理解 Spring 当中的 Bean?
java·后端·面试
老王谈企服2 小时前
2026金融数字化转型:金融数据不能出内网,Agent必须私有化部署,有什么信创适配的产品?
人工智能·ai·金融
敖正炀2 小时前
阻塞队列-0-3-最佳实践
java
nebula-AI2 小时前
llm wiki的固定提示词
人工智能·ai·个人开发·ai编程