【MCP教程系列】SpringBoot 用 Spring AI 搭建 SSE 模式 MCP 服务并改到 TaoToken

发布时间:2026/10/18 16:24:38
【MCP教程系列】SpringBoot 用 Spring AI 搭建 SSE 模式 MCP 服务并改到 TaoToken
1. 为什么要在 SpringBoot 里搭 SSE 模式的 MCP 服务MCP 全称 Model Context Protocol你可以把它理解成一套“让模型调用外部工具”的通用插座标准。模型本身只会聊天但通过 MCP它能去查数据库、调接口、跑脚本。而 SSE 模式就是这套插座里“服务端主动往客户端推消息”的那一种传输方式。传统 HTTP 请求是“你问一句我答一句”模型调用工具时如果工具执行要好几秒中间状态你完全看不到。SSE 不一样它是一条长连接服务端可以持续把“工具开始执行”“中间日志”“最终结果”一段段推回来。对于需要流式展示工具调用过程的场景比如文章润色、批量生成标题SSE 的体验明显更顺。这篇要解决的问题很具体用 SpringBoot Spring AI 搭一个 SSE 模式的 MCP 服务然后把它的模型调用通道统一改到 TaoToken 上。适合谁看如果你手上有本地工具想暴露给模型又不想每个工具都单独配一套 Key 和地址那这套路径就是给你准备的。我试过把工具逻辑和模型通道拆开配置结果发现最省心的做法是MCP 服务只负责“暴露工具”模型调用统一走一个兼容 OpenAI 协议的入口。TaoToken 正好提供这个入口Base URL 是https://taotoken.net/api模型 ID 和 Key 在控制台拿。这样你的 SpringBoot 项目里只需要维护一份模型配置工具代码不用动。整个闭环分四步建项目引依赖、写工具方法、配 SSE 端点、启动验证。下面一步步来每段都有可复制的代码和配置。2. 前置准备Spring AI 依赖与 TaoToken 接入参数怎么配先把环境对齐。JDK 17 是底线Spring AI 1.0.0-M6 对 JDK 版本有要求低于 17 会在编译期报Unsupported class file major version。Maven 用 3.8.6 以上避免依赖解析时出现仓库元数据拉取失败。2.1 pom.xml 依赖片段核心是两个 starter一个负责 MCP 服务端的 WebMVC 暴露一个负责 OpenAI 兼容的模型调用。注意版本号要和 Spring AI 的 BOM 对齐否则会出现NoSuchMethodError。properties java.version17/java.version spring-ai.version1.0.0-M6/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-webmvc-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement2.2 application.yml 里的 TaoToken 参数这里的关键是把 OpenAI 的 base-url 指向 TaoToken 的 API 地址而不是默认的官方地址。模型 ID 填你在控制台看到的那个Key 用sk-开头的那串。server: port: 8080 spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.7 mcp: server: name: springboot-sse-mcp version: 1.0.0 sse-endpoint: /sse sse-message-endpoint: /mcp/message注意api-key不要硬编码进 yml 提交到仓库。用环境变量TAOTOKEN_API_KEY注入本地跑的时候在 IDE 的 Run Configuration 里加或者用export TAOTOKEN_API_KEYsk-xxx。Key 的获取路径是控制台里的 API Keys 页面模型 ID 在模型对话页面能看到当前可用的列表。如果你打算长期跑编码类 AgentCoding Plan 那条通道的额度策略更适合高频调用普通验证用按量计费就够。2.3 三件套对照表配置项值说明Base URLhttps://taotoken.net/api兼容 OpenAI 协议末尾不加/v1API Keysk-开头控制台 API Keys 页获取Model ID如gpt-4o-mini模型对话页查看可用列表这三样在 Spring AI 里分别对应base-url、api-key、chat.options.model。配错任何一个启动时不一定报错但第一次调用工具就会抛异常。3. 可复制配置ToolServer 工具类与 SSE 端点注册工具类是 MCP 服务的核心。Spring AI 用Tool注解把一个普通 Java 方法标记成模型可调用的工具方法参数和返回值会被自动转成 JSON Schema 描述给模型。3.1 ToolServer.java 完整代码这里定义两个工具批量生成标题和文章润色。两个方法都通过注入的ChatClient调模型而ChatClient底层走的就是上面配的 TaoToken 通道。package com.example.mcp; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Service; Service public class ToolServer { private final ChatClient chatClient; public ToolServer(ChatClient.Builder builder) { this.chatClient builder.build(); } Tool(description 根据主题批量生成 5 个文章标题返回 JSON 数组) public String generateTitles(String topic) { String prompt 请围绕主题「 topic 」生成 5 个吸引人的中文标题 以 JSON 数组格式返回每个元素是一个字符串。; return chatClient.prompt() .user(prompt) .call() .content(); } Tool(description 对给定文章进行润色保持原意提升可读性) public String polishArticle(String content) { String prompt 请对以下文章进行润色保持原意不变让表达更流畅自然\n content; return chatClient.prompt() .user(prompt) .call() .content(); } }Tool的description很重要模型靠它判断什么时候该调这个工具。描述写得太模糊模型可能该调的时候不调或者乱调。3.2 注册工具提供者Spring AI 需要一个ToolCallbackProvider把工具类里的方法注册进去。写一个配置类package com.example.mcp; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class McpConfig { Bean public ToolCallbackProvider toolCallbackProvider(ToolServer toolServer) { return MethodToolCallbackProvider.builder() .toolObjects(toolServer) .build(); } }这个 Bean 一注册MCP 服务端启动时就会扫描ToolServer里所有带Tool的方法把它们暴露成 SSE 端点可调用的工具。3.3 SSE 端点说明application.yml里配的sse-endpoint: /sse是客户端建立长连接的地址sse-message-endpoint: /mcp/message是客户端发送工具调用请求的地址。两个端点分工不同前者是服务端往客户端推后者是客户端往服务端发。启动后你可以用 curl 先探一下 SSE 端点是否活着curl -N http://localhost:8080/sse-N是关闭 curl 的缓冲让你能实时看到推送内容。正常的话会先收到一条event: endpoint的消息里面带着 message 端点的完整路径。4. 验证请求启动服务并跑通一次工具调用配置写完启动类就是标准的 SpringBoot 入口package com.example.mcp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class McpApplication { public static void main(String[] args) { SpringApplication.run(McpApplication.class, args); } }4.1 启动与日志观察运行mvn spring-boot:run控制台会打印 MCP 服务注册的工具列表。看到类似Registered tools: [generateTitles, polishArticle]就说明工具挂载成功。如果这里没打印八成是ToolCallbackProvider没被扫描到检查配置类包路径是否在启动类的同级或子包下。4.2 用 MCP 客户端测试工具调用最直接的验证方式是写一个测试类用 Spring AI 的 MCP 客户端连本地 SSE 端点package com.example.mcp; import org.junit.jupiter.api.Test; import org.springframework.ai.mcp.client.McpClient; import org.springframework.ai.mcp.client.transport.SseClientTransport; public class McpClientTest { Test void testGenerateTitles() { var transport new SseClientTransport(http://localhost:8080/sse); var client McpClient.sync(transport).build(); client.initialize(); var result client.callTool(generateTitles, {\topic\:\SpringBoot 入门\}); System.out.println(工具返回: result); } }跑通后你会看到模型返回的 JSON 数组里面是 5 个标题。这一步成功说明从 SpringBoot 到 TaoToken 的整条链路是通的。4.3 验证 SSE 流式响应工具调用本身是请求-响应但 SSE 的价值在于过程推送。你可以在ToolServer的方法里加日志观察服务端是否在工具执行期间持续往连接里写数据。更直观的做法是用浏览器访问http://localhost:8080/sse打开开发者工具的 EventStream 面板能看到event: message一条条进来。如果模型返回内容里出现choices字段解析失败通常是 base-url 末尾多写了/v1。TaoToken 的地址就是https://taotoken.net/apiSpring AI 会自己拼/v1/chat/completions你手动加反而会变成/api/v1/v1/...。5. 本篇常见错排查401、local proxy failed 与 choices 解析异常接入过程里最容易卡住的就那几个报错逐个说清楚。5.1 401 Unauthorized报错长这样org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]原因就三种Key 没注入、Key 写错、Key 对应的额度用尽。先确认环境变量TAOTOKEN_API_KEY在当前 shell 里能echo出来。如果用的是 IDE检查 Run Configuration 的 Environment variables 里有没有加。Key 本身去控制台 API Keys 页重新复制一次注意别把前后空格带进去。5.2 local proxy failed / Connection refusedjava.net.ConnectException: Connection refused这个报错说明请求根本没发出去。检查base-url是不是写成了http://而不是https://或者端口被本地其他服务占了。还有一种情况是公司网络对出站请求做了限制这种需要走正常的网络配置流程不在代码层面解决。5.3 reading choices 解析失败com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field choices或者反过来报找不到choices字段。前者通常是响应体结构和 Spring AI 预期的不一致后者多半是 base-url 拼错导致返回了 HTML 错误页。确认base-url是https://taotoken.net/api模型 ID 是控制台里真实存在的那个。模型 ID 写错时有些网关会返回一个结构不同的错误体Jackson 解析时就炸了。5.4 OAuth 相关报错如果你在日志里看到OAuth字样说明请求被路由到了需要 OAuth 认证的端点。Spring AI 的 OpenAI starter 默认走 API Key 认证不会触发 OAuth。出现这个报错一般是 base-url 被改成了某个需要 OAuth 的地址。改回https://taotoken.net/api即可。5.5 工具没被调用模型回复了文字但没有触发工具。先看Tool的 description 是否足够明确再确认ToolCallbackProviderBean 是否真的注册了。可以在启动日志里搜Registered tools没有这行就是注册失败。6. 把 MCP 服务接到统一通道CTA 与后续动作服务跑通之后你手上就有了一个本地 SSE 模式的 MCP 服务工具逻辑在ToolServer里模型通道在application.yml里。后续要加新工具只需要在ToolServer里加一个带Tool的方法重启即可不用碰通道配置。如果你要把这个服务接到 Claude Code 或 Cline 这类客户端配置里需要填全三件套Base URL 用https://taotoken.net/apiKey 用控制台拿的那串Model ID 填你验证过的模型。SSE 端点则填你本地或部署后的/sse地址。几个常用的入口放这里按需取拿 Key 和看额度API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys查接入参数和协议细节接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc快速验证模型是否可用模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentchat长期跑编码类 AgentCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan最后说个实际踩过的坑SSE 连接在本地开发时容易因为 IDE 的热重启断掉表现为客户端一直收不到event: endpoint。解决办法是关掉spring-boot-devtools的自动重启或者把 SSE 端点排除在重启监听之外。这个坑不涉及代码逻辑但排查起来很费时间提前知道能省不少事。