SpringAI 实战:用 TaoToken 统一 Key 打通 MCP 服务器端与客户端

发布时间:2026/10/8 6:18:08

SpringAI 实战:用 TaoToken 统一 Key 打通 MCP 服务器端与客户端 1. 为什么要在 SpringAI 里把 MCP 两端接到同一个 Key 上MCP 全称 Model Context Protocol模型上下文协议你可以把它理解成大模型世界的 USB 接口Server 端负责把本地能力查天气、读文件、查数据库包装成标准工具Client 端负责让大模型发现并调用这些工具。SpringAI 从 1.0 开始提供了spring-ai-starter-mcp-server-webflux和spring-ai-starter-mcp-client两套 starter让 Java 开发者不用手写 JSON-RPC 就能把这条链路搭起来。但真正动手时很多人会卡在一个很现实的问题上Server 端和 Client 端都要调模型难道要申请两套 Key、维护两份配置尤其是 Client 端要对接 LLM 做意图识别和工具选择Server 端在某些场景下也要用模型做参数补全或结果润色如果两边各配一套凭证本地调试时改一处忘一处报 401 能查半天。我试过把两端的模型调用统一收敛到 TaoToken 的 API 通道上用同一个 Key、同一个 Base URLServer 和 Client 各自读同一份配置。这样做的好处很直接本地跑通后把配置原样搬到测试环境就行不用再区分「这是 Server 的 Key 还是 Client 的 Key」。TaoToken 的接入地址是https://taotoken.net/api兼容 OpenAI 风格的/v1/chat/completionsSpringAI 的 OpenAI starter 可以直接指过去。这篇内容面向的是已经会用 Spring Boot 写接口、但对 MCP 还停留在概念阶段的同学。我会给出可复制的application.yml、MCP Server 与 Client 的 Bean 注册代码以及一次端到端调用验证。你跟着敲完本地能跑出一个「问天气 → Client 调模型 → 模型选工具 → Server 返回结果」的完整闭环。核心检索词就三个SpringAI、MCP 服务器端、MCP 客户端全文围绕它们展开。需要提前说明的是MCP 的传输方式有 STDIO、SSE、Streamable HTTP 几种本文用 WebFlux 的 SSE 模式因为它最适合本地两个独立进程互相调用也方便你用 curl 直接验证 Server 是否活着。如果你用的是 STDIO 模式配置项会略有不同但 Key 统一的思路完全一样。2. TaoToken 前置准备一个 Key 同时喂给 MCP 两端在写代码之前先把「模型通道」这件事定下来。SpringAI 的 MCP Client 本质上是一个 ChatClient它需要base-url、api-key、model三件套才能发起对话。MCP Server 如果只做纯工具暴露其实可以不配模型但一旦你的工具需要模型做参数解析比如用户说「帮我看看西安明天适不适合出门」工具入参需要模型先抽取城市和时间Server 端也得有模型能力。所以统一 Key 的价值就在这里。第一步去 TaoToken 控制台创建一个 API Key。入口在https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_console登录后在 API Keys 页面点新建复制出来的字符串形如sk-xxxxxxxx。这个 Key 就是后面 Server 和 Client 共用的那一把。第二步确认你要用的模型 ID。TaoToken 的模型对话页https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_models里能看到当前可用的模型列表选一个支持 function calling 的比如claude-sonnet-4-5或gpt-4o这类。MCP 的工具调用依赖模型的 function calling 能力选错了模型会出现「模型不调用工具、直接编答案」的情况这是后面排障章节要重点讲的坑。第三步把 Base URL 记牢https://taotoken.net/api。注意这里不带/v1SpringAI 的 OpenAI starter 会自动拼/v1/chat/completions。如果你在别的框架里用记得手动补/v1。这个地址在 Server 和 Client 的配置里会各出现一次但值完全相同。关于 Coding Plan如果你打算长期跑 MCP 相关的 Agent 开发频繁调试工具调用会消耗不少 token可以了解下https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_plan它更适合这种高频、长会话的编码场景。不过本文的验证流程用普通 API Key 就够了不必一开始就上套餐。这里有个细节要注意不要把 Key 硬编码进application.yml提交到 Git。本地开发用环境变量注入比如TAOTOKEN_API_KEY然后在 yml 里写${TAOTOKEN_API_KEY}。这样 Server 和 Client 两个模块读的是同一个环境变量真正做到「一处配置、两端生效」。如果你用 IDEA 启动在 Run Configuration 的 Environment variables 里填一次即可。3. 可复制配置application.yml 与两端 Bean 注册这一节是全文的核心给出能直接粘贴运行的配置和代码。我按「父工程 两个子模块」的结构来组织你也可以放在一个工程里用不同 profile 区分。先看 MCP Server 模块的application.ymlserver: port: 8081 spring: application: name: mcp-weather-server ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-5 temperature: 0.3 mcp: server: name: weather-mcp-server version: 1.0.0 type: SYNC sse-message-endpoint: /mcp/message tool-change-notification: trueServer 端的依赖只需要一个dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server-webflux/artifactId /dependency然后是工具类和暴露 Bean。工具方法用Tool注解描述SpringAI 会自动扫描并注册到 MCP 协议里Service public class WeatherService { Tool(description 根据城市名称获取天气预报入参为城市中文名) public String getWeatherByCity(String city) { MapString, String mock Map.of( 西安, 晴天18-26度, 北京, 小雨12-19度, 上海, 大雨20-24度 ); return mock.getOrDefault(city, 未查询到该城市天气); } }暴露成 MCP 工具的 Bean 注册Configuration public class McpServerConfig { Bean public ToolCallbackProvider weatherTools(WeatherService weatherService) { return MethodToolCallbackProvider.builder() .toolObjects(weatherService) .build(); } }接下来是 MCP Client 模块的application.yml注意 base-url 和 api-key 与 Server 完全一致server: port: 8082 spring: application: name: mcp-weather-client ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-5 mcp: client: enabled: true name: weather-mcp-client version: 1.0.0 type: SYNC sse: connections: weather-server: url: http://localhost:8081 sse-endpoint: /sseClient 端依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client-webflux/artifactId /dependencyClient 的 ChatClient Bean 注册把 MCP 工具挂进对话链路Configuration public class McpClientConfig { Bean public ChatClient chatClient(ChatClient.Builder builder, ToolCallbackProvider mcpTools) { return builder .defaultSystem(你是一个天气助手用户问天气时必须调用工具查询不要自己编造。) .defaultTools(mcpTools) .build(); } }最后是对外接口RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/chat) public String chat(RequestParam(defaultValue 西安今天天气怎么样) String msg) { return chatClient.prompt().user(msg).call().content(); } }这套配置里Server 和 Client 的base-url、api-key、model三项完全对齐这就是「统一 Key」的落地方式。你改模型时两个文件一起改或者干脆抽到一个公共配置里用spring.config.import引入。4. 验证请求一次端到端调用看结果配置写完先启动 Server再启动 Client。Server 启动日志里会看到类似Registered tools: [getWeatherByCity]的输出说明工具已经挂到 MCP 协议上了。如果没看到这行八成是ToolCallbackProvider没被扫描到检查包路径是否在启动类的同级或子包下。先用 curl 直接验证 Server 的 SSE 端点是否活着curl -N http://localhost:8081/sse正常会返回一串event: endpoint加data: /mcp/message?sessionIdxxx。这个 sessionId 是后续消息通道的凭证说明 Server 的 WebFlux SSE 传输层工作正常。如果这里卡住没输出多半是端口被占或 WebFlux 依赖没进来。然后调 Client 的接口触发完整链路curl http://localhost:8082/chat?msg西安今天天气怎么样预期返回类似「西安今天是晴天气温 18-26 度」。这条请求背后发生了这些事Client 把用户问题发给 TaoToken 通道上的模型模型识别出需要调用getWeatherByCity工具Client 通过 MCP 协议把工具调用请求转发给 ServerServer 执行本地方法返回「晴天18-26度」模型拿到结果后组织成自然语言回复。整个过程你只配了一个 Key两端共用。再测一个边界情况验证模型确实在调工具而不是瞎编curl http://localhost:8082/chat?msg广州今天天气怎么样因为 mock 数据里没有广州预期返回「未查询到该城市天气」相关的回复。如果模型返回了一个编造的广州天气说明工具没被调用回到排障章节看第 5 节。想更直观地看模型对话过程可以打开https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_chat用同一个 Key 手动发一句「西安天气」对比一下带工具和不带工具时模型的回答差异能帮你理解 MCP 到底在链路里做了什么。验证通过后建议把这次调用的请求日志打开在application.yml里加logging: level: org.springframework.ai: DEBUG重启后你能看到完整的工具调用入参和出参排查问题时非常有用。5. 本篇常见错排查401、local proxy failed 与工具不触发这一节按真实报错来对照都是我在搭这套链路时踩过的。报错一401 Unauthorizedbody 里带invalid api key。这是最常见的问题九成是 Key 没注入成功。检查三处环境变量TAOTOKEN_API_KEY是否在启动配置里填了yml 里写的是${TAOTOKEN_API_KEY}而不是硬编码的空字符串Key 复制时有没有带多余空格。还有一种情况是 base-url 写成了https://taotoken.net/api/v1SpringAI 又拼了一次/v1变成/api/v1/v1/chat/completions虽然报错可能不是 401 而是 404但同样表现为「连不上」。正确写法就是https://taotoken.net/api。报错二local proxy failed或连接超时。这个报错通常出现在 Client 连 Server 的阶段不是连 TaoToken。检查 Client 配置里的spring.ai.mcp.client.sse.connections.weather-server.url是不是http://localhost:8081端口和 Server 的server.port是否一致。如果你在容器里跑localhost 要换成服务名。另外确认 Server 先于 Client 启动Client 启动时会去拉工具列表Server 没起来就会连接失败。报错三Error reading choices或返回体解析异常。这个多半是模型 ID 写错了或者选的模型不支持 function calling。回到模型对话页确认模型 ID 拼写换成明确支持工具调用的模型。还有一种可能是响应被截断检查temperature是否设得过高导致输出不稳定。报错四模型不调用工具直接编答案。这是最隐蔽的。表现是问广州天气模型编了一个「多云 25 度」。原因通常是 system prompt 没强调「必须调用工具」或者工具描述Tool(description...)写得太模糊模型没识别出该用哪个工具。解决办法是把 description 写具体比如「根据城市中文名查询天气预报当用户询问某城市天气时必须调用」同时在 ChatClient 的defaultSystem里明确要求。另外确认defaultTools(mcpTools)真的挂上了可以在启动日志里搜ToolCallback看注册了几个。报错五OAuth 相关的 401 或invalid_token。如果你在 Client 配置里误加了 OAuth 相关参数而 TaoToken 的 API Key 模式并不需要它会互相冲突。把spring.ai.mcp.client下多余的 auth 配置删掉只保留 sse connections 即可。统一 Key 方案下鉴权只发生在 Client/Server 调模型这一层MCP 两端之间是本地信任的。排查顺序建议先 curl Server 的/sse确认 Server 活着再 curl Client 的/chat看报错最后开 DEBUG 日志看模型请求体。大部分问题在第一步和第二步就能定位。6. 把统一 Key 的思路用到你的 MCP 工程里跑通这个 demo 之后你可以把「统一 Key」的做法固化到工程结构里。最直接的方式是建一个common-config模块把base-url、api-key、model三项抽成application-common.ymlServer 和 Client 都用spring.config.import: classpath:application-common.yml引入。这样以后换模型、换通道只改一个文件。如果你后续要接更多 MCP Server比如文件系统 Server、数据库 ServerClient 端的sse.connections下加一个条目就行Key 还是那一把。TaoToken 的接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_doc里面有不同语言和框架的接入示例遇到 SpringAI 版本差异时可以对照看。API Keys 管理页在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_keys需要轮换 Key 时在这里操作。最后留一个实用技巧本地调试 MCP 工具调用时把temperature设成 0能让模型的工具选择更稳定减少「这次调了、下次没调」的随机性。等链路稳定后再调高做效果优化。这套配置我在几个内部小工具上跑了两周Server 和 Client 共用一把 Key 没出现过鉴权冲突唯一要注意的就是别把 Key 提交到仓库。
延伸阅读

