当前位置: 首页 > news >正文

第七章 MCP协议

代码仓库地址:https://github.com/Liucc-123/ai-agent

官方文档:https://docs.spring.io/spring-ai/reference/index.html

项目目标:通过项目实战掌握AI 大模型应用开发的常见知识,包括有:掌握AI 应用平台使用、AI 大模型接入、开发框架(Spring AI + LangChain4j)、本地部署、Prompt 工程、多模态特性、RAG 知识库、工具调用、MCP 服务开发、智能体原理和开发、服务化部署等技术。

本节重点

学习 AI 应用开发的؜高级特性 —— MCP 模型上下文协议,打通‍ AI 与外部服务的边界。先学习 MCP 的‌几种使用方式,然后基于 Spring AI ⁡框架实战开发 MCP 客户端与服务端,帮你掌‏握 MCP 的架构原理和最佳实践。

具体内容包括:

  • MCP 必知必会
  • MCP 的 3 种使用方式
  • Spring AI MCP 开发模式
  • Spring AI MCP 开发实战 - 图片搜索 MCP
  • MCP 开发最佳实践
  • MCP 部署方案
  • MCP 安全问题

一、需求分析

目前的我们的AI应用已经具备了恋爱知识问答及工具调用的能力,现在再加一个使用的功能:根据另一半的位置找到合适的约会地点

根据之前的已经学的知识点,可以想到下面的实现思路:

  • 最直接的方式,让模型就基于自己已有的知识进行回复。但这种肯定不行,因为模型的知识库不具备时效性,某些商家店铺也许前几年还在,但现在或许已经倒闭了呢。
  • 利用RAG知识库,我们开发者可以将另一半相关附件的广场、娱乐场所等信息整理成文档输入为AI模型,让AI模型基于知识库进行回复。这种方式肯定还是具备一定的时效性问题。
  • 通过网络搜索工具,我们开发一个根据位置查询附近店铺的工具,可以通过第三方API(高德API)来实现,这样得到的信息时效性和准确性会更高。

显然,第三种方式是最好的,但是既然都已经调用API了,还需要手动再开发一遍工具吗?能不能让API直接对接给AI,让AI模型直接与第三方API服务交互。

其实,现在已经有了,就是今天的主角 – MCP协议。


二、MCP必知必会

什么是MCP?

MCP(Model Context Protocol,模型上下文协议)是一种开放标准,使AI模型能够以标准化的方式与外部工具和资源进行交互,让AI模型能够访问最新数据、执行复杂操作,并于现有的系统进行集成。

根据官方定义,MCP是一个开放协议,它规范了应用程序向LLMs提供上下文的方式。可以将MCP想象成是AI应用的USB-C接口,就像USB-C提供了标准化的方式来连接你的设备和各种外设和配件一样,MCP也提供了标准化的方式来连接你的AI模型和不同的数据源和工具。MCP使你能够基于LLMs来构建Agent和复杂的工作流,并将你的模型与世界连接起来。

上面说的可能比较抽象,下面举一些例子来说明MCP的作用。

首先是增强AI的能力,通过MCP协议,AI模型可以很轻松的对接第三方服务,比如高德地图、网页搜索等,从而增强了AI的能力,让AI有了更多具备时效性的知识库,有了“做”的能力。

其次,MCP是一种协议或标准,它本身不提供具体的服务,只是一种规范,服务提供商和服务使用者都要遵循这种规范。标准的好处就是统一大家的理解,就像HTTP协议一样,POST请求就知道你要向服务器端发送数据了,GET请求就知道你是想获取某些数据。200状态码就知道是请求成功了,404就知道指定资源不存在。这些标准能够有效降低开发者的理解成本。

此外,有了标准,就能够打造服务生态,像Maven仓库、Docker镜像仓库一样,MCP也可以基于标准构建MCP服务市场。

这些就是MCP的三大作用:

  • 增强AI的能力
  • 统一标准,降低使用和理解成本
  • 可以打造服务生态,造福广大开发者

MCP架构

1、宏观架构

MCP的核心是“客户端-服务器端”架构,其中客户端主机可以连接到多个MCP服务器。客户端主机是指希望连接MCP服务的应用程序,比如Claude Code or Claude Desktop 。

2、SDK3层架构

Java MCP 实现遵循三层架构:

