AI大模型应用开发实战流程-MCP协议(Model Context Protocol)
本文我以Pexels图片搜索服务为例,详细实战演示了MCP服务端和客户端的开发流程,包括配置、工具注册和单元测试。
MCP协议
什么是MCP呢?MCP是Model Context Protocol模型上下文协议,是一种开放标准,不是一种技术,MCP目的是增强AI与外部系统交互的能力,接入别人提供的服务来实现更多的功能,统一标准,降低使用和理解成本,打造服务生态,造福广大开发者。
那为什么MCP这么火,而工具调用却没那么火?这是因为工具调用是面向我们程序员,而MCP是大家都能直接接入使用。
SDK的三层架构:客户端/服务器层、会话层、传输层,客户端和服务端之间传输可以通过两种方式一种是标准的输入/输出Stdio模式,在本地就像在终端小黑框客户端和服务器聊的有来有回。一种是远程HTTP请求的SSE模式,SSE作用是让服务器持续不断的给客户端发消息。

MCP 架构的主要参与者是:
MCP 主机:协调和管理一个或多个 MCP 客户端的 AI 应用程序
MCP 客户端:维护与 MCP 服务器的连接并从 MCP 服务器获取上下文供 MCP 主机使用的组件
MCP 服务器:向 MCP 客户端提供上下文的程序
例如:Visual Studio Code 充当 MCP 主机。当 Visual Studio Code 建立与 MCP 服务器(如 Sentry MCP 服务器)的连接时,Visual Studio Code 运行时会实例化一个 MCP 客户端对象,该对象用于维护与 Sentry MCP 服务器的连接。 当 Visual Studio Code 随后连接到另一个 MCP 服务器(例如本地文件系统服务器)时,Visual Studio Code 运行时会实例化一个额外的 MCP 客户端对象来维护此连接,从而保持一对一 MCP 客户端与 MCP 服务器的关系。
Spring AI MCP开发模式
Spring AI在MCP官方Java SDK的基础上额外封装了一层,提供了和Spring Boot整合的SDK,支持客户端和服务端的普通调用和响应式调用。那让我们接下来看看如何使用Spring AI开发MCP客户端和服务端吧!
开发MCP客户端:可以参考一下Spring AI官方文档的MCP Client Boot Starters,提供了两种客户端SDK:1、Standard MCP Client,核心启动器提供了Stdio和SSE支持, 2、WebFlux Client,基于WebFlux的SSE传输实现,一般用第一种就好了。
步骤:引入依赖、配置连接、使用服务
步和异步客户端类型,还可以自定义定制客户端行为
开发MCP服务端:同理,参考Spring AI官方文档的MCP Server Boot Starters,提供了3种SDK,有Stdio、WebMVC SSE、WebFlux SSE,支持非响应式和响应式编程,一般建议引spring-ai-starter-mcp-server-webmvc,要用到时候看官方文档就好了。
入依赖、配置服务、开发服务
其他特性:提供工具、资源管理、提示词管理、根目录变更处理
MCP开发实战(图片搜索服务)
使用Pexels图片资源网站的API(免费!免费!)来构建图片搜索服务。
注册登录,然后点击右上角的三个点选择图片和视频API来生成Pexels API密钥,先复制一下密钥,然后我们就可以来使用Pexels来实现图片搜索服务。
MCP服务端开发
在dog-ai-agent根目录下新建一个dog-image-search-mcp-server模块,选用Maven和jdk21,下一步引入lombok然后自己选择一下springboot版本然后创建,创建完建议大家单独的打开这个项目,不要在当前的目录中打开它,因为要去搜索资源的话在当前的子目录获取到的路径是不正确的。右击创建的模块,选择Open In的explorer,然后会弹出文件的窗口,选择刚刚创建的文件右击选择idea打开,然后选择New Window。
刚才在创建时引入了lombok,那首先我们在pom.xml来引入Hutool的依赖和我们要用的MCP的WebMVC SSE的依赖:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.38</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
<version>1.0.0</version>
</dependency>
注:引入WebMVC SSE依赖后,会自动注册SSE端点,包括消息和SSE端点,能够供客户端调用
然后我们要在resource目录下来编写两套服务端配置文件,分别实现Stdio和SSE模式传输,首先将原本的application.properties改为application-stdio.yml进行Refactor,然后将生成的一行代码删掉,编写以下代码:
spring:
ai:
mcp:
server:
name: dog-image-search-mcp-server
version: 0.0.1
type: SYNC
# stdio
stdio: true
# stdio
main:
web-application-type: none
banner-mode: off
接着继续搞SSE的,在resource目录下新建一个application-sse.yml,编写以下代码:
spring:
ai:
mcp:
server:
name: dog-image-search-mcp-server
version: 0.0.1
type: SYNC
# sse
stdio: false
补充:可能你的左边目录包很多红色,这不是报错,这是因为没有添加到Git管理,我们右击dog-image-search-mcp-server找到Git选择Add就可以了。
为了方便灵活切换这两套配置,我们在resource目录下新建一个application.yml,编写以下配置代码:
spring:
application:
name: dog-image-search-mcp-server
profiles:
active: stdio
server:
port: 8127
然后在dogimagesearchmcpserver下新建一个tools包,再在包下新建一个ImageSearchTool类,那我们要如何编写搜索图片的java代码呢,可以进入Pexels的右上角三个点的图片和视频API,然后点击文档,找到Photos下的Search for Photos,将这个文档丢给AI帮我们生成基于Hutool工具库的HTTP工具类的自动搜索图片功能的java代码,然后复制粘贴到我们的ImageSearchTool类,代码如下:
@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());
}
}
记得要将API key替换成自己的,然后生成对应的单元测试进行测试一下,代码如下:
@SpringBootTest
class ImageSearchToolTest {
@Resource
private ImageSearchTool imageSearchTool;
@Test
void searchImage() {
String result = imageSearchTool.searchImage("dog");
Assertions.assertNotNull(result);
}
}
接着在启动类定义ToolCallbackProvider Bean注册工具,代码如下:
@SpringBootApplication
public class DogImageSearchMcpServerApplication {
public static void main(String[] args) {
SpringApplication.run(DogImageSearchMcpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) {
return MethodToolCallbackProvider.builder()
.toolObjects(imageSearchTool)
.build();
}
}
然后呢,客户端是怎么基于本地模式调用我们的MCP服务呢?是客户端会先找到我们MCP的配置,找到启动MC服务P的命令,开一个子进程去运行这个服务。所以呢,我们接下来要在Maven中去package给我们的本地MCP服务打个包,接着打包成功后会在target目录下看到这个jar包,客户端运行时候就是运行这个jar包。
注意:这边可能又会报错不支持发行版21,我们的文档开头有说如何解决,如果忘了可以滑到开头去看看。如果还不行,就在Setting里面的Build Tool里的Maven下的Runner将JRE改为21应该就可以了。
那MCP服务端开发完成,咱们来开发MCP客户端吧。
MCP客户端开发
在根项目中开发客户端,调用刚才创建的图片搜索服务,首先引入MCP客户端MCP Client依赖(刚刚已经引入过了),接着来编写我们的配置,打开resource目录下的mcp-servers.json,里面有我们前面配置过的高德地图,记得去ignore密钥噢,不要把密钥提交到git仓库。我们继续,mcp-servers.json最终代码如下:
{
"mcpServers": {
"amap-maps": {
"command": "npx.cmd",
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "your-api-key"
}
},
"dog-image-search-mcp-server": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dspring.main.web-application-type=none",
"-Dlogging.pattern.console=",
"-jar",
"dog-image-search-mcp-server/target/dog-image-search-mcp-server-0.0.1-SNAPSHOT.jar"
],
"env": {}
}
}
}
然后在test里的TravelAppTest编写图片搜索服务的单元测试,将之前地图的测试注释掉进行测试,代码如下:
@Test
void doChatWithMcp() {
String chatId = UUID.randomUUID().toString();
// 测试地图 MCP
// String message = "我在福建福州东街口,请帮我找个约会地点";
// String answer = travelApp.doChatWithMcp(message, chatId);
// Assertions.assertNotNull(answer);
// 测试图片搜索 MCP
String message = "帮我搜索一些萨摩耶狗在游泳池里玩耍的图片";
String answer = travelApp.doChatWithMcp(message, chatId);
Assertions.assertNotNull(answer);
}
}
到此,我们思考一个问题,你要开发这个图片搜索的功能,如果是你你会选择开发工具调用还是开发MCP服务呢?其实很简单,能用工具调用就用工具调用,引入MCP依赖再开发挺麻烦的。
下面我们来试试SSE连接方式,首先修改MCP服务端的配置文件,就是dog-image-search-mcp-server的application.yml中将stdio改为sse,然后在启动类中以Debug启动。然后修改客户端的配置文件,要将stdio的注释掉:
mcp:
client:
sse:
connections:
server1:
url: http://localhost:8127
# stdio:
# servers-configuration: classpath:mcp-servers.json
运行doChatWithMcp测试一下。
MCP部署方案有两种:本地部署和远程部署
MCP安全问题
1、权限失控风险
用户过度授权(如允许AI“读写所有文件”或“访问全部网络”)。一旦AI被恶意诱导或存在逻辑漏洞,将利用这些宽泛权限造成数据泄露、系统破坏等严重损失。
2、服务器端攻击风险
MCP连接的第三方工具服务器可能“作恶”或有漏洞。恶意服务器可提供虚假数据、窃取AI发送的敏感信息(如数据库凭证),或利用服务器漏洞发起进一步攻击。
3、提示词注入与越权操作
攻击者通过精心构造的输入,诱导AI绕过正常逻辑,滥用其已获得的工具权限。例如,让一个被授权总结邮件的AI,利用文件写入工具去删除系统文件,执行非预期的高危操作。
4、供应链与依赖风险
MCP工具(Server)通常来自开源社区或第三方,可能包含恶意代码或存在未修补的漏洞。广泛使用的工具若被污染或存在漏洞,会导致大规模的安全事件,影响所有集成该工具的应用。
5、数据泄露与隐私暴露
AI通过MCP访问大量敏感数据源(邮件、数据库、API),处理不当极易导致信息泄露。AI可能在回复中完整输出敏感信息;对话记录被窃取;或上下文残留敏感数据并在后续对话中无意泄露。
更多推荐



所有评论(0)