ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MCP 协议实战:用 Spring AI 搭建 AI 工具生态的 STDIO 与 SSE 配置

MCP 协议实战:用 Spring AI 搭建 AI 工具生态的 STDIO 与 SSE 配置 1. 为什么 Java 后端需要关心 MCP 的传输方式如果你已经用 Spring AI 的Tool注解让模型调用过本地方法会发现一个限制工具和 AI 应用绑死在同一个 JVM 里。想让另一个 AI 应用复用同一套工具或者让 Python 同事写的数据分析函数被 Java 侧的模型调用就得把工具从进程内搬到进程外。MCPModel Context Protocol解决的正是这个问题它把工具定义、发现、调用抽象成一套基于 JSON-RPC 2.0 的标准消息格式工具变成独立服务任何支持 MCP 的客户端都能发现并调用。Spring AI 1.1.x 提供了spring-ai-starter-mcp-server和spring-ai-starter-mcp-client两个 starter让你用写 Spring Boot 微服务的方式构建工具服务。但真正落地时第一个卡住大多数人的不是Tool怎么写而是传输层选 STDIO 还是 SSE——两者的配置骨架、启动方式、连通性验证动作完全不同配错了就是连接超时或者工具列表为空。这篇就把两种传输方式的差异拆开讲清楚给出可直接复制的配置骨架并用 TaoToken 的统一 Key 跑通一次完整的工具调用链路。适合谁看正在用 Spring AI 构建 AI 应用、需要把工具能力跨进程或跨语言复用的 Java 后端开发者。读完你能拿到两套可运行的配置知道什么场景选哪种传输以及连不上时先查哪里。2. TaoToken 前置统一 Key 与模型接入MCP 负责工具调用链路但模型本身还得有个入口。我试过在多个项目里分别维护不同厂商的 Key切换模型时改配置很烦后来统一走 TaoToken 的 API 入口一个 Key 覆盖对话和工具调用场景Spring AI 的 OpenAI starter 直接指向它就行。TaoToken 在这里的角色是模型能力的统一接入层不改变 MCP 协议本身的任何行为。你拿到的 Key 填进spring.ai.openai.api-keybase-url指向https://taotoken.net/apiSpring AI 就会把带 tools 字段的请求发过去模型返回工具调用意图再由 MCP Client 转发给 MCP Server。整条链路里 TaoToken 只负责模型那一跳。拿 Key 的路径登录后在控制台的 API Keys 页面创建建议按项目命名方便后面排查是哪个应用在调用。创建后复制保存页面关闭后不再完整显示。注意Key 不要硬编码进代码提交到仓库用环境变量注入下面配置里统一用${TAOTOKEN_API_KEY}占位。模型选择上工具调用需要模型支持 function calling。实测 deepseek-chat 这类模型在工具调用场景表现稳定配置里model字段按你实际可用的模型名填。3. 可复制配置STDIO 与 SSE 两套骨架先明确差异再动手。STDIO 模式下MCP Client 把 MCP Server 当作子进程启动通过标准输入输出收发 JSON-RPC 消息不需要网络端口适合本地工具、单机部署、延迟敏感的场景。SSE 模式下MCP Server 是一个独立运行的 HTTP 服务Client 通过 SSE 长连接接收消息、通过 HTTP POST 发送请求适合跨网络、多客户端共享、需要独立扩缩容的场景。3.1 项目结构与依赖两个独立 Spring Boot 项目mcp-server-demo提供工具mcp-client-demo集成 ChatClient 并连接 Server。Maven 依赖用 Spring AI BOM 统一版本。dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.1.6/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagementServer 侧依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server/artifactId /dependencyClient 侧依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency3.2 SSE 模式配置Server 的application.yml关键是type: SSE和消息端点spring: ai: mcp: server: name: ToolServer version: 1.0.0 type: SSE sse-message-endpoint: /mcp/messages server: port: 8081Client 的application.yml用url指向 Server 的 SSE 端点spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: deepseek-chat mcp: client: servers: my-tool-server: url: http://localhost:8081/mcp server: port: 80803.3 STDIO 模式配置STDIO 模式下 Server 不监听端口Client 负责拉起子进程。Server 侧把type改成STDIO去掉端口相关配置spring: ai: mcp: server: name: ToolServer version: 1.0.0 type: STDIOClient 侧不再用url而是用command指定启动 Server 的命令和参数spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: deepseek-chat mcp: client: stdio: connections: my-tool-server: command: java args: - -jar - /path/to/mcp-server-demo.jar注意STDIO 模式下 Server 进程由 Client 管理Server 里任何往标准输出打印的日志都会污染 JSON-RPC 消息流导致解析失败。务必把日志重定向到文件或标准错误别用System.out.println。3.4 工具定义两种模式共用Server 侧的工具代码不区分传输方式Tool注解标记的方法会被自动暴露Service public class ToolService { Tool(description 查询指定城市的实时天气情况) public String getWeather(ToolParam(description 城市名称例如北京) String city) { return switch (city) { case 北京 - 晴天25°C湿度 40%; case 上海 - 多云28°C湿度 65%; default - 未知城市; }; } Tool(description 根据订单号查询订单物流状态) public String getOrderStatus(ToolParam(description 订单编号) String orderId) { return switch (orderId) { case 12345 - 已发货预计明天到达; default - 订单号不存在; }; } }Client 侧的 Controller 同样不感知传输方式MCP Client 自动发现工具并注入 ChatClientRestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/chat) public String chat(RequestParam String msg) { return chatClient.prompt().user(msg).call().content(); } }4. 验证请求与成功结果配置写完必须验证连通性两种模式的验证动作不一样。4.1 SSE 模式验证先单独启动 Server确认端口监听mvn spring-boot:run看到日志里出现 MCP Server 在 8081 启动后用 curl 探测 SSE 端点。注意这个端点返回的是事件流不是普通 HTML所以会挂住不断输出curl -N http://localhost:8081/mcp正常会看到event:和data:开头的 SSE 消息。如果直接返回 404 或连接拒绝说明端点路径或端口不对。再启动 Client访问对话接口触发工具调用curl http://localhost:8080/chat?msg查询北京天气预期返回类似「北京今天晴天气温25°C湿度40%」。这条回复说明链路走通了Client 通过 SSE 从 Server 拿到工具列表模型决定调用getWeatherClient 通过 HTTP POST 把调用发给 Server结果回填给模型生成最终回复。4.2 STDIO 模式验证STDIO 模式没有独立端口可探测验证方式是直接启动 Client观察它是否成功拉起 Server 子进程。启动 Client 后看日志里有没有子进程启动记录和工具发现记录。然后同样访问curl http://localhost:8080/chat?msg订单12345到哪了预期返回「您的订单12345已发货预计明天到达」。如果返回的是模型自己编的答案而不是工具结果说明工具没被发现往下看排查部分。4.3 两种模式对照维度STDIOSSE通信方式标准输入输出SSE 长连接 HTTP POST是否占端口否是进程管理Client 拉起子进程Server 独立运行跨机器不支持支持多客户端共享每个 Client 一个进程天然支持日志风险污染消息流无适用场景本地工具、单机远程服务、分布式5. 本篇常见错排查连接失败Could not connect to MCP serverSSE 模式下先确认 Server 真的起来了curl -N http://localhost:8081/mcp能出事件流。端口被占用、路径写错、Server 启动失败都会报这个。STDIO 模式下检查command和args拼出来的命令能不能在终端手动跑通路径要用绝对路径。工具列表为空模型不调用工具三个方向查Server 侧ToolService有没有被 Spring 扫描到Tool注解是否生效Client 侧配置的 server 名字和 Server 的name是否对应模型是否支持 function calling换 deepseek-chat 这类支持工具调用的模型试。另外 Server 和 Client 的 Spring AI 版本要一致1.1.6 对 1.1.6版本错配会导致 MCP 协议握手失败。STDIO 模式启动后立刻退出最常见的原因是 Server 往标准输出打了日志。JSON-RPC 靠标准输出传消息一行日志就能让 Client 解析失败然后断开。把 Server 的日志级别调高或者重定向到文件确保标准输出只有协议消息。SSE 模式工具调用超时检查 Client 到 Server 的网络是否通跨机器时localhost要换成实际 IP。如果中间有网关确认 SSE 长连接没被缓冲或超时切断SSE 需要关闭响应缓冲。多个 Server 工具重名Client 连多个 MCP Server 时同名工具会让模型混淆。给每个 Server 起有意义的名字或者在工具description里标注来源排查时能快速定位是哪个 Server 提供的。6. 把工具链路接到统一入口传输方式选型有个简单判断工具和 AI 应用在同一台机器、追求低延迟用 STDIO工具要跨网络共享、要被多个应用复用、要独立扩缩容用 SSE。生产环境里两者可以并存本地开发用 STDIO 快速迭代部署时切 SSE。模型接入这一跳用 TaoToken 的统一 Key 能省掉多厂商配置的维护成本。Spring AI 的 OpenAI starter 把base-url指向https://taotoken.net/apiapi-key用环境变量注入工具调用请求就会带着 tools 字段发出去。想先验证模型对工具调用的支持情况可以在模型对话页面直接试要长期跑编码类 Agent 场景Coding Plan 更合适Key 的创建和管理在 API Keys 页面接入细节和参数说明看接入文档。链路跑通后下一步是把通用工具数据库查询、文件操作、内部 API 封装抽成独立 MCP Server让多个 AI 应用复用。先从 SSE 模式起步因为它更接近你熟悉的微服务部署方式调试时也能用 curl 直接探测端点。
返回列表