一、前言
字节的扣子、腾讯的元器,当我们使用这些平台构建AI智能体的时候会有各种各样功能的插件供我们使用。
你是不是惊叹字节的扣子、腾讯的元器这些AI平台的海量插件生态?其实这些插件都是使用MCP协议构建的MCP Server服务。那么我们是不是可以构建我们自己的插件?搭建我们自己的MCP Server。
今天我们就来介绍一下,定义一个自己的MCP Server工具,并与DeepSeek结合为我们播报指定地区的天气预报。
二、MCP是什么?
由 Anthropic 提出并开源的通信协议,于 2024 年 11 月正式发布,旨在解决大型语言模型(LLM)与外部数据源及工具之间的无缝集成问题。该协议通过标准化 AI 系统与数据源的交互方式,帮助模型获取更丰富的上下文信息,从而生成更准确、更相关的响应。

三、为什么要有MCP?
当大型语言模型首次出现时,用户必须将代码复制并粘贴到文本界面中才能与它们进行交互。这很快被证明是不够的,导致自定义集成以更好地加载上下文。然而,这些是零散的,需要单独开发。模型上下文协议 (MCP) 通过提供通用协议来与 AI 与本地和远程资源进行高效交互,从而解决了这一问题。
四、MCP主要功能点?
1.标准化通信接口为 AI 模型和外部资源(如数据库、API、代码编辑器)提供统一的交互方式,简化集成流程。
2.动态上下文感知MCP支持AI模型在多轮交互中动态维护上下文,确保对话或任务的连贯性。 例如,用户与AI讨论方案后,切换模型仍可继续基于已有上下文协作。
3.动态资源访问模型可通过 MCP 实时调用外部工具(如网页抓取、文件操作)或获取最新数据(如股票价格、天气信息)。
4.安全与隐私保护MCP内置多层安全机制,包括本地服务器处理敏感数据、API密钥隔离存储、细粒度权限控制等。
五、搭建MCP Server
整体环境
SpringBoot 3.2.5
JDK 17
spring-ai-mcp-server-webmvc-spring-boot-starter 1.0.0-M6
Spring-ai-mcp 是 Spring AI 生态中用于快速集成 MCP(Model Context Protocol)服务端功能的 Spring Boot Starter。
其核心作用是为基于 Spring MVC 的传统 Web 应用提供开箱即用的 MCP 服务端能力,简化 AI 模型与外部工具/数据源的集成流程。
添加依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>
spring-ai-mcp-server-webmvc-spring-boot-starter
</artifactId>
<version>1.0.0-M6</version>
</dependency>
编写代码
// 免费天气API基础URL
private static final String BASE_URL = "https://api.open-meteo.com/v1";
private final RestClient restClient;
public WeatherService() {
this.restClient = RestClient.builder()
.baseUrl(BASE_URL)
.defaultHeader("Accept", "application/json")
.defaultHeader("User-Agent", "OpenMeteoClient/1.0")
.build();
}
/**
* 获取指定经纬度的天气预报
*
* @param latitude 纬度
* @param longitude 经度
* @return 指定位置的天气预报
* @throws RestClientException 如果请求失败
*/
@Tool(description = "获取特定经纬度的天气预报")
public String getWeatherConditionByLocation(@ToolParam(description = "纬度") double latitude, @ToolParam(description = "经度") double longitude) {
System.out.println("开始获取指定地点的天气预报");
WeatherInfo weatherInfoData = restClient.get()
.uri("/forecast?latitude={latitude}&longitude={longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,precipitation,weather_code,wind_speed_10m,wind_direction_10m&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,weather_code,wind_speed_10m_max,wind_direction_10m_dominant&timezone=auto&forecast_days=7",
latitude, longitude)
.retrieve()
.body(WeatherInfo.class);
CurrentWeather current = weatherInfoData.getCurrent();
String temperatureUnit = weatherInfoData.getCurrentUnit() != null ? weatherInfoData.getCurrentUnit().getTemperatureUnit() : "°C";
String windSpeedUnit = weatherInfoData.getCurrentUnit() != null ? weatherInfoData.getCurrentUnit().getWindSpeedUnit() : "km/h";
String humidityUnit = weatherInfoData.getCurrentUnit() != null ? weatherInfoData.getCurrentUnit().getHumidityUnit() : "%";
StringBuilder weatherStr = new StringBuilder();
weatherStr.append(String.format("""
当前天气:
温度: %.1f%s (体感温度: %.1f%s)
天气: %s
风向: %s (%.1f %s)
湿度: %d%s
降水量: %.1f 毫米
""",
current.getTemperature(),
temperatureUnit,
current.getFeelsLike(),
temperatureUnit,
getWeatherDescription(current.getWeatherCode()),
getWindDirection(current.getWindDirection()),
current.getWindSpeed(),
windSpeedUnit,
current.getHumidity(),
humidityUnit,
current.getPrecipitation()));
// 添加未来天气预报
weatherStr.append("未来天气预报:\n");
FutureWeatherForecast daily = weatherInfoData.getDaily();
for (int i = 0; i < daily.getTime().size(); i++) {
String date = daily.getTime().get(i);
double tempMin = daily.getTempMin().get(i);
double tempMax = daily.getTempMax().get(i);
int weatherCode = daily.getWeatherCode().get(i);
double windSpeed = daily.getWindSpeedMax().get(i);
int windDir = daily.getWindDirection().get(i);
double precip = daily.getPrecipitationSum().get(i);
// 格式化日期
LocalDate localDate = LocalDate.parse(date);
String formattedDate = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd (EEE)"));
weatherStr.append(String.format("""
%s:
温度: %.1f%s ~ %.1f%s
天气: %s
风向: %s (%.1f %s)
降水量: %.1f 毫米
""",
formattedDate,
tempMin, temperatureUnit,
tempMax, temperatureUnit,
getWeatherDescription(weatherCode),
getWindDirection(windDir),
windSpeed, windSpeedUnit,
precip));
}
return weatherStr.toString();
}
这是获取天气预报的功能代码,需要完整的代码,可以在下面的公众号上给我留言。
添加启动类,并将MCP Server中的方法暴露出去
@SpringBootApplication
public class WeatherApplication {
public static void main(String[] args) {
SpringApplication.run(WeatherApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}
增加配置
server:
port: 8098
spring:
ai:
mcp:
server:
name: weather-server
version: 0.0.1
六、调用MCP Server
使用 VSCode + Cline 调用我们自定义的MCP服务。
《VSCode安装Cline配置deepseek调用MCP插件》
1.stdio模式(本地 Server)
将刚才MCP Server项目打成jar,对应的Cline中的配置如下
"xxxx名称": {
"disabled": false,
"timeout": 60,
"command": "java",
"args": ["-jar", "刚才MCP服务的jar位置"],
"transportType": "stdio"
}
2.SSE模式(连接远程服务器)
这种方式,我们首先启动一下我们的项目
在Cline中添加MCP Server,配置如下点击 Add Server

添加之后,就可以看到我们刚才写的天气预报的方法

输入我们的问题
北京天气预报: latitude: 39.9042 longitude: 116.4074
这里可以看到,将用到我们自定义的MCP Server

MCP Server的输出结果

前面配置过DeepSeek,所以大模型会调整输出样式

通过MCP协议的系统性实践,我们不仅掌握了自建MCP Server的核心技术架构,更成功打通了大模型与定制化服务间的认知协同通道。
2381

被折叠的 条评论
为什么被折叠?



