Spring AI 1.1.2 集成 MCP(Model Context Protocol)实战:以 Tavily 搜索为例

本文分享在 Spring Boot 3.5 + Spring AI 1.1.2 中集成 MCP Client 的完整落地方案。通过连接 Tavily MCP Server,让大模型在对话中自动调用搜索工具获取实时信息,同时保持 Spring Boot 体系内的工程化体验。

一、MCP 是什么?为什么需要它

MCP(Model Context Protocol) 是一种让 LLM 与外部工具/资源交互的标准化协议:

  • MCP Server:将工具能力(搜索、查库、读文件等)以统一格式暴露
  • MCP Client:连接 Server、拉取工具定义,并在需要时转发工具调用
  • LLM(通过 Spring AI 的 tool-calling 能力)在对话过程中自动决定是否调用工具

在 Spring AI 1.1.2 之前,要给模型接外部工具需要手写 @ToolFunctionCallback。MCP 的好处在于:你不需要自己写工具实现,直接复用社区已有的 MCP Server(数百个可用),配置即集成

二、技术栈与版本

组件版本
Spring Boot3.5.9
Spring AI1.1.2
Spring AI MCP Client Starter1.1.2
Java17
MCP Server 示例tavily-mcp (via npx)

三、Maven 依赖配置

3.1 父工程:引入 Spring AI BOM 统一管理版本

在父 pom.xml<properties> 中声明版本号:

<properties> <spring-ai.version>1.1.2</spring-ai.version> </properties> 

<dependencyManagement> 中引入 BOM,这样所有子模块无需重复写版本号:

<dependencyManagement> <dependencies> <!-- Spring AI BOM --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> ​ <!-- MCP Client Starter --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> <version>${spring-ai.version}</version> </dependency> </dependencies> </dependencyManagement> 

3.2 AI 框架模块:引入所需依赖

我把 AI 相关能力封装在 framework-ai 模块中,核心依赖如下:

<dependencies> <!-- Spring AI - OpenAI Starter(兼容 OpenAI 协议的模型均可用) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> ​ <!-- Spring AI MCP Client Starter(关键!) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> ​ <!-- WebFlux(用于流式输出) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies> 
提示spring-ai-starter-mcp-client 会自动引入 MCP 协议实现和 stdio/SSE 传输层,不需要额外依赖。

四、配置 MCP Client:stdio 方式连接 Tavily

MCP 支持多种传输方式(stdio、HTTP/SSE 等)。这里演示最常用的 stdio 模式——Spring 应用启动一个子进程,通过标准输入输出与 MCP Server 通信。

关于 Tavily:Tavily 是一个专为 AI 应用设计的搜索 API,每月提供 1000 次免费搜索额度,足够个人开发和小规模测试使用。前往 https://www.tavily.com/ 注册登录后即可获取 TAVILY_API_KEY

application.yml(或对应 profile 配置)中添加:

spring: ai: openai: api-key: ${OPENAI_API_KEY} base-url: ${OPENAI_BASE_URL} embedding: options: model: ${OPENAI_EMBEDDING_MODEL} # MCP 客户端配置 mcp: client: type: SYNC # 同步模式(适配 Servlet 应用) request-timeout: 60s # 工具调用超时时间 initialized: true # 启动时初始化连接并拉取工具列表 stdio: connections: tavily: # 连接名称(可自定义) command: cmd.exe # Windows 下使用 cmd args: - /c - npx - -y - tavily-mcp@latest env: TAVILY_API_KEY: ${TAVILY_API_KEY} 

配置详解

**type: SYNC**Spring AI MCP Client 支持 SYNCASYNC 两种模式。如果你是传统 Servlet 应用(Spring MVC),选 SYNC;如果是全响应式应用(Spring WebFlux),选 ASYNC

**initialized: true**启动时立即初始化 MCP 连接并拉取工具列表。如果设为 false,第一次调用时才会初始化,可能导致首次响应慢或"工具未生效"的问题。强烈建议设为 ​true

