☰
Spring AI 1.1.2 集成 MCP 实战:Tavily 搜索接入 TaoToken 统一通道
2026/10/8 6:11:03 网站建设 项目流程

1. 为什么要在 Spring AI 1.1.2 里折腾 MCP 和 Tavily

如果你正在用 Spring Boot 写 AI 应用,大概率遇到过这种局面:项目里同时接了 OpenAI、DeepSeek、通义千问好几个模型,每个模型的 Key 散落在不同的配置文件里,Base URL 各不相同,测试环境切到生产环境要改一堆东西。更麻烦的是,当你想让模型具备联网搜索能力时,又得单独写一套工具调用逻辑,代码越堆越厚。

Spring AI 1.1.2 引入的 MCP(Model Context Protocol)支持,恰好能解决这两个痛点。MCP 是一种让大模型与外部工具、资源交互的标准化协议,你可以把它理解成"AI 世界的 USB 接口"——只要工具实现了 MCP Server,任何支持 MCP Client 的框架都能即插即用。Tavily 是一个专为 AI 应用设计的搜索 API,每月有 1000 次免费额度,非常适合做搜索增强问答。

这篇内容聚焦一条完整链路:Spring Boot 3.5 + Spring AI 1.1.2 通过 MCP 接入 Tavily 搜索,同时把模型调用的 endpoint 统一指向 TaoToken 通道,解决多模型 Key 分散、Base URL 切换繁琐的问题。适合已经写过 Spring Boot、想快速给 AI 应用加上联网搜索能力的后端开发者。跟着做下来,你会得到一份可复制的application.yml、一个 MCP 客户端 Bean 定义,以及把 endpoint 改到 TaoToken 后的连通性验证步骤。

先说清楚 MCP 的工作方式。MCP Server 把工具能力(搜索、查库、读文件等)以统一格式暴露出来;MCP Client 负责连接 Server、拉取工具定义,并在需要时转发工具调用;LLM 通过 Spring AI 的 tool-calling 能力,在对话过程中自动决定是否调用工具。在 Spring AI 1.1.2 之前,给模型接外部工具需要手写@Tool注解或FunctionCallback,现在直接复用社区已有的 MCP Server,配置即集成。

TaoToken 在这里扮演的角色是统一通道。它兼容 OpenAI 协议,提供模型对话、Coding Plan、API Keys 管理等能力。你不需要为每个模型单独维护一套 Base URL 和 Key,把 Spring AI 的 OpenAI Starter 指向 TaoToken 的 API 地址,再通过模型 ID 区分不同模型即可。这样 MCP 负责工具扩展,TaoToken 负责模型接入,两者职责清晰。

2. 前置准备:依赖、版本与 TaoToken 通道配置

动手之前先把版本对齐。Spring AI 1.1.2 对 Spring Boot 版本有要求,建议用 3.5.x。Java 版本至少 17。MCP Server 这边用tavily-mcp,通过npx拉起,所以机器上要有 Node.js,建议 18 以上。

先看 Maven 依赖。父工程的pom.xml里声明版本号,然后引入 Spring AI BOM 统一管理:

<properties> <spring-ai.version>1.1.2</spring-ai.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> <version>${spring-ai.version}</version> </dependency> </dependencies> </dependencyManagement>

AI 框架模块里引入实际使用的依赖。这里用 OpenAI Starter,因为 TaoToken 兼容 OpenAI 协议:

<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 传输层,不需要额外依赖。

接下来是 TaoToken 通道的准备。访问官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册后,进入控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建好 Key 之后,模型调用的 Base URL 统一用 https://taotoken.net/api ,注意这个地址不加 UTM 参数。

Tavily 这边,去 tavily.com 注册登录,拿到TAVILY_API_KEY。免费额度每月 1000 次,个人开发和小规模测试够用。

环境变量建议这样组织,避免 Key 硬编码进代码:

export TAOTOKEN_API_KEY="sk-你的TaoToken密钥" export TAVILY_API_KEY="tvly-你的Tavily密钥" export OPENAI_BASE_URL="https://taotoken.net/api"

Windows 下用set或者直接在 IDE 的 Run Configuration 里配。把 Key 放在环境变量里,application.yml通过${}引用,这样不同环境切换只改环境变量,配置文件不用动。

3. 可复制配置:application.yml 与 MCP 客户端 Bean

这一节是核心,配置写对了后面基本就通了。先看application.yml的完整片段:

spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: ${OPENAI_BASE_URL} chat: options: model: gpt-4o-mini temperature: 0.7 embedding: options: model: text-embedding-3-small 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表示同步模式,适配传统 Servlet 应用;如果你的项目是全响应式 WebFlux,改成ASYNC。request-timeout: 60s是工具调用超时时间,Tavily 搜索有时耗时较长,默认值可能不够。initialized: true非常重要,它让应用启动时立即初始化 MCP 连接并拉取工具列表;如果设为false,第一次调用时才初始化,容易出现首次响应慢或"工具未生效"的问题。

