我们团队最近在疯狂的研究AI,期间调研学习了大量的资料,感谢大佬们的分享。
期间不仅做了几个还不错的项目,也踩坑不少,我们也发光发热,把我们总结的经验以专栏的方式分享出来,希望对大家有帮助。
这是专栏内容的第6篇,这是专栏链接,没看之前文章的朋友,建议先看之前的内容。
本节重点
学习 AI 应用开发的高级特性 —— MCP 模型上下文协议,打通 AI 与外部服务的边界。先学习 MCP 的几种使用方式,然后基于 Spring AI 框架实战开发 MCP 客户端与服务端,帮你掌握 MCP 的架构原理和最佳实践。
具体内容包括:
- MCP 必知必会
- MCP 的 3 种使用方式
- Spring AI MCP 开发模式
- Spring AI MCP 开发实战 - 图片搜索 MCP
- MCP 开发最佳实践
- MCP 部署方案
- MCP 安全问题
友情提示:由于 AI 的更新速度飞快,随着平台 / 工具 / 技术 / 软件的更新,教程的部分细节可能会失效,所以请大家重点学习思路和方法,不要因为实操和教程不一致就过于担心,而是要学会自己阅读官方文档并查阅资料,多锻炼自己解决问题的能力。
一、MCP 必知必会
什么是 MCP?
MCP(Model Context Protocol,模型上下文协议)是一种开放标准,目的是增强 AI 与外部系统的交互能力。MCP 为 AI 提供了与外部工具、资源和服务交互的标准化方式,让 AI 能够访问最新数据、执行复杂操作,并与现有系统集成。
根据 官方定义,MCP 是一种开放协议,它标准化了应用程序如何向大模型提供上下文的方式。可以将 MCP 想象成 AI 应用的 USB 接口。就像 USB 为设备连接各种外设和配件提供了标准化方式一样,MCP 为 AI 模型连接不同的数据源和工具提供了标准化的方法。
前面说的可能有些抽象,让我举些例子帮大家理解 MCP 的作用。首先是 增强 AI 的能力,通过 MCP 协议,AI 应用可以轻松接入别人提供的服务来实现更多功能,比如搜索网页、查询数据库、调用第三方 API、执行计算。
其次,我们一定要记住 MCP 它是个 协议 或者 标准,它本身并不提供什么服务,只是定义好了一套规范,让服务提供者和服务使用者去遵守。这样的好处显而易见,就像 HTTP 协议一样,现在前端向后端发送请求基本都是用 HTTP 协议,什么 get / post 请求类别、什么 401、404 状态码,这些标准能 有效降低开发者的理解成本
此外,标准化还有其他的好处。举个例子,以前我们想给 AI 增加查询地图的能力,需要自己开发工具来调用第三方地图 API;如果你有多个项目、或者其他开发者也需要做同样的能力,大家就要重复开发,就导致同样的功能做了多遍、每个人开发的质量和效果也会有差别而如果官方把查询地图的能力直接做成一个服务,谁要用谁接入,不就省去了开发成本、并且效果一致了么?如果大家都陆续开放自己的服务,不就相当于打造了一个服务市场,造福广大开发者了么!
标准可以造就生态。 其实这并不新鲜了,前端同学可以想想 NPM 包,后端同学可以想想 Maven 仓库还有 Docker 镜像源,不懂编程的同学想想手机应用市场,应该就能理解了。
这就是 MCP 的三大作用:
- 轻松增强 AI 的能力
- 统一标准,降低使用和理解成本
- 打造服务生态,造福广大开发者
MCP 架构
1、宏观架构
MCP 的核心是 “客户端 - 服务器” 架构,其中 MCP 客户端主机可以连接到多个服务器。客户端主机是指希望访问 MCP 服务的程序,比如 Claude Desktop、IDE、AI 工具或部署在服务器上的项目。
2、SDK 3 层架构
如果我们要在程序中使用 MCP 或开发 MCP 服务,可以引入 MCP 官方的 SDK,比如 Java SDK。让我们先通过 MCP 官方文档了解 MCP SDK 的架构,主要分为 3 层
分别来看每一层的作用:
- 客户端 / 服务器层:McpClient 处理客户端操作,而 McpServer 管理服务器端协议操作。两者都使用 McpSession 进行通信管理。
- 会话层(McpSession):通过 DefaultMcpSession 实现管理通信模式和状态。
- 传输层(McpTransport):处理 JSON-RPC 消息序列化和反序列化,支持多种传输实现,比如 Stdio 标准 IO 流传输和 HTTP SSE 远程传输。
客户端和服务端需要先经过下面的流程建立连接,之后才能正常交换消息:
3、MCP 客户端
MCP Client 是 MCP 架构中的关键组件,主要负责和 MCP 服务器建立连接并进行通信。它能自动匹配服务器的协议版本、确认可用功能、负责数据传输和 JSON-RPC 交互。此外,它还能发现和使用各种工具、管理资源、和提示词系统进行交互。
除了这些核心功能,MCP 客户端还支持一些额外特性,比如根管理、采样控制,以及同步或异步操作。为了适应不同场景,它提供了多种数据传输方式,包括:
- Stdio 标准输入 / 输出:适用于本地调用
- 基于 Java HttpClient 和 WebFlux 的 SSE 传输:适用于远程调用
客户端可以通过不同传输方式调用不同的 MCP 服务,可以是本地的、也可以是远程的。如图:
4、MCP 服务端
MCP Server 也是整个 MCP 架构的关键组件,主要用来为客户端提供各种工具、资源和功能支持。
它负责处理客户端的请求,包括解析协议、提供工具、管理资源以及处理各种交互信息。同时,它还能记录日志、发送通知,并且支持多个客户端同时连接,保证高效的通信和协作。
和客户端一样,它也可以通过多种方式进行数据传输,比如 Stdio 标准输入 / 输出、基于 Servlet / WebFlux / WebMVC 的 SSE 传输,满足不同应用场景。
这种设计使得客户端和服务端完全解耦,任何语言开发的客户端都可以调用 MCP 服务。如图:
MCP 核心概念
很多同学以为 MCP 协议就只能提供工具给别人调用,但实际上,MCP 协议的本领可大着呢!
按照官方的说法,总共有 6 大核心概念。大家简单了解一下即可,除了 Tools 工具之外的其他概念都不是很实用,如果要进一步学习可以阅读对应的官方文档。
- Resources 资源:让服务端向客户端提供各种数据,比如文本、文件、数据库记录、API 响应等,客户端可以决定什么时候使用这些资源。使 AI 能够访问最新信息和外部知识,为模型提供更丰富的上下文。
- Prompts 提示词:服务端可以定义可复用的提示词模板和工作流,供客户端和用户直接使用。它的作用是标准化常见的 AI 交互模式,比如能作为 UI 元素(如斜杠命令、快捷操作)呈现给用户,从而简化用户与 LLM 的交互过程。
- Tools 工具:MCP 中最实用的特性,服务端可以提供给客户端可调用的函数,使 AI 模型能够执行计算、查询信息或者和外部系统交互,极大扩展了 AI 的能力范围。
- Sampling 采样:允许服务端通过客户端向大模型发送生成内容的请求(反向请求)。使 MCP 服务能够实现复杂的智能代理行为,同时保持用户对整个过程的控制和数据隐私保护。
- Roots 根目录:MCP 协议的安全机制,定义了服务器可以访问的文件系统位置,限制访问范围,为 MCP 服务提供安全边界,防止恶意文件访问。
- Transports 传输:定义客户端和服务器间的通信方式,包括 Stdio(本地进程间通信)和 SSE(网络实时通信),确保不同环境下的可靠信息交换。
MCP 官方文档 中提到,大多数客户端也只支持 Tools 工具调用能力:
所以接下来我们学习使用和开发 MCP 的过程中,只需关注 Tools 工具即可。
二、使用 MCP
本节我们将实战 3 种使用 MCP 的方式:
- 云平台使用 MCP
- 软件客户端使用 MCP
- 程序中使用 MCP
无论是哪种使用方式,原理都是类似的,而且有 2 种可选的使用模式:本地下载 MCP 服务端代码并运行(类似引入了一个 SDK),或者 直接使用已部署的 MCP 服务(类似调用了别人的 API)。
到哪里去找别人开发的 MCP 服务呢?
MCP 服务大全
目前已经有很多 MCP 服务市场,开发者可以在这些平台上找到各种现成的 MCP 服务:
- MCP.so:较为主流,提供丰富的 MCP 服务目录
- GitHub Awesome MCP Servers:开源 MCP 服务集合
- 阿里云百炼 MCP 服务市场
- Spring AI Alibaba 的 MCP 服务市场
- Glama.ai MCP 服务
其中,绝大多数 MCP 服务市场仅提供本地下载 MCP 服务端代码并运行的使用方式,毕竟部署 MCP 服务也是需要成本的。
有些云服务平台提供了云端部署的 MCP 服务,比如阿里云百炼平台,在线填写配置后就能用,可以轻松和平台上的 AI 应用集成。但一般局限性也比较大,不太能直接在自己的代码中使用。
下面来学习 3 种使用 MCP 的方式。
云平台使用 MCP
以阿里云百炼为例,参考 官方 MCP 文档,我们可以直接使用官方预置的 MCP 服务,或者部署自己的 MCP 服务到阿里云平台上。
如图,官方提供了很多现成的 MCP 服务:
让我们进入一个智能体应用,在左侧可以点击添加 MCP 服务,然后选择想要使用的 MCP 服务即可,比如使用高德地图 MCP 服务,提供地理信息查询等 12 个工具。
测试一下,输入 Prompt:我现在居住在北京三里桥,请帮我找到8公里内合适的通勤上班的科技公司。
发现 AI 自动调用了 MCP 提供的多个工具,给出了不错的回答:
AI 会根据需要调用不同的工具,比如将地点转为坐标、查找某坐标附近的地点:
调用工具完成后,AI 会利用工具的输出结果进一步分析并生成回复。这个流程是不是很像工具调用(Tool Calling)?
软件客户端使用 MCP
不同的客户端软件对 MCP 支持程度不同,可以在 官方文档 中查看各客户端支持的特性。
下面我们以主流 AI 客户端 Cursor 为例,演示如何使用 MCP 服务。由于没有现成的部署了 MCP 服务的服务器,我们采用本地运行的方式。
1、环境准备
首先安装本地运行 MCP 服务需要用到的工具,具体安装什么工具取决于 MCP 服务的配置要求。
比如我们到 MCP 市场 找到 高德地图 MCP,发现 Server Config 中定义了使用 npx 命令行工具来安装和运行服务端代码:
大多数 MCP 服务都支持基于 NPX 工具运行,所以推荐安装 Node.js 和 NPX,去 官网 傻瓜式安装即可。
从配置中我们发现,使用地图 MCP 需要 API Key,我们可以到 地图开放平台 创建应用并添加 API Key:
2、Cursor 接入 MCP
在右上角进入 Cursor Settings 设置界面,然后选择 MCP,添加全局的 MCP Server:
接下来从 MCP 市场中找到 MCP Server Config,并粘贴到 mcp.json 配置中,注意要将 API Key 更改为自己的:
保存配置,软件会自动识别并启动服务,效果如图:
3、测试使用 MCP
接下来就可以使用 MCP 服务了,还是提供之前的 Prompt:我现在居住在北京三里桥,请帮我找到8公里内合适的通勤上班的科技公司。
观察效果,发现 AI 可能会多次调用 MCP
但是这也让我们意识到使用 MCP 服务的代价 —— 由于调用次数不稳定,可能产生较高的 AI 和 API 调用费用,所以一般我的建议是 能不用就不用。
如果要使用其他软件客户端,接入 MCP 的方法也是类似的,可以直接看软件官方(或 MCP 官方)提供的接入文档,比如:
- Cherry Studio:查看 软件官方文档 了解集成方法
- Claude Desktop:参考 MCP 官方的用户快速入门指南
程序中使用 MCP
让我们利用 Spring AI 框架,在程序中使用 MCP 并完成我们的需求,实现一个能够根据另一半的位置推荐约会地点的 AI 助手。
💡 类似的 Java MCP 开发框架还有 Solon AI MCP,但由于我们更多地使用 Spring 生态,所以还是推荐使用 Spring AI 框架。
首先了解 Spring AI MCP 客户端的基本使用方法。建议参考 Spring AI Alibaba 的文档,因为 Spring AI 官方文档 更新的太快了,包的路径可能会变动。
1)在 Maven 中央仓库 中可以找到正确的依赖,引入到项目中:
<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 配置,定义需要用到的 MCP 服务:
{
"mcpServers": {
"amap-maps": {
"command": "npx.cmd",
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "你的key"
}
}
}
}
💡 特别注意:在 Windows 环境下,命令配置需要添加 .cmd 后缀(如 npx.cmd),否则会报找不到命令的错误。
3)修改 Spring 配置文件,编写 MCP 客户端配置。由于是本地运行 MCP 服务,所以使用 stdio 模式,并且要指定 MCP 服务配置文件的位置。代码如下:
spring:
ai:
mcp:
client:
stdio:
servers-configuration:classpath:mcp-servers.json
这样一来,MCP 客户端程序启动时,会额外启动一个子进程来运行 MCP 服务,从而能够实现调用。
4)修改 InterviewAPP 的代码,新增一个利用 MCP 完成对话的方法。通过自动注入的 ToolCallbackProvider 获取到配置中定义的 MCP 服务提供的 所有工具,并提供给 ChatClient。代码如下:
@Resourceprivate ToolCallbackProvider toolCallbackProvider;
public String doChatWithMcp(String message, String chatId) {
ChatResponse response = chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
// 开启日志,便于观察效果
.advisors(new MyLoggerAdvisor())
.tools(toolCallbackProvider)
.call()
.chatResponse();
String content = response.getResult().getOutput().getText();
log.info("content: {}", content);
return content;
}
从这段代码我们能够看出,MCP 调用的本质就是类似工具调用,并不是让 AI 服务器主动去调用 MCP 服务,而是告诉 AI “MCP 服务提供了哪些工具”,如果 AI 想要使用这些工具完成任务,就会告诉我们的后端程序,后端程序在执行工具后将结果返回给 AI,最后由 AI 总结并回复。
5)测试运行。编写单元测试代码:
@Testvoid doChatWithMcp() {
String chatId = UUID.randomUUID().toString();
// 测试地图 MCPString message = "我现在居住在北京三里桥,请帮我找到8公里内合适的通勤上班的科技公司。";
String answer = InterviewAPP.doChatWithMcp(message, chatId);
}
运行效果如图所示,可以看到 functionCallbacks 中加载了多个地图 MCP 提供的工具:
可以在地图开放平台的控制台查看 API Key 的使用量,注意控制调用次数避免超出限额:
三、Spring AI MCP 开发模式
Spring AI 在 MCP 官方 Java SDK 的基础上额外封装了一层,提供了和 Spring Boot 整合的 SDK,支持客户端和服务端的普通调用和响应式调用。下面分别学习如何使用 Spring AI 开发 MCP 客户端和服务端。
MCP 客户端开发
客户端开发主要基于 Spring AI MCP Client Boot Starter,能够自动完成客户端的初始化、管理多个客户端实例、自动清理资源等。
1、引入依赖
Spring AI 提供了 2 种客户端 SDK,分别支持非响应式和响应式编程,可以根据需要选择对应的依赖包:9miz/
spring-ai-starter-mcp-client:核心启动器,提供 STDIO 和基于 HTTP 的 SSE 支持spring-ai-starter-mcp-client-webflux:基于 WebFlux 响应式的 SSE 传输实现
比如下面的依赖(具体的依赖名称以官方文档为准):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
2、配置连接
引入依赖后,需要配置与服务器的连接,Spring AI 支持两种配置方式:
1)直接写入配置文件,这种方式同时支持 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
先了解上面这些配置即可,更多配置属性可参考 官方文档。
2)引用 Claude Desktop 格式 的 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"
]
}
}
}
3、使用服务
启动项目时,Spring AI 会自动注入一些 MCP 相关的 Bean。
1)如果你想完全自主控制 MCP 客户端的行为,可以使用 McpClient Bean,支持同步和异步:
// 同步客户端
@Autowired
private List<McpSyncClient> mcpSyncClients;
// 异步客户端
@Autowired
private List<McpAsyncClient> mcpAsyncClients;
查看 McpSyncClient 的源码,发现提供了很多和 MCP 服务端交互的方法,比如获取工具信息、调用工具等等
需要注意的是,每个 MCP 服务连接都会创建一个独立的客户端实例。
2)如果你想利用 MCP 服务提供的工具来增强 AI 的能力,可以使用自动注入的 ToolCallbackProvider Bean,从中获取到 ToolCallback 工具对象。
// 和 Spring AI 的工具进行整合
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
然后绑定给 ChatClient 对象即可:
ChatResponse response = chatClient
.prompt()
.user(message)
.tools(toolCallbackProvider)
.call()
.chatResponse();
4、其他特性
1)Spring AI 同时支持 同步和异步客户端类型,可根据应用需求选择合适的模式,只需要更改配置即可:
spring.ai.mcp.client.type=ASYNC
2)开发者还可以通过编写自定义 Client Bean 来 定制客户端行为,比如设置请求超时时间、设置文件系统根目录的访问范围、自定义事件处理器、添加特定的日志处理逻辑。rxUlXocL6iI0ChnjjDcUaJVycix5vlr0Z9nXRSlePXA=
官方提供的示例代码如下,简单了解即可:
@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
@Overridepublic void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
// 自定义请求超时配置
spec.requestTimeout(Duration.ofSeconds(30));
// 设置此客户端可访问的根目录URI
spec.roots(roots);
// 设置处理消息创建请求的自定义采样处理器
spec.sampling((CreateMessageRequest messageRequest) -> {
// 处理采样CreateMessageResult result = ...
return result;
});
// 添加在可用工具变更时通知的消费者
spec.toolsChangeConsumer((List<McpSchema.Tool> tools) -> {
// 处理工具变更
});
// 添加在可用资源变更时通知的消费者
spec.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> {
// 处理资源变更
});
// 添加在可用提示词变更时通知的消费者
spec.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> {
// 处理提示词变更
});
// 添加接收服务器日志消息时通知的消费者
spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
// 处理日志消息
});
}
}
MCP 服务端开发
服务端开发主要基于 Spring AI MCP Server Boot Starter,能够自动配置 MCP 服务端组件,使开发者能够轻松创建 MCP 服务,向 AI 客户端提供工具、资源和提示词模板,从而扩展 AI 模型的能力范围。
1、引入依赖
Spring AI 提供了 3 种 MCP 服务端 SDK,分别支持非响应式和响应式编程,可以根据需要选择对应的依赖包:
spring-ai-starter-mcp-server:提供 stdio 传输支持,不需要额外的 web 依赖spring-ai-starter-mcp-server-webmvc:提供基于 Spring MVC 的 SSE 传输和可选的 stdio 传输(一般建议引入这个)spring-ai-starter-mcp-server-webflux:提供基于 Spring WebFlux 的响应式 SSE 传输和可选的 stdio 传输
比如下面的依赖(具体的依赖名称以官方文档为准):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
2、配置服务
如果要开发 stdio 服务,配置如下:
# 使用 spring-ai-starter-mcp-server
spring:
ai:
mcp:
server:
name: stdio-mcp-server
version: 1.0.0
stdio: true
type: SYNC # 同步
开发 SSE 服务,配置如下:
# 使用 spring-ai-starter-mcp-server-webmvc
spring:
ai:
mcp:
server:
name: webmvc-mcp-server
version: 1.0.0
type: SYNC # 同步
sse-message-endpoint: /mcp/message # SSE 消息端点路径
sse-endpoint: /sse # SSE 端点路径
如果要开发响应式(异步)服务,配置如下:
# 使用 spring-ai-starter-mcp-server-webflux
spring:
ai:
mcp:
server:
name: webflux-mcp-server
version: 1.0.0
type: ASYNC # 异步
sse-message-endpoint: /mcp/messages # SSE 消息端点路径
sse-endpoint: /sse # SSE 端点路径
还有更多可选配置,详细信息可参考 官方文档。
spring:
ai:
mcp:
server:
enabled: true # 启用/禁用 MCP 服务
stdio: false # 启用/禁用 stdio 传输
name: my-mcp-server # 服务名称
version: 1.0.0 # 服务版本
type: SYNC # 服务类型(SYNC/ASYNC)
resource-change-notification: true # 启用资源变更通知
prompt-change-notification: true # 启用提示词变更通知
tool-change-notification: true # 启用工具变更通知
sse-message-endpoint: /mcp/message # SSE 消息端点路径
sse-endpoint: /sse # SSE 端点路径
# 可选 URL 前缀
base-url: /api/v1 # 客户端访问路径将是/api/v1/sse 和 /api/v1/mcp/message
3、开发服务
无论采用哪种传输方式,开发 MCP 服务的过程都是类似的,跟开发工具调用一样,直接使用 @Tool 注解标记服务类中的方法。
@Service
public class WeatherService {
@Tool(description = "获取指定城市的天气信息")
public String getWeather(
@ToolParameter(description = "城市名称,如北京、上海") String cityName) {
// 实现天气查询逻辑
return "城市" + cityName + "的天气是晴天,温度22°C";
}
}
然后在 Spring Boot 项目启动时注册一个 ToolCallbackProvider Bean 即可:
@SpringBootApplication
public class McpServerApplication {
@Beanpublic ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}
4、其他特性
我们还可以利用 SDK 来开发 MCP 服务的多种特性,比如:
1)提供工具
支持两种方式:
@Bean
public ToolCallbackProvider myTools(...) {
List<ToolCallback> tools = ...
return ToolCallbackProvider.from(tools);
}
@Bean
public List<McpServerFeatures.SyncToolSpecification> myTools(...) {
List<McpServerFeatures.SyncToolSpecification> tools = ...
return tools;
}
2)资源管理:可以给客户端提供静态文件或动态生成的内容
@Beanpublic List<McpServerFeatures.SyncResourceSpecification> myResources(...) {
var systemInfoResource = new McpSchema.Resource(...);
var resourceSpecification = new McpServerFeatures.SyncResourceSpecification(systemInfoResource, (exchange, request) -> {
try {
var systemInfo = Map.of(...);
String jsonContent = new ObjectMapper().writeValueAsString(systemInfo);
return new McpSchema.ReadResourceResult(
List.of(new McpSchema.TextResourceContents(request.uri(), "application/json", jsonContent)));
}
catch (Exception e) {
throw new RuntimeException("Failed to generate system info", e);
}
});
return List.of(resourceSpecification);
}
3)提示词管理:可以向客户端提供模板化的提示词
@Beanpublic List<McpServerFeatures.SyncPromptSpecification> myPrompts() {
var prompt = new McpSchema.Prompt("greeting", "A friendly greeting prompt",
List.of(new McpSchema.PromptArgument("name", "The name to greet", true)));
var promptSpecification = new McpServerFeatures.SyncPromptSpecification(prompt, (exchange, getPromptRequest) -> {
String nameArgument = (String) getPromptRequest.arguments().get("name");
if (nameArgument == null) { nameArgument = "friend"; }
var userMessage = new PromptMessage(Role.USER, new TextContent("Hello " + nameArgument + "! How can I assist you today?"));
return new GetPromptResult("A personalized greeting message", List.of(userMessage));
});
return List.of(promptSpecification);
}
4)根目录变更处理:当客户端的根目录权限发生变化时,服务端可以接收通知
@Beanpublic BiConsumer<McpSyncServerExchange, List<McpSchema.Root>> rootsChangeHandler() {
return (exchange, roots) -> {
logger.info("Registering root resources: {}", roots);
};
}
大家只需要了解上面这些特性即可,无需记忆和编写代码。通过这些特性,大家应该也会对 MCP 有进一步的了解。简单来说,通过这套标准,服务端能向客户端传递各种各样不同类型的信息(资源、工具、提示词等)。
MCP 工具类
Spring AI 还提供了一系列 辅助 MCP 开发的工具类,用于 MCP 和 ToolCallback 之间的互相转换。
也就是说,开发者可以直接将之前开发的工具转换为 MCP 服务,极大提高了代码复用性:
四、MCP 开发实战 - 图片搜索服务
下面我们将开发一个网络图片搜索 MCP 服务,带大家快速掌握 MCP 开发。
MCP 服务端开发
可以使用 Pexels 图片资源网站的 API 来构建图片搜索服务。
1)首先在 Pexels 网站生成 API Key:
2)在项目根目录下新建 module,名称为 dayu-image-search-mcp-server:
注意,建议在新项目中 单独打开该模块,不要直接在原项目的子文件夹中操作,否则可能出现路径上的问题。
3)引入必要的依赖,包括 Lombok、hutool 工具库和 Spring AI MCP 服务端依赖。
有 Stdio、WebMVC SSE 和 WebFlux SSE 三种服务端依赖可以选择,开发时只需要填写不同的配置,开发流程都是一样的。此处我们选择引入 WebMVC:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
引入这个依赖后,会自动注册 SSE 端点,供客户端调用。包括消息和 SSE 传输端点:
4)在 resources 目录下编写服务端配置文件。这里我们编写两套配置方案,分别实现 stdio 和 SSE 模式的传输。
stdio 配置文件 application-stdio.yml(需关闭 web 支持):
spring:
ai:
mcp:
server:
name: dayu-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: dayu-image-search-mcp-server
version: 0.0.1
type: SYNC
# sse
stdio: false
然后编写主配置文件 application.yml,可以灵活指定激活哪套配置:
spring:
application:
name: dayu-image-search-mcp-server
profiles:
active: stdio
server:
port: 8127
5)编写图片搜索服务类,在 tools 包下新建 ImageSearchTool,使用 @Tool 注解标注方法,作为 MCP 服务提供的工具。
@Servicepublic 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());
}
}
编写对应的单元测试类,先来验证工具是否可用:
@SpringBootTestclass ImageSearchToolTest {
@Resourceprivate ImageSearchTool imageSearchTool;
@Testvoid searchImage() {
String result = imageSearchTool.searchImage("computer");
Assertions.assertNotNull(result);
}
}
测试结果如图,成功根据关键词搜索到了多张图片:
6)在主类中通过定义 ToolCallbackProvider Bean 来注册工具:
@SpringBootApplicationpublic class YuImageSearchMcpServerApplication {
public static void main(String[] args) {
SpringApplication.run(YuImageSearchMcpServerApplication.class, args);
}
@Beanpublic ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) {
return MethodToolCallbackProvider.builder()
.toolObjects(imageSearchTool)
.build();
}
}
7)至此就开发完成了,最后使用 Maven Package 命令打包,会在 target 目录下生成可执行的 JAR 包,等会儿客户端调用时会依赖这个文件。
客户端开发
接下来直接在根项目中开发客户端,调用刚才创建的图片搜索服务。
1)先引入必要的 MCP 客户端依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
<version>1.0.0-M6</version>
</dependency>
当然,实际开发中,你也可以按需添加 WebFlux 支持,但要与服务端模式匹配。
2)先测试 stdio 传输方式。在 mcp-servers.json 配置文件中新增 MCP Server 的配置,通过 java 命令执行我们刚刚打包好的 jar 包。代码如下:
{
"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",
"yu-image-search-mcp-server/target/yu-image-search-mcp-server-0.0.1-SNAPSHOT.jar"
],
"env": {}
}
}
}
3)测试运行。编写单元测试代码:
@Testvoid doChatWithMcp() {
// 测试图片搜索 MCPString message = "帮我搜索一些F1赛车的图片";
String answer = InterviewAPP.doChatWithMcp(message, chatId);
Assertions.assertNotNull(answer);
}
4)接下来测试 SSE 连接方式,首先修改 MCP 服务端的配置文件,激活 SSE 的配置:
spring:
ai:
mcp:
client:
sse:
connections:
server1:
url: http://localhost:8127
# stdio:
# servers-configuration: classpath:mcp-servers.json
然后以 Debug 模式启动 MCP 服务。
然后修改客户端的配置文件,添加 SSE 配置,同时要注释原有的 stdio 配置以避免端口冲突:
spring:
ai:
mcp:
client:
sse:
connections:
server1:
url: http://localhost:8127
# stdio:
# servers-configuration: classpath:mcp-servers.json
五、MCP 开发最佳实践
已经学会如何开发 MCP 服务端和客户端后,我们来学习一些 MCP 开发的最佳实践。
1)慎用 MCP:MCP 不是银弹,其本质就是工具调用,只不过统一了标准、更容易共享而已。如果我们自己开发一些不需要共享的工具,完全没必要使用 MCP,可以节约开发和部署成本。我个人的建议是 能不用就不用,先开发工具调用,之后需要提供 MCP 服务时再将工具调用转换成 MCP 服务即可。
2)传输模式选择:Stdio 模式作为客户端子进程运行,无需网络传输,因此安全性和性能都更高,更适合小型项目;SSE 模式适合作为独立服务部署,可以被多客户端共享调用,更适合模块化的中大型项目团队。
3)明确服务:设计 MCP 服务时,要合理划分工具和资源,并且利用 @Tool、@ToolParam 注解尽可能清楚地描述工具的作用,便于 AI 理解和选择调用。
4)注意容错:和工具开发一样,要注意 MCP 服务的容错性和健壮性,捕获并处理所有可能的异常,并且返回友好的错误信息,便于客户端处理。
5)性能优化:MCP 服务端要防止单次执行时间过长,可以采用异步模式来处理耗时操作,或者设置超时时间。客户端也要合理设置超时时间,防止因为 MCP 调用时间过长而导致 AI 应用阻塞。
6)跨平台兼容性:开发 MCP 服务时,应该考虑在 Windows、Linux 和 macOS 等不同操作系统上的兼容性。特别是使用 stdio 传输模式时,注意路径分隔符差异、进程启动方式和环境变量设置。比如客户端在 Windows 系统中使用命令时需要额外添加 .cmd 后缀。
六、MCP 部署方案
由于 MCP 的传输方式分为 stdio(本地)和 SSE(远程),因此 MCP 的部署也可以对应分为 本地部署 和 远程部署,部署过程和部署一个后端项目的流程基本一致。
本地部署
适用于 stdio 传输方式。跟我们开发 MCP 的流程一致,只需要把 MCP Server 的代码打包(比如 jar 包),然后上传到 MCP Client 可访问到的路径下,通过编写对应的 MCP 配置即可启动。
举个例子,我们的后端项目放到了服务器 A 上,如果这个项目需要调用 java 开发的 MCP Server,就要把 MCP Server 的可执行 jar 包也放到服务器 A 上。
这种方式简单粗暴,适合小项目,但缺点也很明显,每个 MCP 服务都要单独部署(放到服务器上),如果 MCP 服务多了,可能会让人很崩溃。这时你不禁会想:我为什么不直接在后端项目中开发工具调用,非要新搞个项目开发 MCP 呢?
远程部署
适用于 SSE 传输方式。远程部署 MCP 服务的流程跟部署一个后端 web 项目是一样的,都需要在服务器上部署服务(比如 jar 包)并运行。
除了部署到自己的服务器之外,由于 MCP 服务一般都是职责单一的小型项目,很适合部署到 Serverless 平台上。使用 Serverless 平台,开发者只需关注业务代码的编写,无需管理服务器等基础设施,系统会根据实际使用量自动扩容并按使用付费,从而显著降低运维成本和开发复杂度。
百炼提供了详细的 使用和部署 MCP 服务指南,可以将自己的 MCP 服务部署到阿里云函数计算平台,实现 Serverless 部署。
1)首先进入 MCP 管理页面,点击创建 MCP 服务:
2)创建 MCP 服务,建议把描述写清楚。注意,安装方式必须选择 npx 或者 uvx 才可以触发函数部署,因为部署的原理就是在阿里云提供的计算资源上运行这些命令来启动服务进程。暂时不支持部署 Java 开发的 MCP,所以此处我们拿地图 MCP 演示:
编写 MCP 服务配置:
3)创建 MCP 服务成功后,可以到阿里云控制台查看函数详情:
4)之后,可以在 AI 应用中使用自定义的 MCP 服务:
💡 友情提示,如果是学习使用,建议及时删除 MCP 服务哦,会自动关联删除函数计算资源。
提交至平台
你还可以把 MCP 服务提交到各种第三方 MCP 服务市场,类似于发布应用到应用商店,让其他人也能使用你的 MCP 服务。
这样做有什么好处呢?A47lphkkDDkxdCS/rQHpdWHGIqT1c+f0rRj5gEHvdTo=
其实这个做法有点像开源,你就想想开源代码有什么好处就理解了,咱直白地说,至少有一个好处是可以提升技术影响力、收获一波流量。要不然你看大公司为啥那么快就在 MCP 服务市场上占坑呢?
当然,如果你有自己的 API 接口服务,通过提供 MCP 服务,相当于增加了用户数和调用量。比如我们前面使用的高德地图 MCP,就依赖高德地图的 API Key,每次调用都会计算费用。
怎么把 MCP 服务提交至平台呢?
其实我们不需要提前学习,因为每个平台的提交规则不同、可能也会不断变化,我们只需要在想提交服务时遵循平台的规则和标准即可。
举个例子,比如提交 MCP 到 MCP.so,直接点击右上角的提交按钮,然后填写 MCP 服务的 GitHub 开源地址、以及服务器配置,点击提交即可。
扩展思路
1)自主实现一个 MCP 服务,并通过 env 环境变量传递参数(如 API Key)
2)在自己的服务器上部署一个 SSE 传输方式的 MCP 服务
3)通过阿里云百炼平台部署一个自定义的 MCP 服务,重点是学习部署流程
4)在任何一个 MCP 服务市场上提交自己开源的 MCP 服务,注意不要暴露敏感信息
本节作业
1)完成本节代码,开发图片搜索 MCP 服务,并基于 Stdio 和 SSE 模式调用服务
2)使用 Cursor 调用 MCP 服务
3)掌握 Spring AI 开发 MCP 服务端和客户端的方法
4)理解 MCP 的调用原理,为什么客户端通过配置就能让 AI 调用 MCP 服务呢?
结语
对AI智能体,AI编程感兴趣的朋友可以在掘金私信我,或者直接加我微信:wangzhongyang1993。
后面我还会更新更多跟AI相关的文章,欢迎关注我一起学习。