**stdio.connections.tavily**定义一个名为 tavily 的 stdio 连接:

  • command + args:拼起来就是 cmd.exe /c npx -y tavily-mcp@latest,即通过 npx 拉取并运行 tavily-mcp
  • env:只注入到子进程的环境变量,API Key 不会暴露给模型
Linux/Mac 用户:将 command 改为 npxargs 改为 ["-y", "tavily-mcp@latest"] 即可。

多个 MCP Server 怎么配?

直接在 stdio.connections 下添加更多连接即可,例如同时接入搜索和文件系统:

spring: ai: mcp: client: type: SYNC initialized: true stdio: connections: tavily: command: cmd.exe args: ["/c", "npx", "-y", "tavily-mcp@latest"] env: TAVILY_API_KEY: ${TAVILY_API_KEY} filesystem: command: cmd.exe args: ["/c", "npx", "-y", "@anthropic/mcp-filesystem@latest", "D:/docs"] 

所有连接的工具会自动合并,模型可以同时使用多个 MCP Server 提供的工具。

五、核心代码:将 MCP 工具挂到 ChatClient

spring-ai-starter-mcp-client 会自动完成以下工作:

  1. 启动 MCP Server 子进程
  2. 通过 MCP 协议拉取工具列表
  3. 将 MCP tools 转换成 Spring AI 的 ToolCallback
  4. 注册一个 ToolCallbackProvider Bean 到 Spring 容器

你要做的只有一件事:把 ​ToolCallbackProvider 挂到 ​ChatClient

5.1 AI 自动配置类

@Configuration public class LanjiiAiAutoConfiguration { ​ /** * 聊天记忆(滑动窗口,保留最近 20 条消息) */ @Bean public ChatMemory chatMemory() { return MessageWindowChatMemory.builder() .maxMessages(20) .build(); } ​ /** * 向量存储(用于 RAG,非 MCP 必须) */ @Bean public VectorStore vectorStore(EmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build(); } } 

5.2 动态构建 ChatClient(核心)

@Component @RequiredArgsConstructor public class DynamicChatClientFactory { ​ private final AiModelConfigService aiModelConfigService; private final AiRolePromptService aiRolePromptService; private final ModelChatStrategyFactory modelChatStrategyFactory; private final ChatMemory chatMemory; private final ToolCallbackProvider toolCallbackProvider; // MCP 自动注入 ​ public ChatClient buildDefaultClient() { AiModelConfig config = aiModelConfigService.getDefaultConfig(); if (config == null) { throw new BizException(ResultCode.NOT_FOUND, "未找到默认启用的模型配置,请先在模型配置中设置默认模型"); } ​ // 通过策略模式动态构建 ChatModel(支持 OpenAI/DeepSeek/智谱 等) ModelChatStrategy strategy = modelChatStrategyFactory .getStrategy(config.getApiProvider()); ChatModel chatModel = strategy.buildChatModel(toModelConfig(config)); ​ String systemPrompt = "你是一个智能助手..."; ​ // 如果配置了角色提示词,优先使用 if (config.getRoleId() != null) { AiRolePrompt rolePrompt = aiRolePromptService.getById(config.getRoleId()); if (rolePrompt != null && rolePrompt.getSystemPrompt() != null) { systemPrompt = rolePrompt.getSystemPrompt(); } } ​ return ChatClient.builder(chatModel) .defaultSystem(systemPrompt) .defaultAdvisors( MessageChatMemoryAdvisor.builder(chatMemory).build() ) .defaultToolCallbacks(toolCallbackProvider) // 关键:挂载 MCP 工具 .build(); } } 

核心就一行:.defaultToolCallbacks(toolCallbackProvider)

这行代码的效果是:

