1. 从本地 MCP 工具调用说起:为什么要把 endpoint 换到统一通道
Spring AI 集成 MCP 协议之后,工具调用链路会变得比普通对话复杂不少。普通对话只有一次 HTTP 请求,而 MCP 工具调用是「模型先决定调哪个工具 → 应用执行工具 → 把结果回填给模型 → 模型再生成最终回答」这样一个多轮往返过程。每一轮往返都要访问模型 endpoint,如果 endpoint 分散在多个地方配置,本地调试和联调环境就会各写一套,改起来很容易漏。
我这次要解决的问题很具体:把 Spring AI 应用里 MCP 工具调用所依赖的模型访问 endpoint 与鉴权配置,统一改到 TaoToken 通道上。这样无论是本地跑 stdio 模式的 MCP Server,还是联调时连远程 SSE 服务,模型侧只认一套 Base URL 和 Key,工具调用链路不会因为环境切换而断掉。
适合谁看:已经用 Spring AI 跑通过基础对话、准备接入 MCP 工具调用、但被多环境 endpoint 配置搞烦的 Java 后端。你需要对 Spring Boot 的application.yml有基本了解,知道什么是ChatClient,剩下的配置我会给全。
先说清楚 MCP 工具调用里模型 endpoint 到底出现在哪。Spring AI 的ChatClient在带工具调用时,会把工具定义(JSON Schema 形式)一起发给模型,模型返回的响应里如果包含tool_calls字段,框架就去执行对应工具,再把ToolResponseMessage追加进消息列表,发起第二次模型请求。也就是说,一次完整的工具调用至少触发两次模型请求,两次都走同一个 endpoint。所以 endpoint 一旦配错,表现往往是「第一次请求有响应,但工具没被触发」或者「工具执行了但第二次请求 401」。
把 endpoint 统一到 TaoToken 之后,你只需要维护一个 Base URL 和一个 API Key。本地调试时用同一套,联调时把 Key 换成联调环境的那把即可,模型 ID 也可以按需切换。下面进入具体配置。
2. TaoToken 前置准备:拿到 Base URL、Key 和 Model ID 三件套
在改application.yml之前,先把三样东西准备好:Base URL、API Key、Model ID。这三件套是后面所有配置的基础,缺一个工具调用都跑不起来。
Base URL 用https://taotoken.net/api,注意这里不带任何查询参数,直接作为 Spring AI 的base-url使用。API Key 需要到控制台创建,路径是 console 页面下的 api-keys 管理。创建时给它起个能认出来的名字,比如spring-ai-mcp-local,方便后面区分本地和联调。Key 只在创建时完整显示一次,复制下来存好。
Model ID 这块要留意:MCP 工具调用对模型的 function calling 能力有要求,不是所有模型都能稳定返回结构化的tool_calls。选模型时优先挑支持工具调用的型号,具体可用列表在模型对话页面能看到,也可以直接查接入文档里的模型清单。我这边实测下来,选一个明确标注支持 function calling 的型号最省事,避免出现「模型把工具描述当普通文本复述一遍」这种尴尬情况。
如果你还没创建过 Key,可以走这个流程:打开 console,进 api-keys,点新建,命名,复制。整个过程不到一分钟。创建完先别急着关页面,把 Key 粘到本地一个临时文件里,等会儿配置要用。
这里有个容易踩的点:Base URL 末尾不要自己加/v1或者/chat/completions。Spring AI 的 OpenAI 兼容 starter 会自己在 base-url 后面拼路径,你多写一段就会变成/api/v1/v1/chat/completions这种,直接 404。我试过在 base-url 后面手滑加了斜杠,结果报了一晚上的连接错误,最后发现是路径重复。
三件套备齐后,建议先用模型对话页面做一次最小验证:发一句「你好」,确认 Key 和 Base URL 能通。这一步能通,后面 Spring AI 配置基本不会卡在鉴权上。如果这一步就报 401,那问题在 Key 本身,先解决 Key 再往下走。
3. 可复制配置:application.yml 与 mcp-server.json 的完整片段
这一节给全配置。先看application.yml,这是核心,模型 endpoint 和 MCP Client 都在这。
spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: your-model-id temperature: 0.7 mcp: client: enabled: true name: spring-ai-mcp-client version: 1.0.0 type: ASYNC request-timeout: 60s stdio: servers-configuration: classpath:mcp-server.json几个参数说明。base-url就是 TaoToken 的 API 地址,api-key我用环境变量占位,实际跑的时候在 IDE 的 Run Configuration 里配TAOTOKEN_API_KEY,或者本地用.env加载,别把 Key 硬编码进 yml 提交到仓库。model填你在模型对话页面选好的 Model ID。type: ASYNC是 MCP Client 的连接模式,异步非阻塞,工具调用多轮往返时不会把主线程卡住。request-timeout给到 60 秒,因为工具执行加二次模型请求,链路比普通对话长。
再看mcp-server.json,这个文件放在src/main/resources下,定义本地 stdio 模式的 MCP Server 怎么启动:
{ "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/yourname/workspace" ] } } }Windows 下command要写成npx.cmd的完整路径,比如D:\\devolop\\node\\npx.cmd,否则 Spring AI 启动子进程时会找不到命令。args里最后那个路径是允许 AI 操作的根目录,为了安全只开放这一个目录,别图省事写成盘符根目录。
如果你用的是 Cline MCP 或者 CC Switch 这类工具做本地调试,它们的配置文件格式和上面基本一致,都是mcpServers对象包一个服务名。区别在于 Cline MCP 的配置入口在插件设置里,CC Switch 是独立的配置文件。不管哪个,Base URL、Key、Model ID 三件套的填法是一样的,只是字段名可能叫baseUrl、apiKey、model。Codex 那边如果用auth.json,结构是{"openai": {"apiKey": "...", "baseURL": "..."}},Model ID 在请求体里传。
配置改完,代码侧只需要保证ChatClient注入了ToolCallbackProvider:
public ChatServiceImpl(ChatClient.Builder builder, ToolCallbackProvider tools) { this.chatClient = builder .defaultToolCallbacks(tools) .build(); }ToolCallbackProvider会被 Spring 自动装配,把mcp-server.json里定义的 MCP Server 暴露的工具全部注册进来。到这里配置就齐了。
4. 验证请求与结果核对:一次完整的工具调用怎么确认成功
配置写完,启动应用,先看控制台日志。MCP Client 初始化成功时会打印类似MCP client initialized with N tools的日志,N 就是filesystem服务暴露的工具数量。如果 N 是 0,说明mcp-server.json没被加载或者 npx 启动失败,先查这个。
日志正常后,发一次工具调用请求。用curl直接打你的 Spring Boot 接口,假设你暴露了一个/chat端点:
curl -X POST http://localhost:8080/chat \ -H "Content-Type: application/json" \ -d '{"message": "在 workspace 目录下创建一个 hello.txt,内容写 TaoToken MCP test"}'请求发出去后,观察两段日志。第一段是模型返回tool_calls,日志里会出现工具名write_file和参数{"path": "...", "content": "..."}。第二段是工具执行完,框架把结果回填后再次请求模型,模型生成最终自然语言回复。两段都出现,才算一次完整工具调用。
结果核对分三层。第一层看文件系统:去mcp-server.json里配的根目录下,确认hello.txt真的被创建了,内容对得上。第二层看接口返回:/chat的响应里应该有模型对这次操作的描述,比如「已创建 hello.txt」。第三层看模型侧:如果返回里出现「我无法直接操作文件」这类话,说明模型没触发工具调用,问题在 Model ID 不支持 function calling 或者工具描述没被正确传递。
我实测下来,最容易出问题的是第二段请求。工具执行成功了,文件也创建了,但第二次模型请求报 401。这种情况通常是 Key 在两次请求之间被换了,或者base-url配成了带路径的地址。核对时把两次请求的 URL 都打出来看,确认都是https://taotoken.net/api开头。
如果工具调用链路正常,你会在日志里看到清晰的「模型请求 → tool_calls → 工具执行 → 模型二次请求 → 最终回复」四步。这四步齐了,说明 endpoint 和鉴权配置改到 TaoToken 已经生效。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
工具调用链路跑不通,报错基本集中在四类。逐个说。
401 Unauthorized。最常见,Key 不对或者没传。先确认TAOTOKEN_API_KEY环境变量在当前 shell 里能echo出来。如果用的是 IDE,Run Configuration 里的环境变量和终端不是一套,容易漏配。还有一种情况是 Key 复制时带了空格,粘到 yml 里变成api-key: " sk-xxx",Spring 读出来带前导空格,鉴权就失败。检查时把 Key 前后引号去掉,确认没有多余字符。
local proxy failed。这个报错通常出现在 MCP Client 启动 stdio 子进程时。原因一般是mcp-server.json里的command路径不对,或者 npx 没装。Windows 下写npx而不是npx.cmd就会报这个。另外 Node.js 版本低于 18 也会导致@modelcontextprotocol/server-filesystem启动失败。排查方法:把command和args拼成一条命令,在终端里手动跑一遍,能跑通再放回配置。
reading choices 相关报错。典型信息是Cannot read field "choices" because response is null或者reading choices空指针。这说明模型返回的响应体结构不对,Spring AI 按 OpenAI 格式解析choices数组时拿不到。原因通常是base-url配错,请求打到了非 OpenAI 兼容的路径上,返回了 HTML 或者别的 JSON 结构。确认base-url是https://taotoken.net/api,末尾没有多余路径。还有一种可能是 Model ID 写错,服务端返回了错误对象而不是正常的 chat completion。
OAuth 相关报错。如果你在配置里误开了某些需要 OAuth 的鉴权模式,或者 Key 类型选成了 OAuth token 而不是 API Key,会报invalid_token或OAuth authentication failed。TaoToken 这边用 API Key 鉴权,不需要 OAuth 流程。检查api-key字段填的是不是从 api-keys 页面创建的那把 Key,别把别的凭证填进来。
这四类报错覆盖了九成以上的配置问题。排查顺序建议:先确认 Key 能通(用模型对话页面验证),再确认base-url无多余路径,再确认 MCP Server 能手动启动,最后看 Model ID 是否支持工具调用。按这个顺序走,基本不会绕弯路。
6. 把配置固化下来:长期编码与 Agent 场景的接入建议
配置调通之后,建议把三件套固化到项目里,别每次换环境都手动改。本地开发用.env文件加载TAOTOKEN_API_KEY,.env加进.gitignore。联调环境用 CI 的 secret 注入,application.yml里只留${TAOTOKEN_API_KEY}占位。Model ID 如果本地和联调不一样,也抽成环境变量TAOTOKEN_MODEL_ID,在 yml 里用${TAOTOKEN_MODEL_ID:default-model}给个默认值。
如果你后面要跑长期的编码 Agent 或者多轮工具调用任务,建议走 Coding Plan,它的额度模型更适合高频工具调用场景,不会因为单次对话轮次多就频繁触发限流。接入方式不变,还是那三件套,只是 Key 换成 Coding Plan 对应的那把。
MCP 工具调用的调试入口在接入文档里有更细的协议说明,遇到工具描述不被模型识别的情况可以去翻一下工具 schema 的写法。模型侧的能力验证还是用模型对话页面最快,改完 Model ID 先在那试一句带工具意图的话,确认能触发再回代码里跑。
最后留一个我踩过的坑:mcp-server.json里配的根目录路径,Windows 下要用双反斜杠\\或者正斜杠/,单反斜杠会被 JSON 解析成转义字符,导致路径变成乱码,MCP Server 启动后操作的是错误目录。这个错很隐蔽,日志里只显示路径不对,不会直接告诉你转义问题。改配置时留意一下就行。