Spring AI 调 MCP 服务,模型通道走 TaoToken 跑通
2026/9/14 3:57:42 网站建设 项目流程

Spring AI 1.0.3 工程接 12306-mcp 时,MCP 客户端配置本身不复杂,复杂的是模型通道:DeepSeek 的 Key 要单独申请、单独维护,而 TaoToken 提供统一 API 兼容通道(https://taotoken.net/?utm_source=taotoken_aicg_blog_end),可以把deepseek.base-url换成 https://taotoken.net/api,api-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.xml:MCP client 和 deepseek starter 缺一不可

先确认工程里这几个依赖都在,Spring AI 1.0.3 的 MCP 客户端、DeepSeek 模型 starter、Web 和 Lombok 缺一不可:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.7</version> </parent> <groupId>org.example</groupId> <artifactId>spring-ai-mcp</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-deepseek</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>

spring-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_source=taotoken_aicg_blog_end 模型广场展示的为准,不要在配置文件里凭感觉填一个,否则调用时会报模型不存在。

1.3 application.yml:sse 段保持,deepseek.base-url 指向 TaoToken

下面这份配置就是 SSE 方案的完整形态,和原文相比只改了两行:deepseek.base-url从 DeepSeek 官方地址换成 https://taotoken.net/api,api-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的末尾不要加/v1,TaoToken 的兼容通道地址就是 https://taotoken.net/api 本身,加了反而会拼出错误路径。

配置类不用动,仍然是那个把 ToolCallbackProvider 接进 ChatClient 的 AppConfig:

@Configuration 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;charset=utf-8") public Flux<String> stream(String msg, String chatId) { return chatClient.prompt() .user(msg) .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, chatId)) .stream() .content(); } }

到这里,SSE 方案的改造就结束了。MCP 服务器远程跑在 ModelScope,Spring AI 负责把 12306 的列车查询能力注册成工具,TaoToken 负责把大模型请求送到兼容通道,三者各管一段。

2. Stdio 本地跑 12306-mcp:mcp-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 i

Spring AI 会用 npx -y 12306-mcp 这种命令在本地拉起服务,所以 npx 必须是全局命令。如果安装过 Node 但没有 npx,执行一次:

npm i -g npx

在 Windows 上还要注意,Spring AI 的 StdioClientTransport 是通过系统命令解释器启动子进程的,Mac/Linux 直接用 npx,Windows 需要 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-key:

spring: 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: synctype: 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-http

streamable-http 是 MCP 规范里相对新的传输方式,它在同一个 HTTP 连接里支持请求-响应和流式响应,比 SSE 的长期连接更省资源。但 Spring AI 客户端对这个协议的适配是从 1.1.0 版本才开始的。如果你拿 1.0.3 去配streamable-http,Spring 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日从广州南到北京西的高铁车次&chatId=001"

msg 参数尽量给完整信息:日期、出发地、目的地。这样大模型经过 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_source=taotoken_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_source=taotoken_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,末尾不要加/v1,Spring 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: npx,Windows 上必须用cmd /c npx包装,否则子进程起不来,日志里会一直卡在等待 STDIO 输出的状态。

整套配置跑通之后,最直接的收益是模型 Key 的维护成本降下来了。Spring AI 的 MCP 接入逻辑没变,变的只是模型通道这一段。以后如果再换模型,不需要动 MCP 配置,只需要去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建一把新 Key 或者换个模型 ID,application.yml 里改两行重启即可。建议你拿今天的 12306-mcp 工程先试一次完整流程,把 SSE 和 Stdio 两种模式都跑一遍,用量记录会对得上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询