  • 模型每次对话时都能"看到"所有 MCP Server 暴露的工具定义
  • 模型会根据用户问题自主决定是否调用工具
  • 工具调用的请求/响应由 Spring AI + MCP Client 自动处理

5.3 聊天 Service 与 Controller

@Service @RequiredArgsConstructor public class ChatServiceImpl implements ChatService { ​ private final DynamicChatClientFactory dynamicChatClientFactory; ​ @Override public Flux<String> chatStream(String message, String conversationId) { ChatClient chatClient = dynamicChatClientFactory.buildDefaultClient(); ​ return chatClient.prompt() .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .user(message) .stream() .content(); } } 
@RestController @RequestMapping("/chat") @RequiredArgsConstructor public class ChatBotController { ​ private final ChatService chatService; ​ @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> chatStream(String message, String conversationId) { return chatService.chatStream(message, conversationId); } } 
注意:Controller 和 Service 不需要做任何 MCP 相关的处理。MCP 工具的发现和调用完全由 ChatClient 内部完成,对业务层透明。

六、运行效果

启动应用后,你会在控制台看到 MCP 初始化日志:

i.m.client.transport.StdioClientTransport:106 - MCP server starting. i.m.client.transport.StdioClientTransport:137 - MCP server started 

然后向 /chat/stream?message=今天杭州天气怎么样&conversationId=test1 发请求,模型会:

  1. 判断"天气"是实时信息 → 决定调用 tavily_search 工具
  2. MCP Client 通过 stdio 将搜索请求发给 tavily-mcp 子进程
  3. tavily-mcp 调用 Tavily API 获取搜索结果
  4. 搜索结果返回给模型
  5. 模型基于搜索结果生成最终回答,流式输出给用户

整个过程模型自主决策,你不需要写任何 if-else 判断"什么时候该搜索"。

七、常见问题与踩坑记录

7.1 Windows 下 stdio 连接必须用 cmd.exe /c

在 Windows 环境下,npx 实际是 .cmd 脚本,不能直接作为 command 启动。必须通过 cmd.exe /c npx ... 来执行,否则会报进程启动失败。

7.2 initialized: true 不可省略

如果不设置 initialized: true,MCP Client 不会在启动时主动初始化,可能导致:

  • 第一次对话时工具列表为空,模型不会调用任何工具
  • 初始化失败时没有明确的错误日志

7.3 SYNC vs ASYNC 如何选择

