☰
Spring AI 干货笔记:STDIO 与 SSE MCP 服务器接入 TaoToken 实战
2026/10/2 11:41:02 网站建设 项目流程

1. 为什么 Spring AI 项目里 MCP 服务器总接不通

Spring AI 的 MCP(Model Context Protocol)服务器接入,是这两年在 Java 生态里被问得最多的一类问题。它本质上解决的是「让大模型能调用你本地或远端工具」这件事:模型负责理解意图,MCP 服务器负责把工具、资源、提示词以标准协议暴露出去,客户端再把这些能力挂到对话链路上。听起来很顺,但真到落地,很多人卡在第一步——传输模式选错、依赖引错、配置项写错,启动日志里一堆transport相关报错,接口却始终返回空。

我见过最典型的场景是这样的:一个 Spring Boot 项目里同时引了spring-ai-starter-mcp-server-webmvc和spring-ai-starter-mcp-server-webflux,本地跑起来看着没事,一调工具就发现 SSE 端点连不上;还有人把 STDIO 服务器当成 HTTP 服务去访问,结果自然是 connection refused。更隐蔽的是模型调用链路——MCP 服务器本身通了,但模型侧没有统一入口,Key 散落在各个配置文件里,换一个模型就要改一遍代码。

这篇笔记就聚焦两件事:STDIO 与 SSE 两类 MCP 服务器在 Spring AI 里到底怎么配、怎么验证;以及如何用 TaoToken 的统一 Key/API 通道把模型调用链路收拢成一条。目标很明确——一次跑通两种传输模式的 MCP 服务对接,配置片段可以直接复制。

先说清楚适用人群:如果你正在用 Spring AI 做 Agent、工具调用、RAG 之外的上下文扩展,或者你手里有一堆 Python/Node 写的 MCP 工具想接进 Java 服务,这篇就是给你写的。不需要你之前用过 MCP,但需要你会基本的 Spring Boot 配置和 Maven 依赖管理。

STDIO 和 SSE 的区别,用一句话概括:STDIO 是「进程内管道」,客户端启动服务器进程,通过标准输入输出通信,适合命令行工具和桌面场景;SSE 是「HTTP 长连接」,服务器作为 Web 服务暴露端点,客户端通过 Server-Sent Events 接收消息,适合多客户端、跨网络的场景。选错模式,后面所有配置都是白费。

2. TaoToken 统一通道的前置准备与依赖选型

在动手配 MCP 之前,先把模型调用这条链路理清楚。Spring AI 本身支持多种模型提供商,但如果你项目里同时用 OpenAI 兼容接口、Anthropic、或者其他模型,每个都要单独配 Key、单独写 base-url,维护成本很高。TaoToken 在这里的角色是一个统一的 API 通道:你只需要一个 Key、一个 Base URL,就能在 Spring AI 里切换不同模型,MCP 服务器暴露的工具也能挂到同一条链路上。

前置准备分三步。第一步,拿到 Key。访问https://taotoken.net/api-keys(带 utm 参数:?utm_source=taotoken_aicg_blog_end&utm_content=mcp_stdio_sse&utm_campaign=rewrite)创建 API Key,复制保存。第二步,确认 Base URL 是https://taotoken.net/api,注意这个地址不加任何 UTM 参数,直接用于代码里的base-url配置。第三步,想清楚你要接的模型 ID,比如gpt-4o、claude-3-5-sonnet这类,后面配置里会用到。

依赖选型是这一步最容易踩坑的地方。Spring AI 的 MCP 服务器 starter 有三个:

Starter传输方式适用场景关键依赖
spring-ai-starter-mcp-serverSTDIO命令行、桌面工具、无 Web 依赖无额外 Web 依赖
spring-ai-starter-mcp-server-webmvcSSE(Spring MVC)传统 Servlet 项目spring-boot-starter-web
spring-ai-starter-mcp-server-webfluxSSE(Spring WebFlux)响应式项目spring-boot-starter-webflux

这里有个官方文档里明确提醒过的坑:如果你的类路径里同时存在DispatcherServlet和DispatcherHandler,Spring Boot 会优先用DispatcherServlet。也就是说,你项目里如果已经引了spring-boot-starter-web,就别再用webflux那个 starter,否则 SSE 端点行为会和你预期不一致。我实测下来,最稳的做法是:Servlet 项目用webmvc,纯响应式项目用webflux,别混。

