我们上一节知道了怎么创建和定义可调用的工具但是你会发现这些工具的使用都是在我们的AI应用内部的我们对工具的改动都相当于是改了我们的应用工具的调用怎么看都像是一种扩展功能一种可随时插拔的插件式的东西所以就有了 MCPMCP 的定义MCP 翻译过来就是模型上下文协议Model Co‏‏‏‏ntext 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 S‏‏‏‏erver 主要用来‏‏‏‏为客户端提供各种工⁡⁡⁡⁡具、资源和功能支持的和客户端一样它也‏‏‏‏可以通过多种方式进行数据传输比如‍‍‍‍ Stdio 标准输入 / 输出、‎‎‎‎基于 Servlet / WebF‏‏‏‏lux / WebMVC 的 SS⁡⁡⁡⁡E 传输满足不同应用场景最简单的使用一些AI工具像是trae都提供了自定义添加MCP服务的功能大多数 MCP 服务都支持基于 NPX 工具运行添加几行配置就行这里补充一下 Node.js 中 npx 工具的使用npx 是 Node.js 生态里“临时运行一个包里的命令”的工具先通过 npx 找这个 npm 包本地没装时会先下载到缓存而不是使用的项目里然后直接把这个包里的可执行命令跑起来若要是想在自己的项目中使用 MCP也可以说是自己项目添加一个MCP客户端了1.首先引入依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-spring-boot-starter/artifactId version1.0.0-M6/version /dependency2.在 resources 目录下新建 mcp-servers.json 配置{ mcpServers: { amap-maps: { command: npx, args: [ -y, amap/amap-maps-mcp-server ], env: { AMAP_MAPS_API_KEY: 改成你的 API Key } } } }3.修改 Spr‏‏‏‏ing 配置文件spring: ai: mcp: client: stdio: // 本地运行使用stdio模式且要指定配置文件位置 servers-configuration: classpath:mcp-servers.json4.添加代码新增一个利用 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 groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-spring-boot-starter/artifactId /dependency2.配置连接用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 ] } } }直接写‏‏‏‏入配置文件‍这‍种‍方‍式同时‎支持‎ s‎td‎‏io ‏和 S‏S⁡E 连⁡‏接方式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: - --port8080 env: API_KEY: your-api-key3.使用服务// 和 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.typeASYNCtypeASYNC 说的是 底层客户端是异步实现但你自己的代码如果还是“发出请求后立刻等结果”那 整体业务看起来仍然是同步的就相当于是两个关口业务代码到底层客户端可以用代码控制是用排队还是等着底层客户端到MCP服务用配置可以控制排队和等着好接下来才是重点MCP 服务端开发图片搜索服务示例用Pexels网站常规搜索接口当然你要先去pexels网站注册并获取自己的API密钥才可以免费调用1.引入依赖spring-ai-starter-mcp-server-webmvc提供基于 Spring MVC 的 SSE 传输和可选的 stdio 传输一般建议引入这个dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId /dependency2.配置文件编写主配置文件 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: false3.编写服务类其实和之前的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 ListString searchMediumImages(String query) { // 设置请求头包含API密钥 MapString, String headers new HashMap(); headers.put(Authorization, API_KEY); // 设置请求参数仅包含query可根据文档补充page、per_page等参数 MapString, 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.stdiotrue, -Dspring.main.web-application-typenone, -Dlogging.pattern.console, -jar, 服务端jar包的位置image-search-mcp-server-0.0.1-SNAPSHOT.jar ], env: {} } } }最后说说题外话同样是可以实现工具的调用skills出来后MCP好像都听不见声了事实就是这样实现方式上skills只是一个文件夹有时候里面还只有提示词连程序脚本都没有可使用起来人们就是偏向skills要我说skills相比MCP有一个巨大的优点就是简单没错就是一些没有技术背景的人都完全可以开源一个自己的skills这就是一个巨大的生态啊当然关于skills与MCP的区别以及是否真的会取代的问题不会那么简单也许我可以专门出一个文章好好聊聊