在这里插入图片描述

  • 客户端/服务器层:McpClient 处理客户端操作,而 McpServer 管理服务器端协议操作。两者都使用 McpSession 进行通信管理。
  • 会话层(McpSession):通过 DefaultMcpSession 实现管理通信模式和状态。
  • 传输层(McpTransport):处理 JSON-RPC 消息的序列化和反序列化,支持多种传输实现。比如Stdio标准输入输出传输和HTTP SSE远程传输。

3、MCP客户端

MCP 客户端是模型上下文协议(MCP)架构中的关键组件,负责建立和管理与 MCP 服务器的连接。它实现了协议的客户端部分,处理:

  • 协议版本协商以确保与服务器兼容
  • 能力协商以确定可用功能
  • 消息传输和 JSON-RPC 通信
  • 工具发现和执行
  • 资源访问和管理
  • 提示系统交互
  • 可选功能:
    • 根管理
    • 采样支持
  • 同步和异步操作
  • 传输选项:
    • 基于 stdio 的传输用于进程间通信
    • 基于 Java HttpClient 的 SSE 客户端传输
    • WebFlux SSE 客户端传输用于响应式 HTTP 流

关于传输,如果MCP客户端和服务器端都在同一台服务器中,可以采用基于Stdio的通信传输方式;

如果MCP客户端和服务器端不在同一台服务器上,采用Http Client的SSE远程通信方式。

4、MCP服务器端

MCP S؜erver 也是整‍个 MCP 架构的‌关键组件,主要用来⁡为客户端提供各种工‏具、资源和功能支持。

它负责处理客户端؜的请求,包括解析协议、提供工具‍、管理资源以及处理各种交互‌信息。同时,它还能记录日志、发送通⁡知,并且支持多个客户端同时连接‏,保证高效的通信和协作。

和客户端一样,它也؜可以通过多种方式进行数据传输,比如‍ Stdio 标准输入 / 输出、‌基于 Servlet / WebF⁡lux / WebMVC 的 SS‏E 传输,满足不同应用场景。

这种设计使؜得客户端和服务端完‍全解耦,任何语言开‌发的客户端都可以调⁡用 MCP 服务。‏如图:

MCP核心概念

MCP的功能除了工具调用还有很多其他的功能官方文档

服务器向客户端提供以下任何功能:

  • 资源:用户或 AI 模型使用的上下文和数据
  • 提示:为用户提供模板消息和工作流程
  • 工具:AI 模型执行的功能

客户端可以向服务器提供以下功能:

  • 采样:服务器发起的代理行为和递归 LLM 交互
  • 根目录:服务器发起的对 uri 或文件系统边界的查询以进行操作
  • 引出:服务器发起的从用户处请求额外信息

如果要开发MCP服务,主要关注前三个概念,其中,工具是重中之重!

MCP官方文档中也提到,大多数客户端客户端也仅支持工具调用能力:

所以在下面的章节中,我们主要关注学习tools。


三、使用MCP

本节我们将实战 3 种使用 MCP 的方式:

  • 云平台使用 MCP
  • 软件客户端使用 MCP
  • 程序中使用 MCP

无论是哪种使用方式,原理都是类似的,而且有 2 种可选的使用模式:本地下载 MCP 服务端代码并运行(类似引入了一个 SDK),或者 直接使用已部署的 MCP 服务(类似调用了别人的 API)。

MCP服务大全

目前已经有؜很多 MCP 服务‍市场,开发者可以在‌这些平台上找到各种⁡现成的 MCP 服‏务:

  • MCP.so:较为主流,提供丰富的 MCP 服务目录
  • GitHub Awesome MCP Servers:开源 MCP 服务集合
  • 阿里云百炼 MCP 服务市场
  • Spring AI Alibaba 的 MCP 服务市场
  • Glama.ai MCP 服务

其中,绝大多؜数 MCP 服务市场仅‍提供本地下载 MCP ‌服务端代码并运行的使用⁡方式,毕竟部署 MCP‏ 服务也是需要成本的。

下面实战这三种使用方式。

云平台使用MCP

以阿里云百炼为例,参考 官方 MCP 文档,我们可以直接使用官方预置的 MCP 服务,或者部署自己的 MCP 服务到阿里云平台上。

如图,官方提供了很多现成的 MCP 服务:

现在,进入之前我们已经创建的智能体应用中,点击左侧的添加MCP,然后开通高德MCP服务。随即就能看到高德MCP底下的12个工具都已经添加进来。

