引言 :当AI遇见"MCP红娘" 💘
在人工智能这个"卷王"频出的圈子里,大语言模型(LLM)的能力边界正在疯狂扩张🚀。然而,由于数据孤岛和工具集成的复杂性,AI模型们常常像是被关在象牙塔里的学霸——知识渊博却无法实操。直到2024年底,Anthropic推出的模型上下文协议(MCP)如同一位专业的"红娘",为AI和外部服务牵线搭桥。本文将带你深入了解这位"红娘"MCP,并展示如何基于SpringAI框架实现MCP的"相亲大会"(客户端和服务端)💑。
一、MCP:AI世界的"万能翻译官" 🦸
MCP(Model Context Protocol)是一种标准化协议,专门负责统一大型语言模型与外部数据源和工具之间的"聊天方式"。它的核心价值就像是给AI配了一个"万能翻译官",让AI能够安全地访问和操作本地及远程数据,真正实现"连接万物"的超能力⚡。
由上图可以看出,在没有MCP协议时,AI需要像个"多语种翻译"一样学习各种软件服务的协议;而有了MCP后,AI只需要学会这一门"世界语"就能与所有服务交流了。
二、MCP vs Tool Calling:为什么有了筷子🥢还要发明叉子?🍴
既然有了Tool Calling,为什么还要有MCP,举两个场景来说明:
- 在SpringAI中编写的Tools,可以放到Python中使用吗🤔?显然是不可以的,要想让Python中使用,就得使用Python语言重写一次Tool,才能使用,这就是不通用性的体现😓。
- 在企业项目开发中,我们采用Spring AI框架来构建智能体🤖。这些智能体通常需要执行一系列通用功能,如天气查询🌤️、商品检索及下单🛒等。基于以往的做法,每当开发新项目时,都需要重新编写实现这些功能的工具(Tool),这显然不利于代码复用🔄。为解决这一问题,可以采用MCP标准进行服务器端开发,并将这些常用工具封装成服务接口🛠️。这样一来,其他的智能体只需通过调用该服务即可轻松访问所需功能,从而极大地提高了代码的复用性和开发效率📈。
所以说,MCP就像是餐具中的"瑞士军刀"——更加通用和便捷。
从技术实现角度看,MCP本质上也是基于Tool Calling机制,但通过标准化协议进行了封装和抽象,提供了更加统一和规范的交互方式。
三、MCP通信机制:两种"约会方式" 💕
根据 MCP 的规范,当前支持两种通信机制(传输方式):
- Stdio(标准输入输出):本地进程间通信(IPC),主要用在本地服务上,操作你本地的软件或者本地的文件。
- SSE(Server-Sent Events):远程网络通信(基于HTTP),主要用在跨网络的实时数据传输,适用于需要访问远程资源或分布式部署的场景。
四、 Spring AI MCP: Java开发者的"神兵利器" ⚔️
SpringAI对MCP做了支持,简化了Java项目中的MCP开发。参考文档
1. MCP Client:AI的"私人助理" 💼
MCP Client 负责建立和管理与 MCP 服务器之间的连接,以及管理:
- 协议版本协商以确保与服务器的兼容性
- 功能协商以确定可用特性
- 消息传输和 JSON-RPC 通信
- 工具发现与执行
- 资源访问与管理
- 提示系统(Prompt )交互
- 可选功能:
- 根管理
- 采样支持
- 同步和异步操作
- 传输选项:
- 基于标准输入输出(stdio)的进程间通信传输
- 基于 Java HttpClient 的 SSE 客户端传输
- 用于响应式 HTTP 流的 WebFlux SSE 客户端传输
2. MCP Server:工具的"才艺展示平台" 🎤
- 服务器端协议操作实现
- 工具暴露与发现
- 基于URI的资源管理
- 提示模板提供与处理
- 与客户端的能力协商
- 结构化日志记录和通知
- 并发客户端连接管理
- 同步和异步API支持
- 传输实现:
- 基于标准输入输出(stdio)的进程间通信传输
- 基于Servlet的SSE(服务器发送事件)服务器传输
- 用于响应式HTTP流的WebFlux SSE服务器传输
- WebMVC SSE 服务器传输,用于基于 Servlet 的 HTTP 流式传输
五、MCP Client实战:让AI成为"浏览器操控大师" 🎮
下面我们将基于SpringAI的MCP功能进行对MCP的Client进行学习。
我们的需求是,给大模型提问:打开网站:https://www.coze.cn/,总结下这个网站的内容`,就会进行相应的操作。显然,大模型如果不做增强的话,是做不到的,下面我们就基于MCP进行实现。
第一步,先在本机通过npm安装@executeautomation/playwright-mcp-server,如下:
#使用国内源安装命令:
npm install -g @executeautomation/playwright-mcp-server --registry=https://registry.npmmirror.com
第二步,增加依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
第三步,增加mcp-servers.json文件:
{
"mcpServers": {
"playwright": {
"command": "npx.cmd",
"args": [
"-y",
"@executeautomation/playwright-mcp-server"
]
}
}
}
第四步,在application.yml中增加MCP的配置内容:
spring:
ai:
mcp:
client:
enabled: true # 启用mcp客户端
name: ${spring.application.name} # 客户端名称
version: 1.0.0 # 版本号
request-timeout: 30s # 请求超时时间
toolcallback:
enabled: true # 开启工具回调功能
stdio:
servers-configuration: classpath:mcp-servers.json # mcp服务器配置文件
第五步,在SpringAIConfig中配置Tools
@Configuration
public class SpringAIConfig {
private static final String SYSTEM_PROMPT = """
你是一个全能助手,可以帮我解决各种问题。
""";
/**
* 创建并返回一个ChatClient的Spring Bean实例。
*
* @param builder 用于构建ChatClient实例的构建者对象
* @return 构建好的ChatClient实例
*/
@Bean
public ChatClient chatClient(ChatClient.Builder builder,
ToolCallbackProvider tools
) {
return builder
.defaultSystem(SYSTEM_PROMPT) // 设置默认的系统角色
.defaultTools(tools) // 设置默认的工具
.build();
}
}
完成以上步骤后,AI就能自动打开浏览器并访问指定网站了!就像给AI配了一个"机械手",让它能够实际操作浏览器。
原理分析:背后的"魔法" ✨
通过上面的实战,已经可以看到大模型可以控制浏览器了,是怎么做到的呢?
实际上,底层的实现也是基于Tool Calling实现的,由于配置了mcpServers,就拥有了好多的工具,注册到SpringAI中。
而这些工具的开发,并不是我们自己做的,也不是用java开发的,是别人开发好的,遵循了MCP协议,所以就可以集成到SpringAI中使用了,而这工具就是用来控制浏览器的,所以就实现了上面的效果。
六、MCP Server实战:打造自己的"天气查询小能手" 🌤️:
前面我们都是使用的已经写好的MCP Server,实际上,也可以自己来实现的。
接下来,我们将写一个自己的天气查询服务,将他封装成MCP Server,这样需要的天气查询服务的大模型,就可以直接集成了。
查询天气服务平台:t.weather.itboy.net/api/weather…
1. 创建my-spring-ai-mcp-server
2. 导入依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xiaokui</groupId>
<artifactId>my-spring-ai-mcp</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>my-spring-ai-mcp-server</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
</dependencies>
</project>
3. 编写配置
server:
port: 8101 #端口
tomcat:
uri-encoding: UTF-8 #服务编码
spring:
application:
name: my-spring-ai-mcp-server
ai:
mcp:
server:
enabled: true
name: ${spring.application.name}
version: 1.0.0
type: ASYNC # 服务类型 异步,支持 SYNC 和 ASYNC
4. 启动类
@Slf4j
@SpringBootApplication
public class MCPServerApplication {
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplicationBuilder(MCPServerApplication.class).build(args);
Environment env = app.run(args).getEnvironment();
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
log.info("--/\n---------------------------------------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}\n\t" +
"External: \t{}://{}:{}\n\t" +
"Profile(s): \t{}" +
"\n---------------------------------------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
env.getProperty("server.port"),
protocol,
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"),
env.getActiveProfiles());
}
}
5. 编写工具
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WeatherDTO {
@JsonPropertyDescription("城市ID")
private String cityId;
@JsonPropertyDescription("城市名称")
private String city;
@JsonPropertyDescription("当前温度(单位:℃)")
private String temperature;
@JsonPropertyDescription("低温(单位:℃)")
private String lowTemperature;
@JsonPropertyDescription("高温(单位:℃)")
private String highTemperature;
@JsonPropertyDescription("数据日期(格式:YYYYMMDD)")
private String date;
@JsonPropertyDescription("空气质量指数")
private String quality;
@JsonPropertyDescription("PM2.5 浓度(单位:微克/立方米)")
private double pm25;
}
@Service
public class WeatherService {
@Tool(description = "根据城市id查询天气信息")
public WeatherDTO getWeather(@ToolParam(description = "城市id") String cityId) {
// 通过http请求获取天气信息,并且通过json数据解析为WeatherDTO对象
String url = "http://t.weather.itboy.net/api/weather/city/" + cityId;
String data = HttpUtil.get(url);
JSONObject jsonObject = JSONUtil.parseObj(data);
return WeatherDTO.builder()
.cityId(jsonObject.getByPath("cityInfo.citykey", String.class)) // 城市ID
.city(jsonObject.getByPath("cityInfo.city", String.class)) // 城市名称
.date(jsonObject.getByPath("date", String.class))// 数据日期
.temperature(jsonObject.getByPath("data.wendu", String.class)) // 当前温度
.lowTemperature(jsonObject.getByPath("data.forecast[0].low", String.class))// 低温
.highTemperature(jsonObject.getByPath("data.forecast[0].high", String.class))// 高温
.quality(jsonObject.getByPath("data.quality", String.class))// 空气质量
.pm25(jsonObject.getByPath("data.pm25", Double.class))// PM2.5数值
.build();
}
}
6. MCPServer配置
@Configuration
public class McpConfig {
/**
* 申明对外提供服务的工具
*/
@Bean
public List<ToolCallback> tools(WeatherService weatherService) {
return List.of(ToolCallbacks.from(weatherService));
}
}
7. 在MCPClient中集成服务
只需要在MCPClient中指定服务地址即可。
server:
port: 8100 #端口
tomcat:
uri-encoding: UTF-8 #服务编码
spring:
application:
name: my-spring-ai-mcp-client
ai:
dashscope:
api-key: ${ALIYUN_API_KEY}
chat:
options:
model: qwen-plus
mcp:
client:
enabled: true # 启用mcp客户端
name: ${spring.application.name} # 客户端名称
version: 1.0.0 # 版本号
request-timeout: 30s # 请求超时时间
toolcallback:
enabled: true # 开启工具回调功能
stdio:
servers-configuration: classpath:mcp-servers.json # mcp服务器配置文件
type: ASYNC # 服务类型,指定为异步,支持 SYNC 和 ASYNC
sse:
connections:
server1:
url: http://localhost:8101 # 指定MCP Server服务器地址
七、在线MCP服务:直接"呼叫外援" 📞
前面我们自定义了MCP服务,并且通过sse协议提供了服务,这种在线服务的方式也是以后的一种主流方式。 使用起来也非常的简单,这里我们以高德为例进行讲解。
1. 集成使用
通过官方文档,可以看到sse的地址:
url规则为: https://mcp.amap.com/sse?key=您在高德官网上申请的key
需要注意:
由于使用外部的sse服务,不要通过配置url的方式进行配置,需要通过代码的方式集成,否则会不能正常使用
增加配置类:
@Configuration
public class McpConfig {
/**
* 高德地图 MCP 服务
*/
@Bean
public List<NamedClientMcpTransport> amapMcpClientTransport() {
McpClientTransport transport = HttpClientSseClientTransport
.builder("https://mcp.amap.com")
.sseEndpoint("/sse?key=xxxxxxx")
.objectMapper(new ObjectMapper())
.build();
return List.of(new NamedClientMcpTransport("amap", transport));
}
}
可以看到,已经调用了高德服务了。
八、 MCP服务市场:AI工具的"应用商店" 🏪
目前也有挺多的MCP服务市场了,以后应该还会有很多。
- 阿里云:bailian.console.aliyun.com/console?tab…
- 百度:sai.baidu.com/ai/mcp
- 魔搭社区:modelscope.cn/mcp
- mcp.so:mcp.so/zh
通过本文的学习,相信你已经对MCP协议有了深入的理解,并且能够基于SpringAI实现MCP客户端和服务端。MCP协议为AI与外部服务的交互提供了标准化方案,极大地扩展了AI的能力边界。未来,随着MCP生态的不断完善,AI应用开发将会变得更加简单和高效!