跳到主要内容
Spring AI 1.1.2 集成 MCP 协议:Tavily 搜索实战 | 极客日志
Java Node.js AI java
Spring AI 1.1.2 集成 MCP 协议:Tavily 搜索实战 Spring AI 1.1.2 集成 MCP 协议实现工具调用,通过 Tavily 搜索获取实时信息。配置 stdio 连接,自动注入 ToolCallbackProvider 至 ChatClient,支持多模型动态切换与流式输出。解决 Windows 下 npx 启动及初始化超时问题。无需手写工具实现,直接复用社区已有 MCP Server,保持 Spring Boot 体系内的工程化体验。
魔尊 发布于 2026/4/9 更新于 2026/5/23 19 浏览一、MCP 是什么?为什么需要它
MCP(Model Context Protocol) 是一种让 LLM 与外部工具或资源交互的标准化协议。
MCP Server :将工具能力(如搜索、查库、读文件等)以统一格式暴露
MCP Client :连接 Server,拉取工具定义,并在需要时转发工具调用
LLM :通过 Spring AI 的 tool-calling 能力,在对话过程中自动决定是否调用工具
在 Spring AI 1.1.2 之前,要给模型接外部工具通常需要手写 @Tool 或 FunctionCallback。引入 MCP 后,你不需要自己写工具实现,直接复用社区已有的 MCP Server(目前已有数百个可用),配置即集成。
二、技术栈与版本
组件 版本 Spring Boot 3.5.9 Spring AI 1.1.2 Spring AI MCP Client Starter 1.1.2 Java 17 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 >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-bom</artifactId >
${spring-ai.version}
pom
import
org.springframework.ai
spring-ai-starter-mcp-client
${spring-ai.version}
<version >
</version >
<type >
</type >
<scope >
</scope >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
<version >
</version >
</dependency >
</dependencies >
</dependencyManagement >
3.2 AI 框架模块:引入所需依赖 <dependencies >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-model-openai</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-client</artifactId >
</dependency >
<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 是一个专为 AI 应用设计的搜索 API,每月提供一定的免费搜索额度。你需要注册获取 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:
client:
type: SYNC
request-timeout: 60s
initialized: true
stdio:
connections:
tavily:
command: cmd.exe
args:
- /c
- npx
- -y
- tavily-mcp@latest
env:
TAVILY_API_KEY: ${TAVILY_API_KEY}
配置详解 type: SYNC
Spring AI MCP Client 支持 SYNC 和 ASYNC 两种模式。如果你是传统 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 改为 npx,args 改为 ["-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 会自动完成以下工作:
启动 MCP Server 子进程
通过 MCP 协议拉取工具列表
将 MCP tools 转换成 Spring AI 的 ToolCallback
注册一个 ToolCallbackProvider Bean 到 Spring 容器
你要做的只有一件事:把 ToolCallbackProvider 挂到 ChatClient 上 。
5.1 AI 自动配置类 @Configuration
public class AiAutoConfiguration {
@Bean
public ChatMemory chatMemory () {
return MessageWindowChatMemory.builder()
.maxMessages(20 )
.build();
}
@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;
public ChatClient buildDefaultClient () {
AiModelConfig config = aiModelConfigService.getDefaultConfig();
if (config == null ) {
throw new RuntimeException ("未找到默认启用的模型配置,请先在模型配置中设置默认模型" );
}
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)
.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 发请求,模型会:
判断"天气"是实时信息 → 决定调用 tavily_search 工具
MCP Client 通过 stdio 将搜索请求发给 tavily-mcp 子进程
tavily-mcp 调用 Tavily API 获取搜索结果
搜索结果返回给模型
模型基于搜索结果生成最终回答,流式输出给用户
整个过程模型自主决策 ,你不需要写任何 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。验证方式:
7.5 工具调用超时 默认超时可能不够,尤其是 Tavily 搜索可能耗时较长。建议设置 request-timeout: 60s 或更大值。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online