输入prompt进行测试:

我的另一半居住在西安雁塔区,请帮我找到 5公里内合适的约会地点。

测试效果如下:

说明:从执行过程中可以看到,MCP就是一遍一遍的进行工具调用,直至完成任务目标。根据用户提示词中提取到关键信息“西安雁塔区”,调用工具“maps_geo”,获取到“西安雁塔区”的相关位置信息。

第二步,模型拿到位置信息后,再次调用工具“maps_around_search”,获取到指定位置附近的适合约会的店铺。

软件客户端使用MCP

环境准备

不同的客户端软件对 MCP 支持程度不同,可以在 官方文档 中查看各客户端支持的特性。

我这里以字节的产品Trae进行演示如何使用 M‌CP 服务。由于没有现成的⁡部署了 MCP 服务的服务‏器,我们采用本地运行的方式。

首先安装本؜地运行 MCP 服‍务需要用到的工具,‌具体安装什么工具取⁡决于 MCP 服务的‏配置要求。

比如我们到 MCP 市场 找到 高德地图 MCP,发现 Server Config 中定义了使用 npx命令行工具来安装和运行服务端代码:

大多数 MCP 服务都支持基于 NPX 工具运行,所以推荐安装 Node.js 和 NPX,去 官网 傻瓜式安装即可。

从配置中我们发现,使用地图 MCP 需要 API Key,我们可以到 地图开放平台 创建应用并添加 API Key:

Trae接入MCP

在Trae的智能体对话窗口中打开设置,进入MCP设置页:

点击手动添加:

复制粘贴刚刚在MCP市场中找到的高德MCP服务的json配置:

MCP应用后边出现对勾了,说明服务启动成功了。

测试使用MCP

上面环境准备好了之后就可以开始测试了。还是下面这个prompt:

我的另一半居住在西安雁塔区,请帮我找到 5公里内合适的约会地点。

观察效果,发现AI模型会多次调用MCP直至完成任务。

最后生成的响应内容还是不错的:

但我们由此也能看到MCP的调用成本还是很大的,因为它一次任务要消耗这么多的token及第三方API KEY的调用量,全是钱啊~


如果要使用؜其他软件客户端,接入‍ 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)添加依赖

<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 服务:mcp-servers.json

{"mcpServers": {"amap-maps": {"command": "npx","args": ["-y","@amap/amap-maps-mcp-server"],"env": {"AMAP_MAPS_API_KEY": "改成你的 API Key"}}}
}

需要注意的是,在windows环境下,需要给命令添加.cmd后缀(如npx.cmd),否则会报找不到明林的错误

3)修改SpringBoot的配置文件,添加MCP配置项。由于我们的mcp是本地服务,因此采用stdio模式,配置代码如下:

spring:ai:mcp:client:stdio:servers-configuration: classpath:mcp-servers.json # 指定mcp配置文件的位置

这样一来,MCP客户端在启动时,会额外启动一个子进程来运行MCP服务,从而实现工具调用。

4)修改LoveApp的代码,新增一个通过MCP完成对话的方法。通过自动注入的ToolCallbackProvider来获取MCP服务所提供的所有工具,将ToolCallbackProvider实例传递给chatClient,示例代码如下:

// ======================使用MCP===========================
@Resource
private ToolCallbackProvider toolCallbackProvider;
public String doChatWithMCP(String message, String chatId) {// 使用工具ChatResponse chatResponse = 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 = chatResponse.getResult().getOutput().getText();log.info("doChatWithTools content: {}", content);return content;
}

测试代码:

@Test
void doChatWithMCPTest(){String chatId = UUID.randomUUID().toString();String message = "我的另一半居住在西安雁塔区,请帮我找到 5公里内合适的约会地点。";String answer = loveApp.doChatWithMCP(message, chatId);Assertions.assertNotNull(answer);
}

通过日志拦截器可以看到,mcp的本质就是工具调用,并不是让AI主动去调用MCP服务,而是告诉AI模型MCP服务都有哪些工具,让AI模型自己决定要调用哪些工具来完成任务,然后告诉给应用程序,由应用程序具体的执行工具,并将工具调用结果返回给AI模型,最终由AI模型生成最终表达,返回给调用方。流程图如下:


四、SpringAI MCP开发模式

SpringAI在MCP官方Java SDK的基础上封装了一层SpringBoot整合的SDK,支持客户端和服务器端的普通调用和响应式调用。下面分别学习如何使用 Spring ‏AI 开发 MCP 客户端和服务端。