stdio.connections.tavily定义了一个名为 tavily 的连接。command加args拼起来就是cmd.exe /c npx -y tavily-mcp@latest,通过 npx 拉取并运行 tavily-mcp。env里注入的TAVILY_API_KEY只对子进程可见,不会暴露给模型。

Linux 或 Mac 用户把command改成npx,args改成["-y", "tavily-mcp@latest"]即可。多个 MCP Server 直接在stdio.connections下继续加,比如同时接入文件系统:

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 提供的工具。

然后是 Java 侧的 Bean 定义。spring-ai-starter-mcp-client会自动完成启动 MCP Server 子进程、拉取工具列表、把 MCP tools 转换成 Spring AI 的ToolCallback、注册ToolCallbackProviderBean 这几件事。你要做的只有把ToolCallbackProvider挂到ChatClient上。

先看自动配置类:

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

核心是动态构建ChatClient的工厂类:

@Component @RequiredArgsConstructor public class DynamicChatClientFactory { private final ChatMemory chatMemory; private final ToolCallbackProvider toolCallbackProvider; public ChatClient buildDefaultClient(ChatModel chatModel) { String systemPrompt = "你是一个智能助手,遇到实时信息需求时主动调用搜索工具。"; return ChatClient.builder(chatModel) .defaultSystem(systemPrompt) .defaultAdvisors( MessageChatMemoryAdvisor.builder(chatMemory).build() ) .defaultToolCallbacks(toolCallbackProvider) .build(); } }

关键就一行.defaultToolCallbacks(toolCallbackProvider)。这行代码让模型每次对话时都能"看到"所有 MCP Server 暴露的工具定义,模型根据用户问题自主决定是否调用工具,工具调用的请求和响应由 Spring AI 加 MCP Client 自动处理。

ChatModel的构建这里简化了,实际项目里你可以通过策略模式支持多个模型。用 TaoToken 通道时,构建OpenAiChatModel的配置如下:

OpenAiApi openAiApi = OpenAiApi.builder() .baseUrl("https://taotoken.net/api") .apiKey(System.getenv("TAOTOKEN_API_KEY")) .build(); OpenAiChatOptions options = OpenAiChatOptions.builder() .model("gpt-4o-mini") .temperature(0.7) .build(); ChatModel chatModel = OpenAiChatModel.builder() .openAiApi(openAiApi) .defaultOptions(options) .build();

换模型只改.model()里的 ID,Base URL 和 Key 不用动,这就是统一通道的价值。

4. 验证请求:curl 连通性与日志断言

配置写完别急着写业务代码,先验证链路通不通。分两步:先验 TaoToken 通道,再验 MCP 工具是否挂载成功。

第一步,用 curl 直接打 TaoToken 的 API,确认 Key 和 Base URL 没问题:

curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "你好"}], "stream": false }'

返回里能看到choices数组和content字段,说明通道正常。如果返回 401,检查 Key 是否复制完整、有没有多余空格。如果返回local proxy failed之类的错误,检查 Base URL 是不是写成了https://taotoken.net/api,注意结尾不要多加/v1,OpenAI Starter 会自动拼接路径。

第二步,启动 Spring Boot 应用,观察控制台日志。MCP 初始化成功会打印类似这样的内容:

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

如果看到MCP server started,说明 tavily-mcp 子进程拉起来了。接着确认工具列表是否拉取成功,可以在启动类里加一段临时日志:

@Bean public CommandLineRunner logTools(ToolCallbackProvider provider) { return args -> { ToolCallback[] callbacks = provider.getToolCallbacks(); System.out.println("已加载 MCP 工具数量: " + callbacks.length); for (ToolCallback cb : callbacks) { System.out.println("工具名: " + cb.getToolDefinition().name()); } }; }

正常应该看到tavily_search之类的工具名。如果数量为 0,说明 MCP 连接没初始化成功,回到第 5 节排查。

第三步,发一个真实请求测试搜索增强。写个简单的 Controller:

@RestController @RequestMapping("/chat") @RequiredArgsConstructor public class ChatController { private final DynamicChatClientFactory factory; private final ChatModel chatModel; @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> chatStream(String message, String conversationId) { ChatClient client = factory.buildDefaultClient(chatModel); return client.prompt() .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .user(message) .stream() .content(); } }

启动后请求:

curl -N "http://localhost:8080/chat/stream?message=今天杭州天气怎么样&conversationId=test1"

模型会先判断"天气"是实时信息,决定调用tavily_search工具,MCP Client 通过 stdio 把搜索请求发给 tavily-mcp 子进程,子进程调用 Tavily API 拿到结果,结果返回给模型,模型基于搜索结果生成最终回答并流式输出。整个过程模型自主决策,你不需要写任何 if-else 判断什么时候该搜索。

