1、概述
MCP的出现为AI的落地发展摁下了加速键,我们通过MCP可以解耦工具和大模型之间的强耦合关系。使一个工具服务可以供不同厂商的大模型调用。加速了工具服务组件化发展。
具体介绍可以看 模型上下文协议(MCP):AI 代理与真实世界交互的桥梁
相关文章:
这篇文章介绍MCP-server的开发及相关调试及一些注意点。文章最后附有全部代码
2、MCP server开发
MCP server 开发有两种模式,一种是stdio模式,另一种是sse模式。stdio相当于是依赖包加载到本地进行运行,
2.1、依赖相关
开发mcp对环境有要求,jdk要求17+,SpringBoot要求【3.4.0+】
MCP-server开发有两个依赖包,底层的技术一个是依赖webmvc,另一个是依赖webflux。webmvc是基于servlet的阻塞式模型,webflux是一个异步非阻塞式的 Web 框架,它能够充分利用多核 CPU 的硬件资源去处理大量的并发请求。
具体两个依赖如下(目前最新版本【1.0.0-M7】):
<!-- webmvc-mcp -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.0.0-M7</version>
</dependency>
<!-- webflux-mcp -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
<version>1.0.0-M7</version>
</dependency>
我们选其中之一【webmvc】进行演示讲解。
2.2、代码开发
大家可以看下目录结构,结构比较简单。主要核心是【OpenMeteoService】和【MCPSpringAiRegisterConfiguration】
服务开发
我们使用open-meteo服务,【open-meteo】是一个开源免费的天气api服务。
这里我们开发两个MCP工具
根据经纬度查询天气
根据经纬度查询空气质量
配置开发
初始化restTemplate
Tools服务注册
application.yml配置
目前使用的都是默认配置,只添加了一个name和version,具体配置可参考官网
目前【sse-endpoint】配置不起作用,原因是依赖初始化时使用默认值,因此如果要使用自定义,需要重写初始化。
解决方案:
创建配置,重写WebMvcSseServerTransportProvider初始化
3、接口验证
我们使用开源组件inspector进行验证mcp服务
运行inspector需要安装node、npm、npx环境
执行【npx @modelcontextprotocol/inspector】命令便可安装运行 启动服务地址为【http://127.0.0.1:6274】
具体调试信息如下:
添加【sse-endpoint】配置后如下:
4、总结
目前只是运行了一个简单的demo,离服务化还是较远。但提出了工具组件化的概念。
对于MCP,还有好多需要完善,比如鉴权、相关依赖完善、各个服务提供商的数据是否完备等。但是这一技术理念还是值得我们现在去理解去学习。
5、代码
代码结构如下:
BeanConfiguration
package com.young.server.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author 刘子洋
* @date 2025年05月13日 下午3:22
* @description:初始化组件bean
*/
@Configuration
public class BeanConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
MCPSpringAiRegisterConfiguration
package com.young.server.config;
import com.young.server.mcp.springAi.OpenMeteoService;
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;
/**
* @author 刘子洋
* @date 2025年05月13日 下午3:18
* @description:MCP 服务注册
*/
@Configuration
public class MCPSpringAiRegisterConfiguration {
/**
* 创建并配置一个天气工具回调提供者
* 该方法通过接收一个OpenMeteoService实例来构建一个MethodToolCallbackProvider对象
* 主要用于在调用OpenMeteoService的方法时提供回调功能
*
* @param openMete OpenMeteoService的实例,用于获取天气信息
* @return 返回一个配置好的MethodToolCallbackProvider对象
*/
@Bean
public ToolCallbackProvider weatherTools(OpenMeteoService openMete){
return MethodToolCallbackProvider.builder()
.toolObjects(openMete)
.build();
}
}
MyMcpServerConfig
package com.young.server.config;//package com.young.server.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import org.springframework.ai.mcp.server.autoconfigure.McpServerProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
/**
* @author 刘子洋
* @date 2025年05月14日 上午10:53
* @description:重写 WebMvcSseServerTransportProvider 初始化
*/
@Configuration
public class MyMcpServerConfig {
@Bean
@Primary
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider(
ObjectProvider<ObjectMapper> objectMapperProvider, McpServerProperties serverProperties) {
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
return new WebMvcSseServerTransportProvider(objectMapper, serverProperties.getSseMessageEndpoint(),
serverProperties.getSseEndpoint());
}
@Bean
public RouterFunction<ServerResponse> mvcMcpRouterFunction(WebMvcSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
}
OpenMeteoService
package com.young.server.mcp.springAi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @author 刘子洋
* @date 2025年05月13日 下午2:53
* @description:空气服务类
*/
@Service
@Slf4j
public class OpenMeteoService {
private final RestTemplate restTemplate;
private static final String WEATHER_TEMPLATE = "当前位置(纬度:%s,经度:%s)的天气信息:\n %s";
public OpenMeteoService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* 根据给定的经纬度获取天气预报
* 此方法使用RestTemplate调用外部天气API,获取JSON格式的天气信息,并将其格式化为字符串返回
*
* @param latitude 纬度,表示地理位置的南北位置
* @param longitude 经度,表示地理位置的东西位置
* @return 格式化后的天气预报信息字符串
*/
@Tool(description = "根据给定的经纬度获取天气预报")
public String getWeatherForecastByLocation(
@ToolParam(description = "经纬度,例如:39.9042") String latitude,
@ToolParam(description = "经纬度,例如:116.4074") String longitude
) {
String url = "https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&hourly=temperature_2m&timezone=auto";
String response = restTemplate.getForObject(url, String.class, latitude, longitude);
log.info("response: {}", response);
return String.format(WEATHER_TEMPLATE, latitude, longitude, response);
}
/**
* 根据给定的经纬度获取空气质量信息
*
* @param latitude 纬度,表示地理位置的南北位置
* @param longitude 经度,表示地理位置的东西位置
* @return 空气质量信息字符串
*/
@Tool(description = "根据经纬度获取空气质量信息")
public String getAirQualityByLocation(
@ToolParam(description = "经纬度,例如:39.9042") String latitude,
@ToolParam(description = "经纬度,例如:116.4074") String longitude
) {
// 模拟数据,实际应用中应调用真实API
return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的空气质量:\n" +
"- PM2.5: 15 μg/m³ (优)\n" +
"- PM10: 28 μg/m³ (良)\n" +
"- 空气质量指数(AQI): 42 (优)\n" +
"- 主要污染物: 无";
}
}
maven(有些组件没有在maven中心仓库,因此自定义加入了一些仓库)
<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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.young</groupId>
<artifactId>my-agi</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>mcp-server</artifactId>
<packaging>war</packaging>
<name>mcp-server Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.0.0-M7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.4.0</version>
</plugin>
</plugins>
</build>
<repositories>
<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>central-portal-snapshots</id>
<name>Central Portal Snapshots</name>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>