MCP客户端开发

客户端开发主要基于 Spring AI MCP Client Boot Starter,能够自动完成客户端的初始化、管理多个客户端实例、自动清理资源等。

1、引入依赖

Sprin؜g AI 提供了 2‍ 种客户端 SDK,‌分别支持非响应式和响⁡应式编程,可以根据需‏要选择对应的依赖包:

  • 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、配置连接

引入依赖后؜,需要配置与服务器‍的连接,Sprin‌g AI 支持两种⁡配置方式:

1)直接写؜入配置文件,这种方‍式同时支持 STDIO 和 SSE 连⁡接方式。

spring:ai:mcp:client:enabled: truename: my-mcp-clientversion: 1.0.0request-timeout: 30stype: SYNCsse:connections:server1:url: http://localhost:8080stdio:connections:server1:command: /path/to/serverargs:- --port=8080env: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 A‍I 会自动注入一些‌ MCP 相关的 B⁡ean。

1)如果你؜想完全自主控制 M‍CP 客户端的行为‌,可以使用 Mcp⁡Client Be‏an,支持同步和异步:

// 同步客户端
@Autowired
private List<McpSyncClient> mcpSyncClients;// 异步客户端
@Autowired
private List<McpAsyncClient> mcpAsyncClients;

查看 Mc؜pSyncClien‍t 的源码,发现提供‌了很多和 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 来 定制客户端行为,比如设置请求超时时间、设置文件系统根目录的访问范围、自定义事件处理器、添加特定的日志处理逻辑。

官方提供的示例代码如下,简单了解即可:

@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {@Overridepublic void customize(String serverConfigurationName, McpClient.SyncSpec spec) {// 自定义请求超时配置spec.requestTimeout(Duration.ofSeconds(30));// 设置此客户端可访问的根目录URIspec.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、引入依赖

SpringAI提供了三种MCP服务器端SDK,分别支持非响应式和响应式编程,可以根据需要选择对应的依赖包:

  • spring-ai-mcp-server-spring-boot-starter:适用于stdio传输,不需要额外的Web依赖
  • spring-ai-starter-mcp-server-webmvc:基于 Spring MVC 的 HTTP 传输,也同时支持STDIO传输(通过设置 spring.ai.mcp.server.stdio=true启用),一般建议引入这个依赖。
  • 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-serverversion: 1.0.0stdio: truetype: SYNC # 同步

开发 SSE 服务,配置如下:

# 使用 spring-ai-starter-mcp-server-webmvc
spring:ai:mcp:server:name: webmvc-mcp-serverversion: 1.0.0type: SYNC # 同步sse-message-endpoint: /mcp/message  # SSE 消息端点路径sse-endpoint: /sse                  # SSE 端点路径

如果要开发响应式(异步)服务,配置如下:

# 使用 spring-ai-starter-mcp-server-webflux
spring:ai:mcp:server:name: webflux-mcp-serverversion: 1.0.0type: 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为一个工具类。

@Service
public class WeatherService {@Tool(description = "获取指定城市的天气信息")public String getWeather(@ToolParameter(description = "城市名称,如北京、上海") String cityName) {// 实现天气查询逻辑return "城市" + cityName + "的天气是晴天,温度22°C";}
}

然后在SpringBootApplication启动时注册一个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)资源管理:可以给客户端提供静态文件或动态生成的内容

@Bean
public 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)提示词管理:可以向客户端提供模板化的提示词

@Bean
public 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)根目录؜变更处理:当客户端‍的根目录权限发生变‌化时,服务端可以接⁡收通知

@Bean
public BiConsumer<McpSyncServerExchange, List<McpSchema.Root>> rootsChangeHandler() {return (exchange, roots) -> {logger.info("Registering root resources: {}", roots);};
}

大家只需要了解上面؜这些特性即可,无需记忆和编写代码。通‍过这些特性,大家应该也会对 MCP ‌有进一步的了解。简单来说,通过这套标⁡准,服务端能向客户端传递各种各样不同‏类型的信息(资源、工具、提示词等)。


MCP工具类

SpringAI提供了MCP工具类来辅助开发,用于MCP和ToolCallback之间的相互转换。

也就是说,我们可以将之前已经开发的工具直接转换为MCP服务,极大提供了代码的复用性。