日志里能看到工具调用的痕迹,类似Tool execution request和Tool execution response。如果模型直接回答而没有调用工具,检查defaultToolCallbacks是否挂上、initialized是否为true。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

这一节把实际踩过的坑列出来,对照报错找原因。

401 Unauthorized。最常见的是 Key 问题。先确认TAOTOKEN_API_KEY环境变量在当前 shell 里能echo出来。如果用的是 IDE,检查 Run Configuration 的 Environment variables 有没有配。还有一种情况是 Key 复制时带了换行或空格,用curl单独测一下就能定位。TaoToken 的 Key 在 API Keys 页面管理,如果怀疑 Key 失效,去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 重新生成一个。

local proxy failed。这个报错通常出现在 Base URL 配置不对的时候。检查spring.ai.openai.base-url是不是https://taotoken.net/api,不要写成https://taotoken.net/api/v1,OpenAI Starter 会自己拼/v1/chat/completions。另外确认网络能正常访问该地址,用curl -I https://taotoken.net/api看返回状态码。

Error reading choices。这个报错说明请求发出去了,但响应体解析失败。常见原因是模型 ID 写错了,TaoToken 通道不认这个模型名。去模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 确认可用的模型 ID,然后改spring.ai.openai.chat.options.model。还有一种可能是响应被截断,检查request-timeout是否太短。

OAuth 相关报错。如果你用的是需要 OAuth 的 MCP Server,stdio 模式下通常不需要 OAuth,但 SSE 模式可能需要。Tavily 的 MCP Server 用 API Key 就够了,不需要 OAuth。如果看到 OAuth 报错,先确认你连的是哪个 Server,是不是配置里混入了其他连接。

Windows 下进程启动失败。npx在 Windows 下实际是.cmd脚本,不能直接作为command启动,必须通过cmd.exe /c npx ...。报错通常是Cannot run program "npx"。按第 3 节的配置写就没问题。

工具列表为空。检查initialized是否为true。如果设为false,第一次调用时才初始化,启动日志里看不到工具数量。另外确认 Node.js 和 npx 可用:

node -v npx -v

版本建议 18 以上。如果 npx 拉取 tavily-mcp 很慢,可以先用npx -y tavily-mcp@latest手动跑一次,把包缓存下来。

SYNC 还是 ASYNC。项目里同时用了spring-boot-starter-web(Servlet)就选SYNC;纯 WebFlux 响应式应用选ASYNC;混合使用(比如引入 webflux 做流式但主体是 Servlet)也选SYNC。选错了会出现工具调用阻塞或响应异常。

工具调用超时。Tavily 搜索偶尔慢,默认超时可能不够。设request-timeout: 60s或更大。如果还是超时,检查网络到 Tavily API 的连通性。

排查的时候有个技巧:把 Spring AI 和 MCP 的日志级别调成 DEBUG,能看到完整的请求响应过程:

logging: level: org.springframework.ai: DEBUG io.modelcontextprotocol: DEBUG

这样工具调用的入参和出参都会打出来,定位问题快很多。

6. 把通道固定下来:长期编码与 Agent 场景的接入建议

配置跑通之后,建议把 TaoToken 通道的接入方式固定成项目规范,避免每个开发者各写一套。核心原则是 Base URL 和 Key 走环境变量,模型 ID 走配置中心或数据库,代码里只读不写死。

对于长期做编码辅助或 Agent 开发的场景,可以考虑用 Coding Plan。它面向持续性的编码任务和 Agent 调用,在额度管理和通道稳定性上比按次调用更合适。具体可以看 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。如果你的场景是偶尔验证模型效果,用模型对话页面就够了;如果是接入到 CI 或自动化流程里,Coding Plan 更省心。

接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各语言的调用示例和参数说明。Claude Code 相关的接入可以参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite ,如果你用 Claude Code 做开发,这个页面有专门的配置说明。

回到 Spring AI 这边,有几个工程化建议。第一,把ChatModel的构建封装成工厂,模型 ID 从配置读取,这样换模型不用改代码。第二,MCP 连接配置放在application.yml里,但 Key 用环境变量注入,不要把 Key 提交到 Git。第三,给工具调用加监控,记录每次调用的工具名、耗时、是否成功,方便排查线上问题。第四,request-timeout根据实际工具调整,搜索类工具给足时间,本地文件类工具可以短一些。

最后说一个实际经验:MCP 工具挂载后,模型的决策质量跟 system prompt 有关系。如果发现模型该搜索的时候不搜索,可以在 system prompt 里明确写"遇到实时信息、新闻、天气、股价等问题时,优先调用搜索工具"。如果发现模型滥用搜索,就加一句"对于常识性问题直接回答,不需要搜索"。这个平衡需要根据你的业务场景调。

整套链路跑通后,你得到的是一个可扩展的架构:MCP 负责工具生态,想加新工具就加一个 connection;TaoToken 负责模型通道,想换模型就改一个 ID。两者解耦,维护成本低。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询