
1. 为什么要在 Spring AI 1.1.2 里折腾 MCP 和 Tavily如果你正在用 Spring Boot 写 AI 应用大概率遇到过这种局面项目里同时接了 OpenAI、DeepSeek、通义千问好几个模型每个模型的 Key 散落在不同的配置文件里Base URL 各不相同测试环境切到生产环境要改一堆东西。更麻烦的是当你想让模型具备联网搜索能力时又得单独写一套工具调用逻辑代码越堆越厚。Spring AI 1.1.2 引入的 MCPModel 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.version1.1.2/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId version${spring-ai.version}/version /dependency /dependencies /dependencyManagementAI 框架模块里引入实际使用的依赖。这里用 OpenAI Starter因为 TaoToken 兼容 OpenAI 协议dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency /dependenciesspring-ai-starter-mcp-client会自动引入 MCP 协议实现和 stdio/SSE 传输层不需要额外依赖。接下来是 TaoToken 通道的准备。访问官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建好 Key 之后模型调用的 Base URL 统一用 https://taotoken.net/api 注意这个地址不加 UTM 参数。Tavily 这边去 tavily.com 注册登录拿到TAVILY_API_KEY。免费额度每月 1000 次个人开发和小规模测试够用。环境变量建议这样组织避免 Key 硬编码进代码export TAOTOKEN_API_KEYsk-你的TaoToken密钥 export TAVILY_API_KEYtvly-你的Tavily密钥 export OPENAI_BASE_URLhttps://taotoken.net/apiWindows 下用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-mcplatest 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-mcplatest通过 npx 拉取并运行 tavily-mcp。env里注入的TAVILY_API_KEY只对子进程可见不会暴露给模型。Linux 或 Mac 用户把command改成npxargs改成[-y, tavily-mcplatest]即可。多个 MCP Server 直接在stdio.connections下继续加比如同时接入文件系统stdio: connections: tavily: command: cmd.exe args: [/c, npx, -y, tavily-mcplatest] env: TAVILY_API_KEY: ${TAVILY_API_KEY} filesystem: command: cmd.exe args: [/c, npx, -y, anthropic/mcp-filesystemlatest, 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()里的 IDBase 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注意结尾不要多加/v1OpenAI 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 节排查。第三步发一个真实请求测试搜索增强。写个简单的 ControllerRestController RequestMapping(/chat) RequiredArgsConstructor public class ChatController { private final DynamicChatClientFactory factory; private final ChatModel chatModel; GetMapping(value /stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString 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今天杭州天气怎么样conversationIdtest1模型会先判断天气是实时信息决定调用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_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 重新生成一个。local proxy failed。这个报错通常出现在 Base URL 配置不对的时候。检查spring.ai.openai.base-url是不是https://taotoken.net/api不要写成https://taotoken.net/api/v1OpenAI Starter 会自己拼/v1/chat/completions。另外确认网络能正常访问该地址用curl -I https://taotoken.net/api看返回状态码。Error reading choices。这个报错说明请求发出去了但响应体解析失败。常见原因是模型 ID 写错了TaoToken 通道不认这个模型名。去模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 确认可用的模型 ID然后改spring.ai.openai.chat.options.model。还有一种可能是响应被截断检查request-timeout是否太短。OAuth 相关报错。如果你用的是需要 OAuth 的 MCP Serverstdio 模式下通常不需要 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-mcplatest手动跑一次把包缓存下来。SYNC 还是 ASYNC。项目里同时用了spring-boot-starter-webServlet就选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_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。如果你的场景是偶尔验证模型效果用模型对话页面就够了如果是接入到 CI 或自动化流程里Coding Plan 更省心。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言的调用示例和参数说明。Claude Code 相关的接入可以参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 如果你用 Claude Code 做开发这个页面有专门的配置说明。回到 Spring AI 这边有几个工程化建议。第一把ChatModel的构建封装成工厂模型 ID 从配置读取这样换模型不用改代码。第二MCP 连接配置放在application.yml里但 Key 用环境变量注入不要把 Key 提交到 Git。第三给工具调用加监控记录每次调用的工具名、耗时、是否成功方便排查线上问题。第四request-timeout根据实际工具调整搜索类工具给足时间本地文件类工具可以短一些。最后说一个实际经验MCP 工具挂载后模型的决策质量跟 system prompt 有关系。如果发现模型该搜索的时候不搜索可以在 system prompt 里明确写遇到实时信息、新闻、天气、股价等问题时优先调用搜索工具。如果发现模型滥用搜索就加一句对于常识性问题直接回答不需要搜索。这个平衡需要根据你的业务场景调。整套链路跑通后你得到的是一个可扩展的架构MCP 负责工具生态想加新工具就加一个 connectionTaoToken 负责模型通道想换模型就改一个 ID。两者解耦维护成本低。