五、MCP开发实战 - 图片搜索服务

下面通过开发一个图片搜素MCP服务来实战掌握MCP核心开发。

MCP服务器端开发

可以使用 Pexels 图片资源网站的 API 来构建图片搜索服务。

1)首先在Pexels网站创建一个API KEY:

记住密钥一定要保存好,千万不要“开源”出去了!

2)在ai-agent项目下新建一个module,名称为ai-image-search-mcp

3)添加依赖

给项目引入必要的依赖,包括有lombok、hutool工具类及SpringAI MCP服务端依赖。

<dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId><version>1.0.0-M6</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.38</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>

引入了依赖后spring-ai-starter-mcp-server-webmvc,支持基于SpringMVC的SSE传输和可选的STDIO传输

4)在resources目录下编写MCP服务端配置文件,我们分别编写基于stdio和sse的配置文件。

stdio配置文件(application-stdio.yml):

spring:ai:mcp:server:name: ai-image-search-mcp-serverversion: 0.0.1type: SYNC# stdiostdio: true# stdiomain:web-application-type: none # 因为是基于本地的标准输入输出,不需要webbanner-mode: off # 因为控制台的任何输出都会传递给AI模型,因此不必要的字符就不需要打印在控制台了,避免传递一些噪音给到AI。

sse配置文件(application-sse.yml):

spring:ai:mcp:server:name: yu-image-search-mcp-serverversion: 0.0.1type: SYNC# sse 启用SSE模式时,需要关闭stdio模式stdio: false

然后编写主配置文件(application.yaml),方便灵活切换stdio和sse模式:

spring:application:name: ai-image-search-mcp-serverprofiles:active: stdio
server:port: 8337

5)编写搜索服务类工具,在tools包下新建ImageSearchTool类,用@Tool注解标注工具方法,@ToolParam注解标注方法参数。

参考官方API文档,通过AI来辅助实现工具逻辑,生成的具体的代码如下:

@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());}
}

6)编写单元测试

@SpringBootTest
class ImageSearchToolTest {@Resourceprivate ImageSearchTool imageSearchTool;@Testvoid searchImage() {String s = imageSearchTool.searchImage("Orange");Assertions.assertNotNull(s);}
}

经过测试,发现工具最后会返回和用户搜索关键词相关的图片链接集合

7)在ai-image-search-mcp项目的主启动类下,将工具注册到ToolCallbackProvider中,便于客户端发现工具。

@Bean
public ToolCallbackProvider imageSearchToolCallbackProvider(ImageSearchTool imageSearchTool) {return MethodToolCallbackProvider.builder().toolObjects(imageSearchTool).build();
}

8)最后一个步骤,通过maven将该项目打成jar包,因为客户端是基于stdio本地连接访问。

MCP客户端开发

接下来,在ai-agent项目下开发MCP客户端,来调用刚才自开发的MCP服务。

1)引入MCP客户端依赖,这个我们之前应该是引入过了,依赖如下:

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId><version>1.0.0-M6</version>
</dependency>

2)先测试stdio传输模式。在mcp-servers.json配置文件中添加图片搜索服务相关配置,通过Java命令执行我们刚刚打的jar包:

"ai-image-search-mcp-server": {"command": "java","args": ["-Dspring.ai.mcp.server.stdio=true","-Dspring.main.web-application-type=none","-Dlogging.pattern.console=","-jar","ai-image-search-mcp/target/ai-image-search-mcp-0.0.1-SNAPSHOT.jar"],"env": {}
}

3)测试使用,这里可以直接复用之前的单元测试,因为doChatWithMCP()方法已经是通过ToolCallbackProvider注入工具的,底层会自动发现我们的图片搜索工具。

@Test
void doChatWithMCPTest(){String chatId = UUID.randomUUID().toString();String message = "帮我搜索一些能够哄我女朋友开心的图片。";String answer = loveApp.doChatWithMCP(message, chatId);Assertions.assertNotNull(answer);
}

通过DEBUG可以发现,工具列表多了一个,现在有13个,第一个就是我们自开发的图片搜索工具,控制台能看到最终AI给出的最终响应。

4)接下来测试SSE传输方式,首先改写MCP服务端的配置,激活sse配置文件

spring:application:name: ai-image-search-mcp-serverprofiles:active: sse
server:port: 8337

以DEBUG模式启动ai-image-search-mcp项目的主启动类;

