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

发布时间:2026/10/8 6:18:10
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 提交到仓库。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询