尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring AI 调 MCP 服务,模型通道走 TaoToken 跑通

Spring AI 调 MCP 服务,模型通道走 TaoToken 跑通 Spring AI 1.0.3 工程接 12306-mcp 时MCP 客户端配置本身不复杂复杂的是模型通道DeepSeek 的 Key 要单独申请、单独维护而 TaoToken 提供统一 API 兼容通道https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end可以把deepseek.base-url换成 https://taotoken.net/apiapi-key换成从 TaoToken 创建的那一把 Key。MCP 侧的 SSE、Stdio 配置全部保持原样启动后请求 chat-stream 接口大模型照样会去调用 12306-mcp 查询列车信息。这篇文章就把这组改动拆开讲清楚pom.xml 依赖、application.yml 里的 deepseek 段、mcp-server.json 的两种系统写法以及验证和排障。1. SSE 调用 12306-mcp只动 deepseek 段MCP 原样保留原文的 SSE 方案里MCP 走的是 ModelScope 上部署好的远程服务模型通道走 DeepSeek 官方 API。这两个东西在 Spring AI 里是完全独立的两段配置spring.ai.mcp.client.sse管 12306-mcp 的接入地址spring.ai.deepseek管大模型用什么渠道回答。所以替换模型通道时MCP 段一个字都不用改。1.1 pom.xmlMCP client 和 deepseek starter 缺一不可先确认工程里这几个依赖都在Spring AI 1.0.3 的 MCP 客户端、DeepSeek 模型 starter、Web 和 Lombok 缺一不可?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.5.7/version /parent groupIdorg.example/groupId artifactIdspring-ai-mcp/artifactId version1.0-SNAPSHOT/version properties maven.compiler.source21/maven.compiler.source maven.compiler.target21/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.3/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-deepseek/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency /dependencies /projectspring-ai-starter-model-deepseek这个依赖决定了 Spring AI 会把spring.ai.deepseek.*下的配置识别成模型通道。MCP 工具注册进来之后通过ToolCallbackProvider注入 ChatClient大模型才能拿到12306-mcp这把工具。1.2 创建 TaoToken 的 API Key打开 TaoToken 注册并创建一个 API Key把生成的 Key 复制保存好。接下来 application.yml 里deepseek.api-key填的就是这把 Key不用再去 DeepSeek 官方渠道单独申请一套密钥了。拿到 Key 之后确认一下你打算在 Spring AI 里用的模型 ID。不同的模型 ID 对应不同的模型名具体以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场展示的为准不要在配置文件里凭感觉填一个否则调用时会报模型不存在。1.3 application.ymlsse 段保持deepseek.base-url 指向 TaoToken下面这份配置就是 SSE 方案的完整形态和原文相比只改了两行deepseek.base-url从 DeepSeek 官方地址换成 https://taotoken.net/apiapi-key换成 TaoToken 的 Key。12306-mcp 的 SSE 地址和 endpoint 保持原样MCP 的日志级别也保留。spring: ai: mcp: client: enabled: true name: spring-ai-agent type: async sse: connections: 12306-mcp: url: https://mcp.api-inference.modelscope.net/ sse-endpoint: /********/sse deepseek: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} logging: level: io.modelcontextprotocol: DEBUG org.springframework.ai.mcp: DEBUG如果你不想用环境变量也可以直接把api-key写成YOUR_API_KEY注意不要带引号Spring Boot 的 relaxed binding 对这种纯字符串 key 处理很直接。base-url的末尾不要加/v1TaoToken 的兼容通道地址就是 https://taotoken.net/api 本身加了反而会拼出错误路径。配置类不用动仍然是那个把 ToolCallbackProvider 接进 ChatClient 的 AppConfigConfiguration public class AppConfig { Bean public ChatClient chatClient(DeepSeekChatModel model, ChatMemory chatMemory, ToolCallbackProvider toolCallbackProvider) { return ChatClient.builder(model) .defaultAdvisors( SimpleLoggerAdvisor.builder().build(), MessageChatMemoryAdvisor.builder(chatMemory).build()) .defaultToolCallbacks(toolCallbackProvider) .build(); } }Controller 也和原文完全一致对外暴露/ai/chat-stream接口走流式返回RestController RequestMapping(ai) public class ChatController { Resource private ChatClient chatClient; GetMapping(value chat-stream, produces text/html;charsetutf-8) public FluxString stream(String msg, String chatId) { return chatClient.prompt() .user(msg) .advisors(advisor - advisor.param(ChatMemory.CONVERSATION_ID, chatId)) .stream() .content(); } }到这里SSE 方案的改造就结束了。MCP 服务器远程跑在 ModelScopeSpring AI 负责把 12306 的列车查询能力注册成工具TaoToken 负责把大模型请求送到兼容通道三者各管一段。2. Stdio 本地跑 12306-mcpmcp-server.json 和模型通道一起换比 SSE 更麻烦的是 Stdio 模式。这个模式下 Spring AI 要直接在本地用 npx 拉起 12306-mcp 的 Node 包所以除了模型通道要换到 TaoToken本地环境还得先装好 npm、npx 和 MCP 源码。2.1 本地依赖clone、npm i、全局 npx先把 12306-mcp 的源码拉下来装好依赖git clone https://github.com/Joooook/12306-mcp.git cd 12306-mcp npm iSpring AI 会用 npx -y 12306-mcp 这种命令在本地拉起服务所以 npx 必须是全局命令。如果安装过 Node 但没有 npx执行一次npm i -g npx在 Windows 上还要注意Spring AI 的 StdioClientTransport 是通过系统命令解释器启动子进程的Mac/Linux 直接用 npxWindows 需要 cmd /c 包一层。2.2 mcp-server.json 与 application.yml 的 stdio 配置Stdio 模式的 MCP server 配置要单独放到一个 JSON 文件里放在 classpath 根目录也就是和 application.yml 同级。Mac/Linux 版本长这样{ mcpServers: { 12306-mcp: { args: [ -y, 12306-mcp ], command: npx } } }Windows 系统要用 cmd 包一层{ mcpServers: { 12306-mcp: { command: cmd, args: [ /c, npx, -y, 12306-mcp ] } } }JSON 文件命名成mcp-server.json然后在 application.yml 里指定它的位置。模型通道的部分和 SSE 方案一模一样改的仍然只有 base-url 和 api-keyspring: ai: mcp: client: enabled: true name: spring-ai-agent type: sync stdio: servers-configuration: classpath:mcp-server.json deepseek: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} logging: level: io.modelcontextprotocol: DEBUG org.springframework.ai.mcp: DEBUG注意type: sync和type: async的区别。SSE 是远程连接用 async 很自然Stdio 是本地子进程原文用的是 sync照抄即可。如果你两种方式都要试可以分别用两套 profile或者直接改 type 和对应的 client 配置段不要同时在一个 profile 里开两个连接器。启动之后日志里会出现一行 Stdio 传输层收到的服务端启动消息类似这样i.m.c.transport.StdioClientTransport : STDERR Message received: 12306 MCP Server running on stdio看到这行字说明本地 MCP server 已经被 Spring AI 拉起来了。这时候再请求 chat-stream 接口大模型的回复就会走 TaoToken 通道而工具调用数据走的是本地 Stdio 管道。两条路径互不干扰。3. Streamable-HTTP 在 Spring AI 1.0.3 的版本差原文章节里还提到一个容易踩的坑Spring AI 1.0.3 并不支持 streamable-http 方式调用远程 MCP只能把 ModelScope 上的接口切成 SSE 模式来用。所以前面两份 application.yml 里远程调用写的都是sse.connections不是streamable-http.connections这不是配置习惯问题是版本能力边界。3.1 1.0.3 为什么不支持 streamable-httpstreamable-http 是 MCP 规范里相对新的传输方式它在同一个 HTTP 连接里支持请求-响应和流式响应比 SSE 的长期连接更省资源。但 Spring AI 客户端对这个协议的适配是从 1.1.0 版本才开始的。如果你拿 1.0.3 去配streamable-httpSpring AI 启动时会提示未识别的 MCP client 类型或者直接忽略这一段配置。判断自己用的版本很简单看 spring-ai-bom 的版本号小于 1.1.0 就老实走 SSE 或 Stdio。ModelScope 上的 12306-mcp 是在服务端做协议切换的它同时暴露了 SSE 和 streamable-http 的 endpoint 地址。原文给的 SSE endpoint 是/********/sse这种路径配置进sse-endpoint字段即可。3.2 升级到 1.1.0 以上的配置骨架如果后续把 Spring AI 升到 1.1.0 或更高远程调用 12306-mcp 可以改成 streamable-http配置结构从sse段换成streamable-http段spring: ai: mcp: client: enabled: true name: spring-ai-agent type: async streamable-http: connections: 12306-mcp: url: https://mcp.api-inference.modelscope.net/ endpoint: /********/mcp deepseek: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY}url是 MCP 服务的基础地址endpoint是对接的 mcp 路径。注意这里两个地址都是 MCP server 的和模型通道没有关系。TaoToken 的 Base URL 只出现在deepseek.base-url里不要顺手把它填到 MCP 的 url 字段。升级版本时还要注意spring-ai-bom 的版本号变了type和连接段配置要同步检查。如果你在公司现有项目上改最好先跑一下原有的 MCP 用例确认新版本对 tool schema 的序列化方式没有破坏性变化。4. 验证 chat-stream日志确认大模型真的调了 12306-mcp配置改完启动 Spring Boot 应用然后打开一个终端。验证的关键不是看应用能不能起来而是看大模型遇到联网查询类问题时会不会真的触发 12306-mcp 这个工具。4.1 请求 chat-stream 接口用 curl 发起一个带起始站和日期的查询请求让模型有充分理由调用列车查询工具curl -N --max-time 60 http://127.0.0.1:8080/ai/chat-stream?msg帮我查一下2025年11月20日从广州南到北京西的高铁车次chatId001msg 参数尽量给完整信息日期、出发地、目的地。这样大模型经过 Function Calling 判断后会生成一个针对 12306-mcp 的工具调用。如果只问「你好」模型根本不需要调工具验证会失败。--max-time 60是必要的MCP 工具查询 12306 可能要好几秒加上流式输出连接要保持一段时间。返回的内容会以 text/html 流式输出。看到车次、出发时间、余票这类信息说明整条链路已经通了。4.2 看日志确认 MCP 工具被调用在 Spring Boot 控制台里INFO 级别下能看到 Stdio 传输层的启动消息但想看具体的工具调用参数必须把 io.modelcontextprotocol 和 org.springframework.ai.mcp 调成 DEBUG。启动后如果日志里出现类似Calling tool: 12306-mcp或者 MCP server 返回的 JSON-RPC 报文就说明 Spring AI 确实把工具调用委派给了 12306-mcp。原文章节里那个STDERR Message received: 12306 MCP Server running on stdio日志在 Stdio 模式下是判断本地 MCP server 是否被拉起来的关键。SSE 模式下没有这行日志要看 Spring AI 有没有建立 SSE 连接成功以及 MCP 工具列表有没有注册到 ChatClient 的工具仓库里。链路通了之后去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的控制台核对一下本次调用的用量记录。重点看这段时间有没有新增的请求数以及模型名、token 消耗数据。这一步能确认请求确实从 TaoToken 通道出去了而不是走了什么绕过路径。5. 换通道后的排障401、404、MCP 静默不触发把模型通道从 DeepSeek 官方切到 TaoToken 后最常见的三个问题其实都集中在模型通道不在 MCP 侧。逐个说清楚。5.1 401 与模型 ID 不对Spring AI 启动时报 401 Unauthorized十有八九是api-key没填对。检查环境变量TAOTOKEN_API_KEY是否真的已导出以及 Key 是否在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 上正常创建且没有过期。如果 401 之外还带一条类似 model not found 的信息则是模型 ID 不对。spring.ai.deepseekstarter 会有默认模型名但 TaoToken 兼容通道上不一定有这个默认模型。去模型广场复制一个当前可用的模型 ID在 application.yml 的 deepseek 段补上model: 模型ID。不要凭记忆填不同模型名之间的映射关系以模型广场为准。5.2 404 与 MCP 静默不触发请求能发出去但返回 404先检查base-url是不是被拼成了https://taotoken.net/api/v1或者多了其他路径。TaoToken 的 Base URL 就是 https://taotoken.net/api末尾不要加/v1Spring AI 会自动拼接具体的调用路径。MCP 静默不触发是另一种情况应用启动正常chat-stream 接口也有响应但返回内容是大模型自己的知识完全没有调用工具。原因通常是 prompt 里没有触发工具调用的信息或者工具列表根本没有注册进来。分别排查确认 ToolCallbackProvider 已经在 ChatClient 构建时通过.defaultToolCallbacks()注入确认 application.yml 里 MCP client 的 enabled 是 true把日志级别调到 DEBUG看 Spring AI 是否在启动时把 12306-mcp 的工具 schema 打印出来了。Stdio 模式还要额外检查 mcp-server.json 的语法。Mac/Linux 上command: npxWindows 上必须用cmd /c npx包装否则子进程起不来日志里会一直卡在等待 STDIO 输出的状态。整套配置跑通之后最直接的收益是模型 Key 的维护成本降下来了。Spring AI 的 MCP 接入逻辑没变变的只是模型通道这一段。以后如果再换模型不需要动 MCP 配置只需要去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建一把新 Key 或者换个模型 IDapplication.yml 里改两行重启即可。建议你拿今天的 12306-mcp 工程先试一次完整流程把 SSE 和 Stdio 两种模式都跑一遍用量记录会对得上。
返回列表