  • 项目中同时使用了 spring-boot-starter-web(Servlet)→ 选 SYNC
  • 纯 WebFlux 响应式应用 → 选 ASYNC
  • 混合使用(如本项目引入了 webflux 做流式但主体是 Servlet)→ 选 SYNC

7.4 确保 Node.js/npx 可用

stdio 方式使用 npx 运行 MCP Server,前提是服务器上已安装 Node.js。验证方式:

node -v # 确保有输出,建议 >= 18 npx -v # 确保 npx 可用 

7.5 工具调用超时

默认超时可能不够,尤其是 Tavily 搜索可能耗时较长。建议设置 request-timeout: 60s 或更大值。

八、源码与在线体验

完整源码https://gitee.com/leven2018/lanjii/tree/master

欢迎 Star ⭐ 和 Fork,项目包含本文涉及的所有代码(MCP 集成、多模型动态切换、RAG 知识库等)。

在线体验http://106.54.167.194/admin/index

可以直接登录后台,在 AI 对话模块中体验 MCP 工具调用的实际效果。

Read more

ROS 2从入门到精通系列(十六):自主导航机器人 - 系统架构与SLAM

ROS 2从入门到精通系列(十六):自主导航机器人 - 系统架构与SLAM 构建完整的自主导航系统,从建图到导航的端到端实现。 引言 自主导航是机器人最经典的应用之一。它涉及: * 感知:LIDAR扫描、里程计 * 建图:SLAM建立环境地图 * 规划:生成无碰撞路径 * 控制:执行运动命令 本篇将从0到1构建一个完整的导航系统。 一、自主导航系统架构 1.1 完整的系统架构 硬件层 控制模块 运动控制 PID Control 安全监督 Emergency Stop 规划模块 全局规划 Dijkstra/A* 局部规划 DWA/TEB 可行性检查 Feasibility Check 感知模块 扫描匹配 Scan Matching 里程计 Odometry

OpenClaw上身机器人,AI不仅能帮订外卖,还能替你跑腿了!

OpenClaw上身机器人,AI不仅能帮订外卖,还能替你跑腿了!

手把手教你一键部署OpenClaw(Clawdbot),2分钟搞定! 过去这些年,AI大多时候还只是待在屏幕里,帮人写写字、画画图或者跑个自动脚本。但最近 OpenClaw 生态彻底爆火,两个基于它的开源项目直接打破了虚拟与现实的界限。这消息一传出来,全球搞机器人和AI的极客们都坐不住了。 就在2月23号旧金山举行的 SF OpenClaw 黑客松上,ROSClaw 项目拿下了冠军。Irvin 团队搞出了一个中间连接层,把现在最火的开源 AI Agent 平台 OpenClaw 直接插到了真实的机器人硬件上。刚拿完奖,团队就大方地宣布把项目开源了。 手把手教你一键部署OpenClaw(Clawdbot),2分钟搞定! 手把手教你一键部署OpenClaw(Clawdbot),2分钟搞定! 具体是怎么做到的呢?他们通过智能插件把 OpenClaw 接到了机器人操作系统(ROS 2)上,还利用 WebRTC 技术实现了超低延迟的安全连接。这意味着你在地球任何一个角落,都能远程操控那些兼容 ROS 的机器人。AI

后仿之SDF 反标Warning的描述和解决

在后仿中SDF的反标log中Error是必须要解决的,但是Warning有时候可能并不会影响到实际的内容,而是工具严格的检查得到的一些警告,因此可能就需要我们仔细的来甄别是否warning需要被解决;针对此,将平时看到的一些warning进行整理,帮助之后解决这些问题: 1. SDFCOM_UHICD:Up-hierarchy Interconnect Delay ignored      这个warning是指将hier间的delay放在device delay上体现,可以不用处理;对跨层次的端口标注INTERCONNECT delay时出现该warning,在层次铺平之后是不会有问题的。 2. SDFCOM_IWSBA:INTERCONNECT will still be annotated     也不用处理,delay实际上也是反标了。     vcs是无法识别assign语句代表的是单纯的连线还是作为一个device存在,所以当vcs检测到对assign语句反标INTERCONNECT delay时会报出该警告,但是依然会将INTERCONNECT delay标注。

CVPR 2026 Oral实测|YOLO-DRONE:无人机低空巡检的“性能天花板”,小目标召回率狂升39%(清华团队力作,电力部署实操全解析)

CVPR 2026 Oral实测|YOLO-DRONE:无人机低空巡检的“性能天花板”,小目标召回率狂升39%(清华团队力作,电力部署实操全解析)

前言:作为长期深耕无人机计算机视觉落地的算法工程师,我始终认为,无人机低空巡检场景的核心痛点,从来不是“模型精度多高”,而是“能否适配复杂飞行工况下的实战需求”。无论是电力巡检中的导线断股、绝缘子破损,还是安防巡检中的人员遗留、设备异常,这些目标往往尺寸极小、飞行过程中受风速扰动导致画面模糊、目标尺度动态变化,传统YOLO系列模型要么小目标漏检严重,要么抗扰动能力弱,要么实时性不足,根本无法满足工业级巡检的落地要求。 2026年CVPR大会上,清华大学团队提出的YOLO-DRONE模型惊艳全场,成功入选Oral(口头报告),成为低空巡检领域唯一入选的单阶段检测模型。这款专为无人机低空巡检设计的多尺度动态感知模型,创新性融合自适应尺度感知头(ASPH)与风速补偿特征对齐模块,彻底解决了传统模型“小目标漏检、抗扰动差、实时性不足”三大痛点——在UAV-DT无人机巡检专用数据集上,小目标召回率直接提升39%,同时支持1080p@45FPS实时处理,目前已正式部署于国内某省级电力巡检系统,实现输电线路的自动化巡检落地。 我第一时间获取了YOLO-DRONE的技术论文及开源代码,搭建了模拟无