Spring AI 入门级学习 5:MCP
我们上一节知道了怎么创建和定义可调用的工具,但是你会发现这些工具的使用都是在我们的AI应用内部的,我们对工具的改动都相当于是改了我们的应用,工具的调用怎么看都像是一种扩展功能,一种可随时插拔的插件式的东西
所以就有了 MCP
MCP 的定义
MCP 翻译过来就是模型上下文协议(Model Context Protocol),就是一种标准,造一个能被AI调用的工具不是什么新鲜事,但要互通要有标准啊,像是现实中的造车、食品等方面都有国家标准,所以有了 MCP 就相当于给可插拔的工具调用说明了是使用的 USB 还是 type-c,标准可以造就生态
了解 MCP 的架构
MCP 的核心是 “客户端 - 服务器” 架构,其中 MCP 客户端主机可以连接到多个服务器,服务端也可以为多个客户端提供服务,客户端主机是指希望访问 MCP 服务的程序,像是Claude code、trae等
我们先说MCP 客户端
MCP Client 主要负责和 MCP 服务器建立连接并进行通信,为了适应不同场景,它提供了多种数据传输方式,包括:
- Stdio 标准输入 / 输出:适用于本地调用
- 基于 Java HttpClient 和 WebFlux 的 SSE 传输:适用于远程调用
(这里简单补充一下stdio 模式和SSE 模式
stdio 就是 标准输入 / 标准输出
意思是客户端直接启动一个本地程序,然后通过这个程序的命令行输入输出跟它通信
SSE 是 Server-Sent Events
意思是 MCP 服务端自己作为一个 Web 服务跑起来,客户端通过 URL 连过去通信)
再说说MCP 服务端
MCP Server 主要用来为客户端提供各种工具、资源和功能支持的,和客户端一样,它也可以通过多种方式进行数据传输,比如 Stdio 标准输入 / 输出、基于 Servlet / WebFlux / WebMVC 的 SSE 传输,满足不同应用场景
最简单的使用
一些AI工具(像是trae)都提供了自定义添加MCP服务的功能,大多数 MCP 服务都支持基于 NPX 工具运行,添加几行配置就行

