1. 为什么要把 Spring AI MCP 服务端的 endpoint 改到 TaoToken
如果你正在用 Spring AI 写 MCP 服务端,大概率会遇到一个很现实的问题:本地跑通工具调用之后,下一步就得接一个真正能用的模型通道。默认配置里,Spring AI 会去连各家模型厂商的原生地址,你得分别准备 OpenAI Key、Claude Key,甚至还要为不同模型维护不同的 base-url。项目一多,Key 管理就变成一团乱麻。
我这次要解决的就是这件事:把 Spring AI MCP 服务端的模型调用 endpoint 统一改到 TaoToken,用一个 Key、一个 Base URL 覆盖多种模型,服务端代码几乎不用动,只改配置。TaoToken 在这里扮演的角色是统一的 API 通道,它对外暴露 OpenAI 兼容的接口,所以 Spring AI 里所有走 OpenAI 协议的客户端都能直接指过去。
先说清楚适用人群:如果你在做 Spring AI MCP 服务端的本地开发与联调,需要让服务端在收到 MCP 请求后,能把工具调用和模型推理串起来,并且希望少折腾 Key,那这篇就是给你写的。MCP 服务端的核心职责是解析请求、路由到 LLM、执行工具调用、组装响应,而模型这一层换成 TaoToken 之后,你依然保留完整的工具执行链路,只是出口地址变了。
需要提前说明的是,MCP 服务端本身有两种角色:一种是纯工具服务端,只暴露@McpTool给客户端调用,不直接调模型;另一种是带 LLM 路由的服务端,会在内部调用模型来决定是否触发工具。这篇聚焦后者,也就是服务端内部要发模型请求的场景,因为只有这种场景才涉及 endpoint 的替换。如果你的服务端只是暴露工具、由客户端去调模型,那 endpoint 配置在客户端侧,思路是一样的。
我实测下来,整个改造的核心就三处:依赖里确认 OpenAI starter 存在、application.yml里把 base-url 和 api-key 指向 TaoToken、然后验证一次工具调用请求能正常返回。下面按顺序拆开讲。
2. TaoToken 前置准备:Key、Base URL 与模型 ID 三件套
在动 Spring AI 配置之前,先把 TaoToken 这边的三样东西准备好,后面配置里会反复用到。所谓三件套,就是 Base URL、API Key、Model ID,缺一不可。
Base URL 用https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI 兼容的根路径。Spring AI 的 OpenAI 客户端会在它后面拼接/v1/chat/completions之类的路径,所以你在配置里填的就是这个根。
API Key 需要到控制台里创建。打开 API Keys 页面,新建一个 Key,复制出来保存好。这个 Key 只在创建时完整显示一次,丢了就得重建。建议本地开发单独建一个 Key,方便随时吊销,不要和线上共用。
Model ID 取决于你想调哪个模型。TaoToken 支持多种模型,你在模型对话页面可以先试跑一下,确认某个模型 ID 能正常出结果,再写进配置。常见的比如gpt-4o-mini、claude-3-5-sonnet这类命名,具体以你账号下可用的为准。我建议先用一个便宜的小模型把链路跑通,确认没问题再换成主力模型。
这里有个容易踩的坑:很多人以为 Base URL 要填到/v1这一层,其实不用。Spring AI 的OpenAiApi默认会把/v1拼进去,你填https://taotoken.net/api就行。如果你填成https://taotoken.net/api/v1,最后请求路径会变成/api/v1/v1/chat/completions,直接 404。这个我在联调时踩过,排查了半天。
另外,Key 的存放方式建议用环境变量,不要硬编码进application.yml提交到仓库。本地可以用 IDE 的运行配置注入,或者用.env配合 spring-dotenv。下面配置示例里我会写成占位符,你替换成自己的即可。
准备好这三样,就可以进 Spring AI 的配置了。如果你还没有 Key,先去控制台建一个,再回来继续。
3. 可复制的 application.yml 与 MCP Server 端配置片段
这一节是重点,直接给可复制的配置。先看依赖,pom.xml里除了 MCP 服务端 starter,还要有 OpenAI 的 starter,因为模型调用走的是 OpenAI 兼容协议。
<dependencies> <!-- MCP 服务端,WebFlux 版本,支持 SSE / Streamable-HTTP --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webflux</artifactId> </dependency> <!-- OpenAI 兼容客户端,用于调用 TaoToken --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> </dependencies>然后是application.yml,这是核心。注意base-url和api-key两处指向 TaoToken,model填你验证过的 Model ID。
spring: main: banner-mode: off ai: # 模型通道指向 TaoToken openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.7 # MCP 服务端配置 mcp: server: name: my-mcp-server version: 0.0.1 type: ASYNC protocol: STREAMABLE capabilities: tool: true resource: true prompt: true completion: true几个关键点解释一下。spring.ai.openai.base-url就是我们要改的 endpoint,指向 TaoToken 的 API 根地址。api-key用环境变量注入,避免明文。spring.ai.openai.chat.options.model是默认模型,MCP 服务端内部发起模型请求时会用它。
MCP 服务端这边,type: ASYNC适合响应式应用,配合 WebFlux。protocol: STREAMABLE是现在推荐的传输方式,取代了老的 SSE。如果你还在用 SSE,把protocol改成SSE即可,但新项目建议直接上 Streamable-HTTP。
如果你用的是settings.xml或者 IDE 的配置方式,思路一样,把 base-url 和 key 填对就行。下面再给一个等价的 JSON 形式,方便你在某些需要 JSON 配置的场景里对照:
{ "spring.ai.openai.base-url": "https://taotoken.net/api", "spring.ai.openai.api-key": "你的 TaoToken Key", "spring.ai.openai.chat.options.model": "gpt-4o-mini", "spring.ai.mcp.server.protocol": "STREAMABLE", "spring.ai.mcp.server.type": "ASYNC" }配置写完后,服务端启动时 Spring AI 会自动装配OpenAiChatModel,MCP 服务端在需要模型推理时会用这个 Bean。你不需要手动 new 任何客户端,自动配置会处理。
有一点要提醒:如果你同时引入了多个模型 starter,比如又引了 Anthropic 的,可能会有 Bean 冲突。本地联调阶段建议只保留 OpenAI 这一个,确认链路通了再按需加。
4. 验证一次工具调用请求:从启动到拿到响应
配置写完,接下来验证服务端能不能正常响应。分两步:先确认服务端起来了,再发一次真实的工具调用请求。
先写一个最简单的工具,用@McpTool注解暴露出去:
@Component public class CalculatorTools { @McpTool(name = "add", description = "Add two numbers together") public int add( @McpToolParam(description = "First number", required = true) int a, @McpToolParam(description = "Second number", required = true) int b) { return a + b; } }启动类保持标准写法,自动配置会扫描到这个 Bean 并注册:
@SpringBootApplication public class McpServerApplication { public static void main(String[] args) { SpringApplication.run(McpServerApplication.class, args); } }启动命令,把 Key 通过环境变量传进去:
export TAOTOKEN_API_KEY=你的Key mvn spring-boot:run启动成功后,日志里会看到 MCP 服务端注册的工具列表,以及监听端口。默认 Streamable-HTTP 的端点是/mcp,SSE 的话是/sse。确认端口后,用 curl 发一次工具调用请求:
curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "add", "arguments": { "a": 3, "b": 5 } } }'如果一切正常,你会拿到类似这样的响应:
{ "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "8" } ] } }看到8就说明工具调用链路通了。但注意,这一步验证的是 MCP 协议层,还没验证模型通道。要验证 TaoToken 的 endpoint 是否生效,需要触发一次会走模型的请求,比如让服务端内部调用模型来决定调用哪个工具。你可以在服务端加一个测试入口,或者用 MCP 客户端发一个需要模型推理的 prompt。
更直接的验证方式是单独测一下 OpenAI 客户端。写一个 CommandLineRunner,启动时发一条消息:
@Bean CommandLineRunner testModel(OpenAiChatModel chatModel) { return args -> { String reply = chatModel.call("用一句话介绍你自己"); System.out.println("模型返回: " + reply); }; }如果控制台打印出模型回复,说明base-url和api-key配置正确,TaoToken 通道打通。如果这里报 401,就是 Key 的问题;如果报连接失败,就是 base-url 写错了。这一步能把模型通道和 MCP 协议层分开验证,排查起来更快。
5. 本篇常见错误排查:401、local proxy failed 与 reading choices
联调阶段最常见的几个报错,我按出现频率排一下,对照着查。
第一个是 401 Unauthorized。报错信息通常是401 Unauthorized: {"error":{"message":"Invalid API key"}}。原因基本是 Key 没传进去或者传错了。检查环境变量TAOTOKEN_API_KEY是否在当前 shell 生效,echo $TAOTOKEN_API_KEY看一下。如果是 IDE 里跑,检查运行配置的 Environment variables 有没有加。还有一种情况是 Key 复制时带了空格,去掉首尾空格。
第二个是local proxy failed或者连接被拒绝。这个多半是 base-url 写错,或者本地网络到 TaoToken 的连通性有问题。先确认base-url是https://taotoken.net/api,没有多余路径。然后用 curl 直接测一下:
curl https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY"能返回模型列表就说明网络和 Key 都没问题,问题在 Spring 配置侧。
第三个是reading choices相关的解析错误,比如Cannot deserialize value of type ... from Array value或者choices字段读不到。这通常是响应格式和客户端预期不一致。Spring AI 的 OpenAI 客户端期望标准 OpenAI 响应结构,如果 TaoToken 返回的是兼容格式,一般不会有问题。遇到这个先确认你用的模型 ID 是 chat 类型,不是 embedding 或别的类型。另外检查有没有重复引入多个 starter 导致客户端串了。
第四个是 OAuth 或鉴权头相关的报错。Spring AI 默认用Authorization: Bearer <key>,如果你在配置里额外加了自定义 header,可能覆盖掉默认的。检查application.yml里有没有spring.ai.openai.chat.options下误加了 header 配置。
第五个是 MCP 服务端启动报 Bean 冲突,比如OpenAiChatModel有多个候选。这是引入了多个模型 starter 导致的,本地联调先只留 OpenAI 一个。
排查顺序建议:先 curl 测 TaoToken 通不通,再测 Spring 里模型客户端通不通,最后测 MCP 工具调用通不通。三层分开,定位很快。
6. 把通道固定下来:后续接入与长期使用建议
链路跑通之后,建议把配置固化下来,避免每次联调都重新折腾。几个实用做法。
Key 用环境变量或者配置中心管理,本地开发可以用.env文件配合 spring-dotenv,但记得把.env加进.gitignore。团队协作时,Base URL 和 Model ID 可以写进application.yml提交,Key 单独注入。
模型 ID 建议做成可配置项,不同环境用不同模型。比如本地用便宜的小模型跑通链路,测试环境用中等模型,生产再换主力模型。Spring AI 支持通过 profile 覆盖配置,application-dev.yml和application-prod.yml分别写不同的 model 即可。
如果你后续要接 Claude Code 或者做长期编码 Agent,可以考虑用 Coding Plan,把模型调用额度固定下来,避免按次计费的不确定性。日常验证模型是否可用,直接在模型对话页面试跑最快。接入文档里有完整的接口说明,遇到协议细节可以对照。
最后说一个我自己的习惯:每次改完 endpoint 配置,先跑那个 CommandLineRunner 的模型测试,确认模型通道通了,再去测 MCP 工具调用。这样能把「模型通道问题」和「MCP 协议问题」彻底分开,省下大量排查时间。工具调用返回正确结果、模型也能正常回复,这套 Spring AI MCP 服务端接 TaoToken 的配置就算真正落地了。