ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Spring AI 实现 MCP Server 和 Client:Java 侧 SSE 通道配置与联调验证

Spring AI 实现 MCP Server 和 Client:Java 侧 SSE 通道配置与联调验证 1. 为什么 Java 开发者需要关注 MCP 的 SSE 通道如果你正在用 Spring Boot 写业务系统又想让大模型直接调用你系统里的方法MCPModel Context Protocol就是目前最省事的方案。它做的事情说白了就一件把你的 Java 方法包装成标准工具让模型能像调函数一样调它。而 Spring AI 从 1.0 开始把 MCP Server 和 Client 的 starter 都准备好了Java 侧不用手写协议解析。这篇聚焦一个具体场景本地跑通一次端到端调用。Server 端用 Spring AI 暴露两个数学工具Client 端用 Spring AI 的 OpenAI SDK 接入模型通过 SSE 长连接把工具挂到 ChatClient 上最后在浏览器里发一句话看模型是否真的调用了你写的 Java 方法。适合谁看会 Spring Boot、想给现有系统加 AI 工具能力的后端正在评估 MCP 传输方式选 SSE 还是 stdio 的架构同学以及被sse-endpoint和sse-message-endpoint两个路径搞混的人。整条链路我会给出可复制的依赖、yml 和代码你跟着敲就能跑。SSE 在这里的角色是传输层。MCP 本身不绑定传输方式stdio 适合本地进程SSE 适合远程部署的 Server——Client 先发一个 GET 建立长连接Server 持续推事件后续工具调用走 POST。理解这一点后面配置里两个端点各管什么就清楚了。2. 前置准备TaoToken 统一 Key 与 API 通道Client 端要调模型就得有 Key 和 base-url。我试过把模型 Key 散落在各个项目的 yml 里换环境时特别容易漏。TaoToken 在这里的作用是提供一个统一的 Key/API 通道Client 的spring.ai.openai配置直接指向它就行不用为每个模型单独维护一套地址。官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 地址配置 base-url 用这个不带 UTMhttps://taotoken.net/api你需要提前准备的东西JDK 17 或以上Spring AI 1.0.x 要求Maven 3.8一个可用的模型 Key填到 Client 的spring.ai.openai.api-key两个端口Server 用 8080Client 用 8081别冲突。注意Server 和 Client 是两个独立进程别塞进同一个 Spring Boot 应用里否则 SSE 连接会自己连自己排查起来很绕。3. MCP Server暴露工具与 SSE 端点配置3.1 Maven 依赖Server 端核心依赖是spring-ai-starter-mcp-server-webmvc它自带 WebMVC 的 SSE 支持。版本用 Spring AI 的 BOM 统一管理避免各 starter 版本打架。dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server-webmvc/artifactId /dependency /dependencies3.2 用 McpTool 声明工具工具类放在com.example.mcpserver.tool下方法上加McpTool参数加McpToolParam。description 会直接进模型的工具描述写清楚点模型选工具靠它。package com.example.mcpserver.tool; import org.springframework.ai.mcp.annotation.McpTool; import org.springframework.ai.mcp.annotation.McpToolParam; import org.springframework.stereotype.Component; Component public class MathTools { McpTool(name add, description 计算两个数字的和返回 double) public double add( McpToolParam(description 第一个加数, required true) double a, McpToolParam(description 第二个加数, required true) double b) { return a b; } McpTool(name square, description 计算一个数字的平方) public double square( McpToolParam(description 待计算数字, required true) double a) { return a * a; } }3.3 application.yml 关键配置这里两个端点最容易搞混我拆开说sse-endpointClient 发 GET 建立长连接的地址Server 通过它推事件sse-message-endpointClient 发 POST 提交工具调用请求的地址。server: port: 8080 spring: application: name: mcp-server ai: mcp: server: name: mcp-server version: 1.0.0 enabled: true protocol: SSE sse-endpoint: /api/v1/sse sse-message-endpoint: /api/v1/mcp capabilities: tool: true resource: false prompt: falsecapabilities.tool: true是显式声明不写的话工具不会注册。resource 和 prompt 用不到就关掉减少暴露面。启动后访问http://localhost:8080/api/v1/sse浏览器会挂住不返回这是正常的——长连接建立成功就是这个表现。想确认工具注册了看启动日志里有没有Registered tools: [add, square]这类输出。4. MCP Client接入模型并挂载远程工具4.1 Client 依赖Client 需要 web、OpenAI starter 和 MCP Client starter 三个。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client-webflux/artifactId /dependency /dependencies4.2 ChatClient 配置把 MCP 工具注入进去关键点是ToolCallbackProviderSpring AI 会自动把 MCP Client 拉到的远程工具包装成它直接塞给 ChatClient 的defaultToolCallbacks。package com.example.mcpclient.config; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.model.tool.ToolCallbackProvider; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class ChatClientConfig { Bean public ChatMemory chatMemory() { return MessageWindowChatMemory.builder().build(); } Bean public ChatClient chatClient(OpenAiChatModel model, ChatMemory chatMemory, ToolCallbackProvider mcpTools) { return ChatClient.builder(model) .defaultSystem(你是一个数学助手需要计算时调用工具不要心算。) .defaultAdvisors( new SimpleLoggerAdvisor(), MessageChatMemoryAdvisor.builder(chatMemory).build()) .defaultToolCallbacks(mcpTools) .build(); } }4.3 application.yml指向 TaoToken 与远程 Serverserver: port: 8081 spring: application: name: mcp-client ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: gpt-4o-mini mcp: client: sse: connections: math-server: url: http://localhost:8080 sse-endpoint: /api/v1/sseconnections下可以挂多个 Serverkey 只是别名。url填 Server 的 hostsse-endpoint填 Server 里配的那个长连接路径别把 message-endpoint 填进来。4.4 Controller 入口package com.example.mcpclient.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/ai) public class AiController { private final ChatClient chatClient; public AiController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(value /chat, produces text/plain;charsetutf-8) public String chat(RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }5. 联调验证一次端到端调用先起 Server再起 Client。两个都起来后浏览器访问http://localhost:8081/ai/chat?message帮我算一下 12 加 30 等于多少预期结果模型不会直接回 42而是先触发工具调用日志里能看到add被调用参数是 12 和 30然后模型拿到返回值再组织语言回复。Client 控制台会打印 SimpleLoggerAdvisor 的请求响应Server 控制台会打印工具执行记录。再试一个http://localhost:8081/ai/chat?message9 的平方是多少这次应该命中square工具。如果两次都命中了正确的工具说明 SSE 通道、工具注册、模型调用三段链路全通了。想更直观地看 SSE 事件流可以用 curl 手动建连curl -N http://localhost:8080/api/v1/sse你会看到event: endpoint和data: /api/v1/mcp?sessionIdxxx这样的推送sessionId 就是后续 POST 要带的。这一步能帮你确认 Server 端 SSE 是活的。6. 本篇常见错误排查连接建立失败Client 启动报Connection refused九成是 Server 没起或者端口不对。先单独 curl 一下 Server 的 sse-endpoint确认能挂住再起 Client。工具没被调用模型直接心算检查 Server 的capabilities.tool是否为 true以及 Client 的defaultToolCallbacks(mcpTools)有没有漏。另外 system prompt 里明确写需要计算时调用工具模型偷懒心算的情况会少很多。两个端点填反了sse-endpoint是 GET 长连接sse-message-endpoint是 POST 消息入口。Client 配置里只填sse-endpoint填成 message-endpoint 会连不上。版本冲突Spring AI 各 starter 必须走同一个 BOM混用版本会出现NoSuchMethodError。检查dependencyManagement里 BOM 有没有生效。Client 用 webflux starter 但项目是 webmvcMCP Client 的 SSE 实现依赖 WebClient用 webflux starter 更稳。如果坚持 webmvc注意别把 webflux 的自动配置排除掉。模型返回 401Key 或 base-url 不对。base-url 用https://taotoken.net/api别多加路径后缀。7. 继续深入的方向跑通这条链路后你可以把 Server 换成真实业务工具比如查订单、算库存Client 侧挂多个 MCP Server 做工具聚合。需要管理多个 Key 或切换模型时TaoToken 的模型对话入口可以快速验证工具描述是否被模型正确理解https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content如果打算把 MCP 用到长期编码或 Agent 场景Coding Plan 提供了更集中的额度管理https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentKey 的创建和管理在控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content接入细节和参数说明看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content实际调试时我习惯先 curl 通 SSE 再起 Client这样能把传输层问题和模型层问题分开。工具描述尽量写具体模型选错工具大多是因为 description 太模糊。
返回列表