更多相关文章

2026/10/8 6:13:08

从零搭建OpenRig:多智能体持久化协作编排系统架构与实践

1. 先从一个让人头疼的协作场景说起如果你和我一样,手里同时维护着好几个专精的 AI Agent——一个负责 SQL 生成,一个做数据可视化,一个写周报——大概很快就会撞上同一个问题:单打独斗的 Agent 干不了复杂的协作活,而…

2026/10/8 7:23:11

多模态的下一站,物理AI 物理推理 走向统一

【具身AGI导读】多模态融合的下一步往哪走,有人给了一个与「加法」相反的答案:不是往模型里再添一个通道,而是回头去找这些通道共同在量的那个东西。9 月 11 日,ECCV 2026 的一场专访里,斯坦福研究者吴佳俊把多模态融合…

2026/10/8 7:23:11

Python上机实验2|随机模拟与算法效率

实验简介: 本次上机实验围绕 random 随机库 交互式猜数字游戏,增加输入校验、记录猜测过程、连玩3局求平均次数; 实现思路: 交互式猜数字游戏 使用 random.randint(1,100) 生成1~100随机答案。用 try-except 捕获非整数输入&#…

2026/10/8 7:23:11

ponytail插件完全指南:轻量技能插件的使用与进阶

1. 从“ponytail”这个词说起:它到底指什么第一次看到“ponytail”作为项目标题,很多人会愣一下——这不是“马尾辫”吗?一个发型词汇怎么会出现在技术社区的热搜里?我最初也以为是某个美妆博主在分享扎头发的技巧,直到…

2026/10/8 7:23:11

openrig:统一编排Claude Code与Codex的AI编程工具管理方案

1. 从零认识 openrig:它到底解决什么问题第一次看到 openrig 这个名字,很多人会以为是某个硬件支架项目,毕竟 rig 在英文里有“装配、支架”的意思。但如果你最近在折腾 Claude Code、Codex 这类命令行 AI 编程助手,就会明白它其实…

2026/10/8 7:18:11

体验的Scaling时刻:读淘天首席科学家郑波2026云栖演讲

基于 2026 云栖大会主论坛演讲《体验的Scaling时刻》 演讲人:阿里巴巴 ATH 事业群技术副总裁、淘天集团首席科学家 郑波 视频源:Bilibili BV1p3hE6iEgW 核心论断:AI 的技术演进正迎来“体验的 Scaling(规模化)时刻”。…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/7 8:18:33

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