1. 从 stdio 到 SSEMCP 客户端接入到底卡在哪MCPModel Context Protocol是让大模型调用外部工具的一套协议Spring AI 从 1.0.0-M6 开始把服务端和客户端都做成了 starter理论上引个依赖、写个Tool就能跑。但真正动手时很多人会卡在同一个地方本地 stdio 模式跑通了想换成 SSE 远程通道客户端却连不上或者连上了工具列表是空的。这个问题的根源在于 stdio 和 SSE 是两套完全不同的传输机制。stdio 靠标准输入输出流通信客户端要负责把服务端进程拉起来所以配置里写的是command和argsSSE 走 HTTP服务端得先独立跑起来监听端口客户端只填一个url就行。两种模式的配置文件、启动参数、依赖开关都不一样混着配就会出问题。这篇就按「先 stdio 跑通、再切 SSE」的顺序把 Spring Boot 项目里用 Spring AI 接入 MCP 的完整链路走一遍。你会看到可复制的 pom 依赖、两套 application.yml 骨架、MCP 客户端 Bean 代码以及启动后怎么调用工具、怎么验证 SSE 连接、stdio 怎么回退。适合已经在写 Spring Boot、想给项目接上 MCP 工具能力的后端同学。2. 前置准备依赖、Key 与 TaoToken 接入在写代码之前先把两件事准备好Maven 依赖和模型侧的接入凭证。服务端这边核心依赖是spring-ai-mcp-server-webmvc-spring-boot-starter它引入后会自动注册 SSE 端点包括消息端点和 SSE 传输端点。默认开启 SSE 传输要开 stdio 得通过参数显式打开。客户端这边对应的是spring-ai-mcp-client-spring-boot-starter。两个都用1.0.0-M6版本版本号要对齐不然会出现 Bean 找不到的情况。模型侧如果你打算让 MCP 工具真正被大模型调用需要一个能走 OpenAI 兼容协议的服务。我这边用的是 TaoToken它的 API 地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions格式Spring AI 的 OpenAI starter 直接改 base-url 就能接。先去控制台建一个 API Key后面配置里会用到。!-- 服务端MCP Server WebMVC自动注册 SSE 端点 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-webmvc-spring-boot-starter/artifactId version1.0.0-M6/version /dependency !-- 客户端MCP Client -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-spring-boot-starter/artifactId version1.0.0-M6/version /dependency !-- 工具库示例里用 hutool 发 HTTP 请求 -- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.25/version /dependency拿 Key 的入口在控制台的 API Keys 页面建完之后复制出来注意别提交到 Git。如果你还没决定用哪个模型可以先去模型对话页面试一下返回格式确认接口通不通再写进配置。3. 可复制配置两套 yml 与 MCP 客户端 Bean配置这块是整个接入最容易出错的地方因为 stdio 和 SSE 的开关是互斥的。我的做法是拆成三个文件application.yml做主入口和端口application-stdio.yml和application-sse.yml分别管两种模式靠spring.profiles.active切换。先看 stdio 模式。stdio 靠标准输入输出通信所以必须关掉 Web 容器否则 Tomcat 一起来就会抢占标准流导致协议握手失败。# application-stdio.yml spring: ai: mcp: server: name: image-search-mcp-server version: 0.0.1 type: SYNC stdio: true # 核心开关开启 stdio 传输 main: web-application-type: none # 关闭 Web 服务stdio 模式必须 banner-mode: off再看 SSE 模式。SSE 是 HTTP 长连接服务端要独立监听端口所以stdio必须关掉同时保留 Web 容器。# application-sse.yml spring: ai: mcp: server: name: image-search-mcp-server version: 0.0.1 type: SYNC stdio: false # 关闭 stdio启用 SSE server: port: 8127 # SSE 模式下的监听端口主配置文件负责激活哪套# application.yml spring: application: name: image-search-mcp-server profiles: active: stdio # 默认走 stdio切 SSE 改成 sse服务端的工具类用Tool注解标注方法Spring AI 会自动扫描并注册。这里以图片搜索为例方法体里发 HTTP 请求、解析 JSON、返回 URL 列表Service public class ImageSearchTool { private static final String API_KEY System.getenv(IMAGE_API_KEY); private static final String API_URL https://api.pexels.com/v1/search; Tool(description search image from web) public String searchImage(ToolParam(description Search query keyword) String query) { try { return String.join(,, searchMediumImages(query)); } catch (Exception e) { return Error search image: e.getMessage(); } } public ListString searchMediumImages(String query) { MapString, String headers new HashMap(); headers.put(Authorization, API_KEY); MapString, Object params new HashMap(); params.put(query, query); String response HttpUtil.createGet(API_URL) .addHeaders(headers) .form(params) .execute() .body(); return JSONUtil.parseObj(response) .getJSONArray(photos) .stream() .map(photoObj - (JSONObject) photoObj) .map(photoObj - photoObj.getJSONObject(src)) .map(photo - photo.getStr(medium)) .filter(StrUtil::isNotBlank) .collect(Collectors.toList()); } }工具注册靠一个ToolCallbackProviderBean放在主类里SpringBootApplication public class ImageSearchMcpServerApplication { public static void main(String[] args) { SpringApplication.run(ImageSearchMcpServerApplication.class, args); } Bean public ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) { return MethodToolCallbackProvider.builder() .toolObjects(imageSearchTool) .build(); } }客户端这边stdio 模式用mcp-servers.json描述怎么拉起服务端进程{ mcpServers: { image-search-mcp-server: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -Dspring.main.web-application-typenone, -Dlogging.pattern.console, -jar, image-search-mcp-server/target/image-search-mcp-server-0.0.1-SNAPSHOT.jar ], env: {} } } }切到 SSE 时客户端配置换成 URL 形式同时把 stdio 那段注释掉避免端口冲突spring: ai: mcp: client: sse: connections: server1: url: http://localhost:8127 # stdio: # servers-configuration: classpath:mcp-servers.json4. 验证请求调用工具与确认 SSE 连接配置写完先验证 stdio。用 Maven 打包服务端生成可执行 JARmvn clean package -DskipTests打包完成后客户端启动时会按mcp-servers.json里的java -jar命令把服务端进程拉起来。写个单元测试确认工具能被发现SpringBootTest class McpClientStdioTest { Autowired private ChatClient chatClient; Test void testImageSearchTool() { String result chatClient.prompt() .user(帮我搜一张关于 mountain 的图片) .call() .content(); System.out.println(result); } }如果 stdio 通了控制台会打印出模型调用工具后返回的图片 URL。接下来切 SSE先把application.yml的active改成sse单独启动服务端看到日志里出现 SSE 端点注册信息就说明服务端起来了。java -jar image-search-mcp-server/target/image-search-mcp-server-0.0.1-SNAPSHOT.jar --spring.profiles.activesse服务端启动后用 curl 直接探一下 SSE 端点是否可达curl -N http://localhost:8127/sse正常的话会看到event: endpoint和一条data:消息里面带着消息端点的路径。这一步能通说明 SSE 通道本身没问题。然后启动客户端同样跑上面的单元测试工具列表里应该能看到searchImage。如果你用的是 TaoToken 接模型客户端里模型配置大概长这样spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini把TAOTOKEN_API_KEY设成环境变量别写死在 yml 里。5. 本篇常见错排查报错一No tool callbacks found或工具列表为空。九成是ToolCallbackProviderBean 没注册或者Tool注解的方法所在类没被 Spring 扫描到。检查主类包路径是否覆盖了 tools 包Bean 方法名别和已有 Bean 冲突。报错二stdio 模式下客户端启动后立刻退出。通常是服务端 JAR 路径写错了或者web-application-type没设成none。stdio 模式下 Web 容器一起来就会抢标准流协议握手直接失败。确认mcp-servers.json里的-jar后面是绝对路径或正确的相对路径。报错三SSE 连接超时或 404。先确认服务端是不是用sseprofile 启动的再确认端口没被占用。SSE 模式下stdio必须是false如果两个都开着服务端行为会不确定。另外客户端配置里 stdio 那段一定要注释掉否则客户端会尝试拉起本地进程和远程 SSE 冲突。报错四模型不调用工具直接编答案。这多半是模型侧的问题不是 MCP 的问题。确认你用的模型支持 function callingbase-url 和 api-key 正确。可以先用模型对话页面单独测一下模型返回排除模型本身的问题。报错五切换 profile 后配置没生效。Spring Boot 的 profile 激活优先级是命令行 环境变量 application.yml。如果你在命令行传了--spring.profiles.activesseapplication.yml里的active: stdio会被覆盖这是正常的。排查时用--debug启动看日志里实际加载了哪个 profile。6. 下一步把 MCP 接进你的编码流stdio 和 SSE 都跑通之后你会发现 MCP 的价值不在于单个工具而在于它能被 Agent 反复调用。如果你打算把 MCP 工具接进日常编码流程比如让模型在写代码时自动查文档、搜图片、调内部接口可以考虑用 Coding Plan 把模型调用和工具链统一管起来省得每个项目都单独配一遍 Key 和 base-url。接入过程中如果遇到工具注册、SSE 握手、模型不调用这类问题先去接入文档对照配置项大部分坑都在 profile 切换和 stdio/SSE 开关的互斥上。需要新建 Key 或者管理多个项目的凭证直接进 API Keys 页面操作就行。