Maven 依赖片段(以 WebMVC 为例):

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

如果你要同时支持 STDIO 和 SSE,可以在 WebMVC starter 基础上,通过spring.ai.mcp.server.stdio=true开启 STDIO 传输。这样同一个服务器既能被命令行客户端以 STDIO 方式拉起,也能通过 HTTP SSE 端点访问。注意,这个开关默认是关的,不开的话 STDIO 客户端连不上。

模型侧依赖,如果你用 OpenAI 兼容协议接 TaoToken,加:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency>

到这里,依赖和 Key 都齐了。下一步进入配置环节,这也是全文最核心的部分。

3. 可复制的 application.yml 与 MCP 客户端配置

配置分两块:MCP 服务器自身的配置,以及模型调用链路(TaoToken)的配置。先给一份完整的application.yml,你可以直接复制改。

server: port: 8080 spring: ai: # TaoToken 统一模型通道 openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o temperature: 0.7 # MCP 服务器配置 mcp: server: name: spring-ai-mcp-server version: 1.0.0 type: SYNC instructions: "This server provides weather and system info tools" capabilities: tool: true resource: true prompt: true completion: true # SSE 相关 sse-message-endpoint: /mcp/messages keep-alive-interval: 30s # 同时开启 STDIO 传输 stdio: true

几个关键点解释一下。base-url必须是https://taotoken.net/api,不要带任何查询参数;api-key用环境变量注入,别硬编码在文件里。type: SYNC表示同步服务器,如果你用 WebFlux 响应式,可以改成ASYNC。sse-message-endpoint是 SSE 的消息端点路径,客户端会往这个路径发消息。keep-alive-interval是保活间隔,默认关闭,设成30s后服务器会定期给客户端发 ping,防止长连接被中间层断开。

如果你只想跑 STDIO,不需要 SSE,那配置可以简化成:

spring: ai: mcp: server: name: stdio-mcp-server version: 1.0.0 type: SYNC

对应的依赖换成spring-ai-starter-mcp-server,不需要 Web 依赖。

接下来是 MCP 客户端配置。Spring AI 的 MCP 客户端 starter 是spring-ai-starter-mcp-client,配置方式有两种:STDIO 客户端和 SSE 客户端。STDIO 客户端配置:

spring: ai: mcp: client: stdio: connections: weather-server: command: java args: - -jar - /path/to/your-mcp-server.jar

SSE 客户端配置:

spring: ai: mcp: client: sse: connections: weather-server: url: http://localhost:8080 sse-endpoint: /sse

注意sse-endpoint默认是/sse,如果你服务器端改了路径,这里要对应改。客户端连接名weather-server可以自定义,后面在代码里通过这个名字拿工具。

模型调用侧,如果你想在代码里显式指定 TaoToken 通道,可以这样写:

@Bean public ChatClient chatClient(OpenAiChatModel chatModel) { return ChatClient.builder(chatModel) .defaultSystem("You are a helpful assistant with access to MCP tools.") .build(); }

OpenAiChatModel会自动读取spring.ai.openai下的配置,也就是我们上面写的 TaoToken 地址和 Key。这样模型调用和 MCP 工具调用就走同一条链路了。

这里补一句关于 Claude Code 的配置,如果你同时用 Claude Code 做开发,它的settings.json里也可以配 TaoToken:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "your-token-here" } }

三件套记牢:Base URL 是https://taotoken.net/api,Key 从https://taotoken.net/api-keys拿,Model ID 按你实际用的填。这三样对齐了,模型侧就不会出问题。

4. 启动验证:日志、SSE 端点与工具调用实测

配置写完,启动项目,重点看三处:启动日志、SSE 端点、工具调用返回。

启动日志里,你应该能看到类似这样的输出:

Registered MCP tool: getWeather MCP server started with name: spring-ai-mcp-server SSE endpoint available at: /sse STDIO transport enabled

如果没看到Registered MCP tool,说明你的ToolCallbackProviderBean 没被扫描到,检查一下@Bean方法是否在@SpringBootApplication扫描范围内。如果没看到SSE endpoint available,检查依赖是不是引成了纯 STDIO 的 starter。

SSE 端点验证,用 curl 直接连:

curl -N http://localhost:8080/sse

正常的话会保持连接并输出事件流,类似:

event: endpoint data: /mcp/messages?sessionId=xxx