5)在MCP客户端,修改配置文件切换到SSE模式:

spring:application:name: ai-agentprofiles:active: localai:mcp:client:sse:connections:server1:url: http://localhost:8337# stdio模式
#        stdio:
#          servers-configuration: classpath:mcp-servers.json

6)运行单元测试

因为是SSE传输模式,所以只能发现我们图片搜索服务这一个工具,之前12个工具都是高德MCP以本地npx的运行起来的,所以检测不到这12个工具。

可以看到,工具请求也是成功的到了MCP服务端这里

这是AI模式最终的生成表达,符合我们的预期!


六、MCP开发最佳实践

已经学会如؜何开发 MCP 服‍务端和客户端后,我‌们来学习一些 MC⁡P 开发的最佳实践‏。

1)慎用 MCP:MCP 不是银弹,其本质就是工具调用,只不过统一了标准、更容易共享而已。如果我们自己开发一些不需要共享的工具,完全没必要使用 MCP,可以节约开发和部署成本。我个人的建议是 能不用就不用,先开发工具调用,之后需要提供 MCP 服务时再将工具调用转换成 MCP 服务即可。

2)传输模式选择:؜Stdio 模式作为客户端子进程运行‍,无需网络传输,因此安全性和性能都更‌高,更适合小型项目;SSE 模式适合⁡作为独立服务部署,可以被多客户端共享‏调用,更适合模块化的中大型项目团队。

3)明确服务:设计 MCP 服务时,要合理划分工具和资源,并且利用 @Tool@ToolParam注解尽可能清楚地描述工具的作用,便于 AI 理解和选择调用。

4)注意容错؜:和工具开发一样,要注意‍ MCP 服务的容错性和‌健壮性,捕获并处理所有可⁡能的异常,并且返回友好的‏错误信息,便于客户端处理。

5)性能优化:MC؜P 服务端要防止单次执行时间过长,‍可以采用异步模式来处理耗时操作,或‌者设置超时时间。客户端也要合理设置⁡超时时间,防止因为 MCP 调用时‏间过长而导致 AI 应用阻塞。

6)跨平台兼容性:开发 MCP 服务时,应该考虑在 Windows、Linux 和 macOS 等不同操作系统上的兼容性。特别是使用 stdio 传输模式时,注意路径分隔符差异、进程启动方式和环境变量设置。比如客户端在 Windows 系统中使用命令时需要额外添加 .cmd 后缀。


http://www.dtcms.com/a/304782.html

相关文章:

  • Wndows Docker Desktop-Unexpected WSL error错误
  • 报告研读——80页数据资产化实践指南报告-2024【附全文阅读】
  • 天铭科技×蓝卓 | “1+2+N”打造AI驱动的汽车零部件行业智能工厂
  • 为什么全景渲染更耗时?关键因素解析
  • 3D游戏引擎的“眼睛“:相机系统深度揭秘与技术实现
  • 【ARM】FPU,VFP,ASE,NEON,SVE...是什么意思?
  • Synopsys:消息管理
  • 2025年1中科院1区顶刊SCI-投影迭代优化算法Projection Iterative Methods-附完整Matlab免费代码
  • Vivado常用IP
  • GaussDB 数据库架构师修炼(十) 性能诊断常用视图
  • Rust基础-part8-模式匹配、常见集合
  • 嵌入式开发问题:warning: #177-D: variable “key“ was declared but never referenced
  • 2025年Solar应急响应公益月赛-7月笔记ing
  • Generative AI in Game Development
  • Class24AlexNet
  • STM32——HAL库
  • HBase、MongoDB 和 Redis 的区别详解
  • 图片查重从设计到实现(7) :使用 Milvus 实现高效图片查重功能
  • Redis内存使用耗尽情况分析
  • 达梦数据库DM用户管理-三权分立与四权分立,用户创建与维护,用户与模式的关系,用户相关权限
  • Spring Boot 简单接口角色授权检查实现
  • Rust 实战二 | 开发简易版命令行工具 grep
  • uv工具使用记录(Linux系统)
  • 【C++算法】75.优先级队列_数据流中的第 K 大元素
  • React 中获取当前路由信息
  • Android权限机制详解:保障用户隐私与应用安全
  • pytorch格式转华为昇腾的om格式
  • 移动语义和右值引用有什么关系?
  • Prometheus-1--什么是Prometheus?
  • Leetcode——475. 供暖器