(这里补充一下 Node.js 中 npx 工具的使用
npx 是 Node.js 生态里“临时运行一个包里的命令”的工具
先通过 npx 找这个 npm 包
本地没装时,会先下载到缓存,而不是使用的项目里
然后直接把这个包里的可执行命令跑起来)
若要是想在自己的项目中使用 MCP
(也可以说是自己项目添加一个MCP客户端了)
1.首先引入依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
2.在 resources 目录下新建 mcp-servers.json 配置
{
"mcpServers": {
"amap-maps": {
"command": "npx",
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "改成你的 API Key"
}
}
}
}
3.修改 Spring 配置文件
spring:
ai:
mcp:
client:
stdio: // 本地运行使用stdio模式,且要指定配置文件位置
servers-configuration: classpath:mcp-servers.json
4.添加代码,新增一个利用 MCP 完成对话的方法
@Resource
private ToolCallbackProvider toolCallbackProvider;
// 通过自动注入的 ToolCallbackProvider 获取到配置中定义的 MCP 服务提供的 所有工具
public String doChatWithMcp(String message, String chatId) {
ChatResponse response = chatClient
.prompt()
.user(message)
.tools(toolCallbackProvider) // 像工具调用一样使用
.call()
.chatResponse();
String content = response.getResult().getOutput().getText();
return content;
}
从这段代码我们能够看出,MCP 调用的本质就是类似工具调用( .tools(toolCallbackProvider) )
那接下来我们就进入
自己开发一个MCP服务
首先是 MCP 客户端开发
(其实与上面说的自己项目中使用MCP一样,不过这里再说的详细一点吧)
1.引入依赖(spring-ai-starter-mcp-client:核心启动器,提供 STDIO 和基于 HTTP 的 SSE 支持)
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
2.配置连接
用spring配置+json文件(目前仅支持 stdio 连接)
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:mcp-servers.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Desktop",
"/Users/username/Downloads"
]
}
}
}
直接写入配置文件,这种方式同时支持 stdio 和 SSE 连接方式
spring:
ai:
mcp:
client:
enabled: true
name: my-mcp-client
version: 1.0.0
request-timeout: 30s
type: SYNC
sse:
connections:
server1:
url: http://localhost:8080
stdio:
connections:
server1:
command: /path/to/server
args:
- --port=8080
env:
API_KEY: your-api-key
3.使用服务
// 和 Spring AI 的工具进行整合
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
// 这里可以真的把MCP当做工具注册的
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
ChatResponse response = chatClient
.prompt()
.user(message)
.tools(toolCallbackProvider)
.call()
.chatResponse();
4.补充:同步和异步客户端
Spring AI 同时支持 同步和异步客户端类型,可根据应用需求选择合适的模式,只需要更改配置即可:spring.ai.mcp.client.type=ASYNC
(type=ASYNC 说的是 底层客户端是异步实现;
但你自己的代码如果还是“发出请求后立刻等结果”,那 整体业务看起来仍然是同步的;
就相当于是两个关口,业务代码到底层客户端可以用代码控制是用排队还是等着,底层客户端到MCP服务用配置可以控制排队和等着)
好,接下来才是重点
MCP 服务端开发(图片搜索服务示例,用Pexels网站常规搜索接口)
(当然你要先去pexels网站注册并获取自己的API密钥才可以免费调用)
1.引入依赖(spring-ai-starter-mcp-server-webmvc:提供基于 Spring MVC 的 SSE 传输和可选的 stdio 传输,一般建议引入这个)
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
2.配置文件
编写主配置文件 application.yml,可以灵活指定激活哪套配置
spring:
application:
name: image-search-mcp-server
profiles:
active: stdio // 或是sse
server:
port: 8127
stdio 配置文件 application-stdio.yml(需关闭 web 支持)
spring:
ai:
mcp:
server:
name: yu-image-search-mcp-server
version: 0.0.1
type: SYNC
#stdio
stdio: true
#stdio
main:
web-application-type: none
banner-mode: off
SSE 配置文件 application-sse.yml(需关闭 stdio 模式)
spring:
ai:
mcp:
server:
name: yu-image-search-mcp-server
version: 0.0.1
type: SYNC
#sse
stdio: false
3.编写服务类(其实和之前的@Tool定义工具调用一样的)
@Service
public class ImageSearchTool {
// 替换为你的 Pexels API 密钥(需从官网申请)
private static final String API_KEY = "你的 API Key";
// Pexels 常规搜索接口(请以文档为准)
private static final String API_URL = "https://api.pexels.com/v1/search";
@Tool(description = "search image from web")
public String searchImage(@ToolParam(description = "Search query keyword") String query) {
try {
return String.join(",", searchMediumImages(query));
} catch (Exception e) {
return "Error search image: " + e.getMessage();
}
}
/**
* 搜索中等尺寸的图片列表
*
* @param query
* @return
*/
public List<String> searchMediumImages(String query) {
// 设置请求头(包含API密钥)
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", API_KEY);
// 设置请求参数(仅包含query,可根据文档补充page、per_page等参数)
Map<String, Object> params = new HashMap<>();
params.put("query", query);
// 发送 GET 请求
String response = HttpUtil.createGet(API_URL)
.addHeaders(headers)
.form(params)
.execute()
.body();
// 解析响应JSON(假设响应结构包含"photos"数组,每个元素包含"medium"字段)
return JSONUtil.parseObj(response)
.getJSONArray("photos")
.stream()
.map(photoObj -> (JSONObject) photoObj)
.map(photoObj -> photoObj.getJSONObject("src"))
.map(photo -> photo.getStr("medium"))
.filter(StrUtil::isNotBlank)
.collect(Collectors.toList());
}
}
4.在主类中通过定义 ToolCallbackProvider Bean 来注册工具(放配置类也行)
@Bean
public ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) {
return MethodToolCallbackProvider.builder()
.toolObjects(imageSearchTool)
.build();
}
然后就可以用SSE模式:先启动这个MCP服务端,然后客户端用url的方式访问;
或是stdio模式:打包成jar包,然后客户端用stdio用配置文件中的命令启动这个jar包,再访问
客户端stdio方式中
stdio模式中mcp-servers.json 配置文件及相关命令
{
"mcpServers": {
"yu-image-search-mcp-server": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dspring.main.web-application-type=none",
"-Dlogging.pattern.console=",
"-jar",
"服务端jar包的位置image-search-mcp-server-0.0.1-SNAPSHOT.jar"
],
"env": {}
}
}
}
最后说说题外话:同样是可以实现工具的调用,skills出来后MCP好像都听不见声了
事实就是这样,实现方式上skills只是一个文件夹,有时候里面还只有提示词连程序脚本都没有,可使用起来人们就是偏向skills,要我说skills相比MCP有一个巨大的优点:就是简单,没错,就是一些没有技术背景的人都完全可以开源一个自己的skills,这就是一个巨大的生态啊
当然关于skills与MCP的区别以及是否真的会取代的问题,不会那么简单,也许我可以专门出一个文章好好聊聊
更多推荐
所有评论(0)