这个sessionId是后续发消息要用的。如果你看到 404,检查sse-message-endpoint配置和实际请求路径是否一致。如果连接立刻断开,看日志里有没有keep-alive相关报错。

工具调用验证,写一个简单的 Controller:

@RestController public class McpTestController { private final ChatClient chatClient; public McpTestController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping("/test-tool") public String testTool(@RequestParam String city) { return chatClient.prompt() .user("What's the weather in " + city + "?") .call() .content(); } }

启动后访问http://localhost:8080/test-tool?city=Beijing,如果返回里包含天气信息,说明模型成功调用了 MCP 工具。如果返回的是「I don't have access to weather data」,说明工具没挂上,检查 MCP 客户端连接配置。

STDIO 模式的验证稍微不同。你需要用 MCP 客户端以 STDIO 方式拉起服务器进程,比如用官方的 MCP Inspector 工具,或者自己写一个简单的 STDIO 客户端。日志里会显示STDIO transport initialized,然后你通过标准输入发 JSON-RPC 请求,标准输出会返回结果。

实测下来,最容易出问题的是模型侧和 MCP 侧的 Key 混用。有人把 TaoToken 的 Key 配到了 MCP 服务器配置里,或者反过来,结果两边都报 401。记住:TaoToken 的 Key 只用于模型调用,MCP 服务器本身不需要 Key(除非你开了安全认证)。

5. 常见报错排查:401、local proxy failed 与 choices 解析

这一节列几个真实遇到过的报错,对照着排查。

401 Unauthorized。这个最常见,两种可能:一是 TaoToken Key 没配或配错,检查spring.ai.openai.api-key是否读到了环境变量;二是 Key 过期或被禁用,去https://taotoken.net/api-keys确认状态。如果日志里出现401且伴随invalid_api_key,基本就是 Key 问题。

local proxy failed / connection refused。这个通常出现在 SSE 客户端连服务器时。检查三点:服务器是否真的启动了(看端口监听)、url配置是否带了http://前缀、sse-endpoint路径是否和服务器端一致。如果是 STDIO 模式报这个,检查command和args是否能正确拉起进程,路径别写相对路径。

Error reading choices / choices is null。这是模型返回解析失败,多半是 TaoToken 通道返回的格式和 Spring AI 预期不一致。检查base-url是不是写成了https://taotoken.net/api/(末尾多了斜杠),或者模型 ID 写错了。还有一种情况是你用的模型不支持 chat completions 格式,换一个模型试试。

OAuth / authentication failed。如果你在 MCP 客户端配置里开了 OAuth,但服务器端没配对应的认证,就会报这个。MCP 服务器的安全认证是可选的,初期调试建议先关掉,跑通再说。

No tool callbacks registered。工具没注册上。检查你的ToolCallbackProviderBean 是否返回了非空列表,以及@Tool注解的方法是否是 public 的。Spring AI 只会扫描 public 方法。

SSE connection closed unexpectedly。长连接被断开,多半是keep-alive-interval没设,或者中间有反向代理超时。设成30s试试,如果还不行,检查代理层的 read timeout。

排查顺序建议:先看启动日志有没有报错,再用 curl 测 SSE 端点,最后测工具调用。一层层往下,别跳步。

6. 把两种传输模式收进同一条链路

STDIO 和 SSE 不是二选一的关系。实际项目里,我更推荐的做法是:用 WebMVC starter 起一个 SSE 服务器,同时开stdio: true,这样命令行工具和 Web 客户端都能接。模型侧统一走 TaoToken 通道,Key 和 Base URL 只维护一份。

如果你要长期跑 Agent 类任务,建议把 Coding Plan 也用上,https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=mcp_stdio_sse&utm_campaign=rewrite,它适合需要持续调用模型、频繁触发工具的场景,比按次调用更划算。模型对话调试可以用https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=mcp_stdio_sse&utm_campaign=rewrite快速验证通道是否正常。接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=mcp_stdio_sse&utm_campaign=rewrite,遇到配置项不确定的时候翻一下。

最后留一个我踩过的坑:别在application.yml里同时配spring.ai.mcp.server.stdio=true和spring.ai.mcp.client.stdio,前者是服务器开 STDIO 传输,后者是客户端连 STDIO 服务器,两个概念,配混了会互相干扰。服务器和客户端分开配,各管各的。

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

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

立即咨询