ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型——基于Spring AI服务,开发MCP服务并接入TaoToken统一通道

大模型——基于Spring AI服务,开发MCP服务并接入TaoToken统一通道 1. 从本地 Spring AI 到 MCP 服务为什么模型端点要统一走 TaoToken如果你已经用 Spring AI 在本地跑通过一个能对话的 Demo接下来大概率会碰到一个很现实的问题MCP 服务注册好了工具也能被客户端发现但模型请求这一环总是散落在各处。客户端一套 Key服务端一套 KeySSE 服务里再写一套时间一长自己都记不清哪个配置文件对应哪个环境。更麻烦的是不同模型供应商的 Base URL、鉴权头、模型名格式都不一样Spring AI 的OpenAiApi默认又只认 OpenAI 那套规范一旦要换模型就得改代码。我这次要做的就是把 Spring AI 搭出来的 MCP 服务端模型调用端点统一改到 TaoToken 的 API 通道上。TaoToken 是一个面向开发者的模型统一接入通道它把多家模型的调用收敛成一套 OpenAI 兼容的接口你只需要一个 Key、一个 Base URL就能在 Spring AI 里用同一份配置切换不同模型。对于正在做 MCP 工具注册、又不想在模型接入上反复折腾的 Java 开发者来说这个组合能省掉大量适配工作。MCP 本身是 Model Context Protocol你可以把它理解成“让模型知道有哪些工具可用”的协议。Spring AI 从 1.0 版本开始提供了spring-ai-mcp-server相关 starter支持 stdio 和 SSE 两种传输方式。stdio 适合本地命令行进程SSE 适合 HTTP 长连接。无论哪种服务端最终都要调用模型来完成推理而这一步正是我们要接到 TaoToken 的地方。这篇文章面向的是本地已经跑通 Spring AI 的 Java 开发者目标是一次性打通 MCP 工具注册与模型请求链路。我会给出可复制的application.yml配置片段、MCP 服务端启动命令以及用 curl 验证工具列表与对话返回的检查动作。整个过程不需要你改 Spring AI 的源码只靠配置和少量 Bean 调整就能完成。先说清楚整体链路客户端比如 Cline、Trae 这类支持 MCP 的工具通过 stdio 或 SSE 连接到你的 Spring AI MCP 服务端服务端启动时向客户端暴露工具列表当客户端发起对话时服务端把请求转发给 TaoToken 的/v1/chat/completions拿到模型返回后再回传给客户端。模型端点统一之后你换模型只需要改application.yml里的model字段不用动任何 Java 代码。这里有个容易踩的坑Spring AI 的 MCP 服务端和模型客户端是两个独立的自动配置。很多人只配了 MCP 的spring.ai.mcp.server却忘了配spring.ai.openai结果服务能启动、工具能注册但一对话就报找不到 API Key。所以下面的配置我会把这两块分开写清楚避免你漏配。2. TaoToken 前置准备拿 Key、认端点、选模型在动 Spring AI 配置之前先把 TaoToken 这边的三样东西准备好API Key、Base URL、Model ID。这三样是后面所有配置的基础缺一个都跑不通。API Key 的获取入口在控制台的 API Keys 页面地址是https://taotoken.net/api-keys。登录后新建一个 Key复制出来保存好。注意这个 Key 只在创建时完整显示一次关掉页面就看不到了所以建议直接贴到你的密码管理器或者临时文件里。Key 的格式通常是一串以sk-开头的字符串长度比较长别手动截断。Base URL 这块要特别小心。TaoToken 的 API 根地址是https://taotoken.net/api但在 Spring AI 的 OpenAI 配置里base-url需要写到/v1这一层也就是https://taotoken.net/api/v1。如果你只写到/apiSpring AI 拼接出来的请求路径会变成/api/chat/completions少了一层/v1服务端会返回 404。这个细节我在第一次配置时就踩过日志里看到 404 还以为是 Key 的问题排查了半天。Model ID 取决于你想用哪个模型。TaoToken 的模型列表可以在模型对话页面查看地址是https://taotoken.net/chat。你可以在那里先手动发一条消息确认模型能正常返回然后再把对应的模型名填到 Spring AI 配置里。常见的模型名格式类似gpt-4o、claude-3-5-sonnet这种具体以你账号下可用的为准。如果你打算长期做编码类 Agent可以关注 Coding Plan 页面https://taotoken.net/coding-plan那里有针对代码场景的套餐说明。把这三样东西整理成一张表方便后面配置时对照配置项值说明API Keysk-xxxxxxxx控制台创建只显示一次Base URLhttps://taotoken.net/api/v1注意带/v1Model ID如gpt-4o以模型对话页可用为准这里要提醒一句不要把 Key 硬编码到 Java 代码里也不要把带 Key 的application.yml提交到 Git。推荐用环境变量注入Spring AI 支持${TAOTOKEN_API_KEY}这种占位符写法。下面配置片段里我会用环境变量方式你本地设置好TAOTOKEN_API_KEY就行。另外TaoToken 的接入文档在https://taotoken.net/doc里面有各语言 SDK 的调用示例。虽然 Spring AI 用的是自己的OpenAiApi封装但请求体和响应体格式跟 OpenAI 一致所以文档里的 curl 示例可以直接拿来对照排查。如果你在 Spring AI 里遇到返回结构解析问题用文档里的 curl 先验证一遍能快速定位是通道问题还是代码问题。准备好这三样之后就可以进入 Spring AI 的配置环节了。下一节我会给出完整的application.yml和必要的 Bean 配置你可以直接复制到自己的项目里改。3. 可复制配置application.yml 与 MCP 服务端启动这一节是整篇文章的核心我会把 Spring AI 接入 TaoToken 的配置拆成三块模型客户端配置、MCP 服务端配置、以及启动命令。你按顺序复制即可。先看application.yml的完整片段。假设你的项目是spring-ai-mcp-stdio-server模块配置文件放在src/main/resources/application.ymlspring: application: name: spring-ai-mcp-stdio-server main: web-application-type: none banner-mode: off ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api/v1 chat: options: model: gpt-4o temperature: 0.7 mcp: server: name: addOrMinus-service version: 1.0.0 stdio: true type: SYNC这里有几个关键点。spring.ai.openai.api-key用环境变量注入避免明文。base-url必须写到/v1。spring.ai.openai.chat.options.model填你在 TaoToken 模型对话页确认可用的模型名。spring.ai.mcp.server.stdio: true表示用标准输入输出传输适合被 Cline、Trae 这类客户端以子进程方式拉起。type: SYNC表示同步工具调用如果你用的是响应式编程可以改成ASYNC。如果你用的是 SSE 服务端模块配置会略有不同主要是把stdio换成sse并指定端口spring: ai: mcp: server: name: querweather-sse-mcp version: 1.0.0 sse: enabled: true endpoint: /sse server: port: 9090注意 SSE 模式下web-application-type不能是none要保留默认的 servlet 类型否则端口不会监听。接下来是 MCP 工具的注册。Spring AI 用Tool注解标记方法然后在配置类里把工具 Bean 注册进去。下面是一个加法工具的示例Component public class MathTools { Tool(description 计算两个整数相加的结果) public int add(int a, int b) { return a b; } Tool(description 计算两个整数相减的结果) public int minus(int a, int b) { return a - b; } }然后在 MCP 服务端配置里注册这个工具提供者Configuration public class McpServerConfig { Bean public ToolCallbackProvider mathToolCallbackProvider(MathTools mathTools) { return MethodToolCallbackProvider.builder() .toolObjects(mathTools) .build(); } }这样服务端启动时就会把add和minus两个工具暴露给客户端。客户端在对话时如果问到“3 加 5 等于几”模型会决定调用add工具服务端执行后把结果回传。启动命令这块如果你打包成了可执行 JAR直接用java -jar启动java -jar target/spring-ai-mcp-stdio-server.jar但 stdio 模式下服务端是等着客户端通过标准输入发消息的你直接在终端跑会看到它“卡住”不动这是正常的。真正的启动方式是在 MCP 客户端的配置文件里声明由客户端拉起子进程。以 Cline 或 Trae 的mcp.json为例{ mcpServers: { addOrMinus-service: { disabled: false, timeout: 30, type: stdio, command: java, args: [ -jar, D:/mcp/spring-ai-mcp-demo-master/spring-ai-mcp-stdio-server/target/spring-ai-mcp-stdio-server.jar ], cwd: D:/mcp/spring-ai-mcp-demo-master/spring-ai-mcp-stdio-server/target, env: { TAOTOKEN_API_KEY: sk-你的Key, TIMEZONE: Asia/Shanghai, spring.ai.mcp.server.stdio: true, spring.main.web-application-type: none, spring.main.banner-mode: off } } } }注意env里把TAOTOKEN_API_KEY传进去了这样 Spring AI 启动时能读到。如果你不想在mcp.json里写明文 Key可以先在系统环境变量里设置好然后这里省略。SSE 模式的启动就简单了直接跑 Spring Boot 应用mvn spring-boot:run -pl spring-ai-mcp-sse-server启动后监听9090端口客户端用 URL 方式连接{ mcpServers: { querweather-sse-mcp: { url: http://localhost:9090/sse, transportType: sse, autoApproval: false, requireManualConfirmation: true } } }配置到这里就完成了。下一节我会用 curl 验证工具列表和对话返回确认整条链路真的通了。4. 验证请求用 curl 检查工具列表与对话返回配置写完之后别急着在客户端里点来点去先用 curl 把服务端和 TaoToken 通道分别验证一遍。这样出问题时能快速定位是哪一段的毛病。先验证 TaoToken 通道本身是否可用。这一步不经过 Spring AI直接打 APIcurl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [ {role: user, content: 只回复两个字通了} ] }如果返回的 JSON 里choices[0].message.content是“通了”说明 Key、Base URL、模型名都没问题。如果返回 401检查 Key 是否复制完整如果返回 404检查 URL 是不是漏了/v1如果返回模型不存在的错误去模型对话页确认模型名。通道验证通过后再验证 MCP 服务端的工具列表。SSE 模式下MCP 协议的工具列表通过 JSON-RPC 暴露你可以用 curl 发一个tools/list请求curl -X POST http://localhost:9090/mcp \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 1, method: tools/list, params: {} }返回结果里应该能看到add和minus两个工具每个工具带name、description、inputSchema。如果返回空列表说明ToolCallbackProvider没注册成功检查McpServerConfig里的 Bean 是否被扫描到。stdio 模式下没法直接用 curl因为它是标准输入输出。你可以写一个简单的测试脚本往进程的标准输入里写 JSON-RPC 消息echo {jsonrpc:2.0,id:1,method:tools/list,params:{}} | java -jar target/spring-ai-mcp-stdio-server.jar正常的话标准输出会打印工具列表的 JSON。如果没有任何输出检查spring.ai.mcp.server.stdio是否为true以及spring.main.web-application-type是否为none。工具列表验证通过后再验证一次完整的对话链路。在客户端里问“3 加 5 等于几”观察服务端日志。你应该能看到类似这样的流程客户端发送对话请求 → 服务端调用 TaoToken 的 chat completions → 模型返回工具调用意图 → 服务端执行add(3, 5)→ 把结果回传给模型 → 模型生成最终回答“8”。如果日志里看到Tool execution相关的记录说明工具调用成功了。如果模型直接回答“8”而没有走工具可能是模型没理解工具描述试着把Tool的description写得更明确比如“当用户询问两个数相加时使用此工具”。还有一个验证技巧在 TaoToken 的模型对话页面手动发一条同样的消息对比返回。如果那边正常、Spring AI 这边不正常问题就在 Spring AI 配置如果两边都不正常问题在通道或模型本身。验证通过后你就可以在客户端里正常使用 MCP 工具了。下一节我会列出几个常见的报错和排查方法都是我在配置过程中真实遇到过的。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节按报错信息来组织你遇到哪个就查哪个。每个报错我都会给出触发场景和排查步骤。401 Unauthorized。这个最常见通常是 Key 没传对。先确认TAOTOKEN_API_KEY环境变量在当前 shell 里能echo出来。如果是在mcp.json的env里传的注意 JSON 里不能有注释Key 字符串不能带多余空格。还有一种情况是 Key 被撤销了去控制台 API Keys 页面确认状态是“启用”。如果 Key 没问题检查base-url是否写成了https://taotoken.net/api少了/v1有些网关对路径敏感会返回 401 而不是 404。local proxy failed。这个报错通常出现在客户端侧意思是客户端尝试连接本地 MCP 服务端时失败了。先确认服务端进程是否真的起来了。stdio 模式下客户端会拉起子进程如果command或args路径写错子进程起不来就会报这个。检查mcp.json里的args路径是否指向真实存在的 JAR 文件Windows 下路径用正斜杠或双反斜杠。SSE 模式下确认9090端口没有被占用用netstat -ano | findstr 9090查一下。如果端口被占改server.port或者杀掉占用进程。reading choices 相关报错。这个一般出现在 Spring AI 解析 TaoToken 返回结果时报错信息里带reading choices或Cannot deserialize。原因是返回的 JSON 结构和 Spring AI 预期的ChatCompletion结构不完全一致。排查方法先用第 4 节的 curl 命令看原始返回确认choices字段存在且是数组。如果返回里多了或少了字段可能是模型或通道的兼容性问题。这时候可以尝试换一个模型或者在 Spring AI 里自定义OpenAiApi的ResponseErrorHandler。另一个常见原因是流式和非流式混用spring.ai.openai.chat.options里如果开了stream: true但客户端不支持流式解析也会报这个。先关掉流式试试。OAuth 相关报错。如果你在日志里看到OAuth或token endpoint字样说明某个环节在尝试走 OAuth 鉴权。TaoToken 的 API Key 方式是 Bearer Token不需要 OAuth 流程。出现这个报错通常是 Spring AI 的某个 starter 默认启用了 OAuth 客户端自动配置。检查pom.xml里是否引入了spring-boot-starter-oauth2-client如果有排除掉或者设置spring.security.oauth2.client.registration为空。另外spring.ai.openai的配置里不要写client-id、client-secret这类字段只保留api-key和base-url。除了这四个还有一个配置层面的坑JDK 版本。Spring AI 1.0 要求 JDK 17 及以上如果你本地是 JDK 11编译会报“无效的目标发行版”。检查pom.xml里的maven.compiler.source和target是否为 17。用java -version确认运行时版本一致。如果之前用 JDK 21 编译过、现在换回 17记得先mvn clean清掉旧的 class 文件。排查的时候日志是你的第一手资料。把 Spring AI 的日志级别调到DEBUG在application.yml里加logging: level: org.springframework.ai: DEBUG io.modelcontextprotocol: DEBUG这样能看到请求体、响应体、工具调用的详细过程。如果请求体里的model字段和你配置的不一致说明配置没生效检查是否有多个application.yml或者 profile 覆盖。最后提醒一句如果你在mcp.json里同时配了 stdio 和 SSE 两个服务注意它们的name不能重复否则客户端会混淆。每个服务的env是独立的Key 要分别传。6. 统一通道之后模型切换与长期编码场景的接入建议模型端点统一到 TaoToken 之后最直接的好处是换模型不用改代码。你只需要改application.yml里的model字段重启服务端客户端那边完全无感。我试过在同一个 MCP 服务里上午用gpt-4o做通用对话下午换成claude-3-5-sonnet做代码审查切换成本就是改一行配置。对于长期做编码类 Agent 的场景建议把 MCP 工具按领域拆成多个服务端。比如一个服务端专门放文件操作工具一个放 Git 操作工具一个放数据库查询工具。每个服务端独立配置模型这样你可以让文件操作走便宜快速的模型让代码生成走能力更强的模型。TaoToken 的 Coding Plan 页面https://taotoken.net/coding-plan有针对编码场景的套餐说明如果你打算长期跑 Agent可以去看看额度规则。接入文档在https://taotoken.net/doc里面除了 curl 示例还有错误码说明。遇到不认识的返回码先查文档比在日志里猜要快。模型对话页面https://taotoken.net/chat适合做快速验证改完配置后先在那里发一条消息确认模型可用再去客户端里测。如果你还没创建 Key入口在https://taotoken.net/api-keys。创建时建议按用途命名比如mcp-stdio-dev、mcp-sse-prod方便后面排查是哪个环境在用。Key 泄露了要立刻撤销重建不要觉得本地开发无所谓。最后说一个实际经验MCP 服务端的超时设置要和模型响应时间匹配。mcp.json里的timeout默认是 30 秒如果你用的模型响应较慢或者工具执行本身耗时客户端会提前断开。把timeout调到 60 或 120同时在 Spring AI 侧设置spring.ai.openai.chat.options.timeout。两边都设避免一边等另一边超时。配置改完之后记得用第 4 节的 curl 再验证一遍工具列表和对话返回。确认无误后就可以在 Cline 或 Trae 里正常提问了。整个链路打通后你后续加新工具只需要写Tool方法并注册 Bean模型端点这块不用再动。
返回列表