
如果你是个 Java 开发者最近一定在各种技术社区里高频看到 Spring AI MCP Server 这个词。我自己的感觉是从 2024 年底模型上下文协议MCP被提出到 Spring AI 在 2025 年的几个版本里把 MCP 客户端和服务端支持做得越来越成熟Java 生态接入 AI 的方式已经彻底变了。以前我们要自己写 HTTP 接口、拼 Prompt、解析 JSON现在把业务能力封装成 MCP Tool模型就能直接调用语义和链路都干净很多。这篇文章我不会只讲概念我会把从环境准备、代码编写、客户端接入到 Docker 一键部署的完整过程都过一遍。整个过程基于我最近在真实项目里从零落地的经验代码和脚本都是可以直接抄的。不管你是刚接触 Spring AI 的新手还是已经在做企业内部 AI 应用的老手只要按着步骤走基本都能把 Spring AI MCP Server 跑起来并理解它背后真正解决的是什么问题。1. 先搞懂 MCP 在解决什么问题1.1 AI 应用集成的“USB-C”接口MCP 全称是 Model Context Protocol也就是模型上下文协议。它的出现背景很直白AI 模型本身不会主动访问你的数据库、文件系统或者内部 API想让模型完成具体业务操作就必须给它接上工具和数据。2025 年以前每家 AI 框架都有一套自己的接入方式OpenAI 有 Function CallingLangChain 有自定义 ToolSpring AI 早期也有自己的 Tool 注解。结果就是一套业务能力想服务多个模型框架得写多套适配代码维护成本非常高。MCP 做的事情就是把这些接口统一起来。它规定了一个标准协议让模型应用比如一个基于 Spring AI 的 Agent和外部能力提供方比如一个提供天气查询、订单查询、数据库操作的服务通过固定的方式通信。这个协议设计得像 USB-C 接口只要都支持这个标准插上就能用不用管另一方内部怎么实现。Spring AI 从 1.0 开始就同时实现了 MCP 的客户端和服务端这也是 Spring AI MCP Server 这个词越来越火的核心原因。1.2 MCP 架构里的三个角色我在第一次接触 MCP 时最容易搞混的是角色之间的关系。实际上一个完整的 MCP 链路里有三个角色Host宿主进程运行着 AI 模型和 Prompt 编排逻辑的应用通常就是我们说的 AI Agent 或者聊天机器人的后端服务。MCP Client客户端代理在 Host 内部运行负责按照协议去连接外部 MCP Server把工具列表拉回来转发调用请求。MCP Server服务端独立部署的进程封装一个个业务能力对外暴露统一的 MCP 端点比如 HTTP SSE 或者 stdio 方式。用一个生活化的类比Host 就像一家餐厅的厨房MCP Client 是传菜员MCP Server 则是各个供应商的仓库。菜单就是工具列表厨房需要什么食材就跟仓库说一声仓库按标准打包送过来而不是每家供应商都用自己的送货车。1.3 MCP 体系中的三大原语MCP 协议里有三个核心原语理解了它们就理解了整个协议的设计Tools工具模型在推理时能够主动调用的函数比如“查询订单状态”“获取股票价格”。工具是 MCP 里最常用、最关键的原语也是我们在 Spring AI 里主要封装的内容。Resources资源对外提供的数据或文本内容比如一份文档、一张表结构说明。资源可以被模型作为上下文读取但它不像工具那样带输入输出参数。Prompts提示词模板服务端预置的提示词模板方便 Host 直接复用比如“生成一份 MySQL 慢查询分析报告”这样一整套 Prompt 和参数组合。在实际的 Spring AI MCP Server 开发里我们 90% 的精力都在写 Tools剩下的场景才会用到 Resources 和 Prompts。所以这篇文章后面的实现部分会重点展示如何把一个方法变成模型能调用的 Tool。1.4 为什么 Java 开发者要在这个时间点切入很多人会问既然 LangChain 或者 Python 生态做 AI 更成熟为什么 Java 还要做这件事我个人的体会是企业内部的核心业务系统绝大多数都是 Java 写的Spring Boot 几乎是行业标配。AI 应用要发挥作用一定要连上已有业务能力比如查询工单、变更库存、发送通知这些服务本来就在 Java 体系里。过去我们把业务能力暴露给 AI 有两种做法一种是为模型侧单独写接口很啰嗦另一种是干脆让 AI 直接调数据库安全风险很大。MCP 让这些业务系统自己封装成标准的 ToolAI 服务通过网络来调用权限、参数校验、日志审计都在原有服务里完成。这是一个非常符合 Java 后端工程习惯的模式。而且 Spring AI 从 1.0 GA 到 2.x 版本接口稳定了很多踩坑成本已经大幅降低。2. 环境准备与版本选型2.1 在动手前先检查这些环境开始写代码前先确认本机环境。我这次使用的组合是 JDK 17 Maven 3.9 Spring Boot 3.4.5 Spring AI 1.0.0 GA。这个组合目前看是比较稳的。JDK 版本建议至少 17因为 Spring Boot 3.x 是强制要求 17 以上的如果你还在用 JDK 8建议先用 SDKMAN 或者直接装一个 17 的独立目录平时开发和构建时切换使用。用下面的命令快速检查环境java -version mvn -version docker --version如果 Maven 没有安装或者版本低于 3.6建议先升级。Maven 版本太老的话拉取 Spring AI 的部分依赖会出现奇怪的解析错误这是我在实践中踩过的第一个坑。Docker 方面如果你只跑到本机联调可以不装但如果要看第四部分的一键部署就必须准备一台能跑 Docker 的 Linux 服务器或者本地 Docker Desktop。2.2 Spring AI 版本怎么选1.0.x 还是 2.x这是最近社区里问得很多的问题尤其是 Spring AI Alibaba 一度传出各种说法。先回答一句比较直接的Spring AI Alibaba 并没有停更仍在正常迭代国内开发者用阿里云百炼平台的 qwen 系列模型最方便接入方式也在持续完善。至于版本选择我的建议是参考下面的原则如果你希望稳定优先用 Spring AI 1.0.0 GA 或 1.0.x 的最新补丁版。这个版本对应 Spring Boot 3.4.xAPI 已经冻结可以安全用于生产。mcp-server 和 mcp-client 两个模块也都是独立打包的不会引入太多历史包袱。如果你想尝试 Agent 编排和更前沿的能力可以用 Spring AI 2.x但要注意 2.x 的部分 API 和 1.0 不兼容升级成本是存在的。而且 2.x 对 Spring Boot 的版本要求更高一般要配合 Spring Boot 3.5 或以上的基线。我这次落地选择了 Spring AI 1.0.0 GA原因很现实团队里还有其他微服务依赖 Spring Boot 3.4.x统一基线容易维护。工具调用Tool Calling和 MCP 是 1.0 里的核心能力已经足够稳定。这里给出一张我参考过的版本对应表Spring AI 版本适合的 Spring Boot 版本成熟度建议使用场景0.8.x3.2.x实验仅学习不建议新项目1.0.0 GA3.4.x稳定生产可用推荐1.0.x 后续补丁3.4.x稳定生产推荐跟进修复2.0.x3.5.x较新探索新功能谨慎生产2.3 创建项目骨架我习惯直接用 Spring Initializr 生成基础工程也可以手动建 Maven 项目。关键是 pom.xml 里的依赖组合要看准。我的 pom 里核心依赖是这样配的parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.4.5/version relativePath/ /parent properties java.version17/java.version spring-ai.version1.0.0/spring-ai.version /properties dependencies !-- Web 基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI 模型接入以阿里云百炼为例 -- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId version1.0.0.2/version /dependency !-- MCP Server 端通过 WebMvc SSE 暴露 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-webmvc/artifactId /dependency !-- MCP Client 端如果本服务也需要作为客户端去调其他 MCP Server -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency /dependencies有一点需要特别解释spring-ai-alibaba-starter 里已经带了 Spring AI 的 model 相关依赖并且通过阿里云百炼平台接入 qwen 系列模型时配置项是spring.ai.dashscope.api-key不是老的spring.ai.openai.api-key。如果你用的是通义或百炼直接用这个 starter 是最省事的。如果不想用阿里云可以用spring-ai-starter-model-openai或spring-ai-starter-model-anthropic结构上是类似的。依赖引入之后还需要在 Spring Boot 启动类上加上重要注解吗不需要Spring AI 的 MCP 支持是约定大于配置加了 starter 之后自动配置就会生效我们只需要在 properties 里告诉它“暴露什么端点”以及“连接哪些服务”。3. 核心代码实现写一个能被 AI 调用的 Tool3.1 第一步定义一个业务工具类先明确我们要做什么。为了演示完整链路我实现一个极简但常见的工具查询当前服务器的本地日期并顺便返回星期几。这个工具本身很简单但是它能很好展示 Spring AI 如何把一个 Java 方法变成一个模型可理解、可调用的函数。我在项目里新建了一个包com.example.mcpserver.tools内部类如下package com.example.mcpserver.tools; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; Component public class DateTool { Tool(description 获取服务器当前日期和星期几) public CurrentDate getCurrentDate( ToolParam(description 日期格式例如 yyyy-MM-dd 或 MMMM d, yyyy) String pattern) { LocalDate today LocalDate.now(); String formatted today.format(DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH)); String dayOfWeek today.getDayOfWeek().getDisplayName( java.time.format.TextStyle.FULL, Locale.ENGLISH); return new CurrentDate(today.toString(), dayOfWeek, formatted); } public record CurrentDate( String isoDate, String dayOfWeek, String formattedDate) { } }这里有两个关键点第一方法上标注了Tool注解注解里的 description 会作为工具描述传给模型模型正是靠这个描述决定什么时候调用工具所以描述要写清楚“这个工具能干嘛”。第二参数和返回值都尽量使用明确的 Java 类型我用的是嵌套 record 类型Spring AI 会基于它自动生成 JSON Schema让模型知道该传什么参数、会收到什么结果。为什么不要用 Map 当返回值这是我踩过坑的。用 Map 的话工具 Schema 会退化成“一个对象”这种模糊结构模型经常猜错字段名尤其在 qwen 这类模型上表现不稳定。换成 record 之后字段名、类型都固定了调用成功率会明显上升。3.2 第二步把 Tool 注册到 MCP Server有了工具类还不够还要把它注册到 MCP Server 里。Spring AI 支持两种注册方式一种是直接创建一个ToolCallbackProvider的 Bean另一种是使用MethodToolCallbackProvider来批量注册某个类里所有Tool方法。我比较推荐用ToolCallbackProvider的方式注册代码长这样package com.example.mcpserver.config; import com.example.mcpserver.tools.DateTool; import java.util.List; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class McpServerConfig { Bean public ToolCallbackProvider dateToolCallbackProvider(DateTool dateTool) { // 把 DateTool 实例中的所有 Tool 方法都暴露为 MCP Tool return MethodToolCallbackProvider.builder() .toolObjects(dateTool) .build(); } }如果以后有多个工具把它们都加到toolObjects(...)里就行了。这里有一个容易被忽略的细节ToolCallbackProvider是 Spring AI 中所有 Tool 的统一抽象它同时服务了两种场景。第一种是你直接在一个 ChatClient 里使用这些工具第二种是你把工具通过 MCP Server 暴露给远程的 AI 应用调用。我们在服务端用 MCP Server 暴露时框架会自动把该 Provider 中的工具转换成 MCP 的工具列表。3.3 第三步启动服务端暴露 SSE 端点注册完工具之后下一步就是启动 MCP Server 端。Spring AI MCP 默认提供了两套服务端实现spring-ai-mcp-server-webmvc和spring-ai-mcp-server-webflux。因为大部分 Java 后端项目用的都是 Spring MVC所以我选的是前者。它基于 Servlet 容器通过 SSEServer-Sent Events提供 MCP 端点。启动类不需要改但需要在application.yml里加配置spring: ai: mcp: server: name: order-ai-mcp-server version: 1.0.0 sse-endpoint: /mcp enabled: true这个配置的含义是把 MCP 服务端点暴露在应用的/mcp路径上MCP 客户端通过 SSE 连上来时会先获取能力列表再逐个调用。直接启动这个服务默认端口是 8080如果只跑服务端可以顺手给 Spring Boot 配置一个server.servlet.context-path或者用 8081 避免冲突。启动后可以用浏览器或者 curl 检查端点是否存活。注意 SSE 端点不是普通 GET 接口直接请求/mcp会一直挂着这是正常的因为它期望客户端按 MCP 协议的消息格式发送请求。用下面的命令简单确认服务启动成功即可curl http://localhost:8080/order-ai-mcp-server正常情况下会返回 404 或者其他非 5xx 状态只要不是连接拒绝说明端口起来了。3.4 第四步客户端接入让模型真正能调用工具服务端准备好了但真正要让 AI 模型调用这个工具还需要一个 Host 端。这一步很多教程会跳过导致很多人写完 Server 不知道怎么测。我这里提供两种验证方式任选其一。方式一在同一个 Spring Boot 工程里同时配置一个 ChatClient用一个测试接口验证工具调用。这种方式最快速适合本地验证。关键配置如下spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus mcp: client: enabled: true webmvc: connections: - name: local-server url: http://localhost:8080/mcp然后在代码里注入ChatClient写一个测试接口package com.example.mcpserver.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder.build(); } GetMapping(/ai/date) public String askDate(RequestParam(defaultValue 今天星期几) String message) { return chatClient.prompt(message).call().content(); } }调用流程是我的 HTTP 请求发给 ChatControllerChatClient把消息发给百炼平台的 qwen-plusqwen 模型发现需要查找日期就会通过 MCP Client 连接到本服务的/mcp端点调用DateTool.getCurrentDate拿到结果后再组合成自然语言返回给用户。整个链路在一次 HTTP 响应内完成。方式二使用独立的 Host 服务比如另一个 Spring Boot 工程只做 MCP Client连接独立的 MCP Server。这个更接近生产环境但本地调试时链路较长我建议先把方式一跑通。3.5 完整链路验证一次真实的调用过程我实际跑通的请求是这样的启动服务后控制台打成http://localhost:8080/ai/date?message今天星期几返回结果类似今天是 2025-06-04星期三。为了确认模型真的通过 MCP 调用了工具而不是自己瞎编的日期我开启了客户端日志logging: level: org.springframework.ai.mcp.client: DEBUG org.springframework.ai.tool: DEBUG在日志里能看到类似下面这样的记录Sending tool call: nameDateTool.getCurrentDate, arguments{pattern:yyyy-MM-dd} Tool execution result: CurrentDate[isoDate2025-06-04, dayOfWeekWednesday, formattedDate2025-06-04]看到这行日志就证明整个 Spring AI MCP Server 的链路完全通了。有时候模型会要求同时调用多个工具日志里会有多次 tool call 记录这也是正常现象。4. 一键部署从本机到生产环境4.1 为什么要用 Docker 部署 MCP Server我们在真实项目中很快发现本机跑通只是第一步真正麻烦的是让 MCP Server 稳定运行在服务器上。如果直接把 Java 进程扔到服务器上环境差异会让问题变得不可控比如服务器 JDK 版本不对、Maven 没装、端口被占用、日志没轮转。Docker 化之后构建物变成一个标准镜像在任何有 Docker 环境的机器上都能跑出一样的行为。这也是“一键部署”落地的核心保障。4.2 编写一个干净的多阶段 Dockerfile我采用的 Dockerfile 是多阶段构建模式第一个阶段用 Maven 镜像编译打包第二个阶段只保留 JRE 运行环境。这样最终镜像体积能控制在 300MB 左右而不是带着整套 Maven 依赖跑。# 第一阶段构建 FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests -B # 第二阶段运行 FROM eclipse-temurin:17-jre WORKDIR /app COPY --frombuilder /app/target/mcp-server-*.jar app.jar EXPOSE 8080 ENV JAVA_OPTS-Xms256m -Xmx512m ENTRYPOINT [sh, -c, java $JAVA_OPTS -jar app.jar]这里有一个细节mvn dependency:go-offline这一步会把所有依赖提前拉取并缓存到镜像层之后每次修改源码重新构建时只有真正变化的依赖才会重新下载构建速度会快很多。如果你把整个源码 COPY 进去再运行 Maven 命令没有利用好依赖缓存每次构建都会非常慢。4.3 docker-compose 编排与环境变量注入实际部署时我不会直接docker run而是写一个docker-compose.yml把环境变量、端口映射、健康检查都管理起来。密钥不要写死在镜像里用环境变量的方式注入version: 3.8 services: mcp-server: image: registry.example.com/ai/mcp-server:1.0.0 container_name: mcp-server ports: - 8080:8080 environment: - DASHSCOPE_API_KEY${DASHSCOPE_API_KEY} - TZAsia/Shanghai healthcheck: test: [CMD, curl, -f, http://localhost:8080/actuator/health] interval: 30s timeout: 5s retries: 3如果项目里有 Spring Boot Actuator建议在 pom 里加上依赖并暴露 health 端点management: endpoints: web: exposure: include: health这样 Docker 的 healthcheck 才能正常探测服务是否就绪。4.4 编写 deploy.sh 一键部署脚本接下来是“一键部署”的核心了。我通常会在服务器上放一个deploy.sh内容包含构建镜像、停止旧容器、启动新容器、健康检查四步。脚本设计成一个幂等操作重复执行不会出问题#!/bin/bash set -e IMAGE_NAMEregistry.example.com/ai/mcp-server TAG1.0.0 CONTAINER_NAMEmcp-server echo 1. 构建 Docker 镜像 docker build -t ${IMAGE_NAME}:${TAG} . echo 2. 停止并删除旧容器如果存在 if [ $(docker ps -aq -f name${CONTAINER_NAME}) ]; then docker stop ${CONTAINER_NAME} docker rm ${CONTAINER_NAME} fi echo 3. 启动新容器 docker run -d \ --name ${CONTAINER_NAME} \ -p 8080:8080 \ --restart unless-stopped \ -e DASHSCOPE_API_KEY${DASHSCOPE_API_KEY} \ -e TZAsia/Shanghai \ ${IMAGE_NAME}:${TAG} echo 4. 等待健康检查通过 for i in {1..30}; do STATUS$(curl -s -o /dev/null -w %{http_code} http://localhost:8080/actuator/health || true) if [ $STATUS 200 ]; then echo 部署成功健康检查通过 exit 0 fi sleep 2 done echo 健康检查超时服务可能启动失败 docker logs ${CONTAINER_NAME} --tail 50 exit 1这个脚本第 4 步很重要。如果不做健康检查就退出后面接 CI/CD 时很可能把还没启动完成的实例当作成功。我在实践中把探测超时设置在 30 次乘 2 秒大约是 60 秒。Java 应用首次启动时需要加载 Spring 上下文60 秒通常够用但如果你的服务里有很多 Bean 或者要连数据库建议把循环次数加大到 60也就是约 120 秒。4.5 第一次部署的现场记录我在一台 2 核 4G 的云服务器上试跑这套流程时整个过程大约花费 3 分钟。前面 Maven 构建阶段占了大部分时间因为需要拉取依赖后续每次改代码重新部署如果依赖没有大变化构建时间可以缩短到 1 分钟内。部署完成后我在服务器上执行了一次远端调用测试curl -H Content-Type: application/json \ -d {message:现在几号} \ http://服务器IP:8080/ai/date返回结果正常。随后我用同一台服务器上的另一个 Spring AI Host 服务去连接部署好的 MCP Server测试外部连接也能正常拿到工具列表。这就验证了 MCP Server 不只是“本地能跑”而是真正可以作为基础设施暴露给任意模型应用使用。4.6 关于可观测性的几个建议一旦 MCP Server 进入生产环境光有一个健康检查是不够的。我建议在部署时至少加上下面几项日志保留策略使用 JSON 格式日志方便采集到 Elasticsearch 或 Loki。关键指标记录每次工具调用的耗时、成功率以及模型调用工具的次数。Spring AI 默认没有完整埋点我是在工具方法里手动加了一个简单的计数和耗时打印。超时配置模型等待工具返回时间可能很长建议在 MCP 服务端配置合理超时时间。Spring AI 相关模块的配置项是spring.ai.mcp.server.timeout单位为秒默认值对高延迟场景可能不够。对于大多数中小团队做到这三点已经足够。不必在一开始就上重型的链路追踪系统等调用量大了再逐步加。5. 实战中的坑踩过才懂5.1 MCP 协议版本对齐问题这是我最开始被卡住最久的地方。Spring AI 1.0.0 GA 使用的 MCP 协议版本和客户端依赖的协议版本如果不匹配客户端会报Unsupported protocol version或者握手失败。原因是 Spring AI 的 mcp-server 和 mcp-client 是不同 starter各自可能引入不同版本的 MCP SDK。解决办法有两个第一所有用到的 Spring AI 相关依赖版本都放到同一套 BOM 管理下尤其注意spring-ai-mcp-server-webmvc和spring-ai-starter-mcp-client要使用同一版本第二显式设置协议版本比如在服务端和客户端都配置mcp.server.version为同一个支持的协议版本。这里要说一句用 Spring AI 官方 BOM 是最省心的方式我在 pom 里的 spring-ai.version 统一设为 1.0.0 后没有再出现过这类问题。5.2 工具被调用但模型输出的结果不准确如果模型明明调用了工具返回里也带了isoDate2025-06-04但最终给你的自然语言回答还是“根据今天的日期应该是 2025 年 6 月 5 日”这种不一致多半是模型本身的指令遵循能力较弱或者系统提示词引导不够。我遇到这种情况时会在系统提示词里明确加上一句“当你有工具结果时必须以工具返回的数据为准不得自行推算日期。”这句简单的话就能明显减少错误输出。另外选择的模型最好要支持工具调用。比如 qwen-plus、qwen-max 系列都支持部分轻量模型对工具调用的支持比较弱建议在接入前先查看模型文档确认是否支持 function calling 或 tool calling。5.3 连接阿里云百炼时的 Key 与网络配置接入百炼平台时最容易出的问题是配置项搞错。我见过有人把spring.ai.dashscope.api-key写成spring.ai.openai.api-key结果请求报 401。还有网络问题如果你的服务器在中国大陆通常不需要额外处理网络但如果你的服务器在海外就需要考虑目标平台 API 的网络连通性。这里的处理方式属于常规运维范畴按各家云厂商的网络配置说明来做即可不展开讨论。一个更隐蔽的坑是 API Key 里带特殊字符比如sk-xxx:yyy这种带有冒号的字符在 yml 里如果不加引号会被解析成不正确的字符串。正确写法spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY}用环境变量引用后就不存在特殊字符解析问题。5.4 同一个工程里跑 Server 和 Client 时端口冲突如果你还是和我一样先用本地双角色既是 Server 又是 Client来验证记得确认不要自连。为了调试可以让 MCP Server 跑在 8080 端口而 MCP Client 连接http://localhost:8080/mcp请求入口ChatController也暴露在 8080。这样做有一个潜在问题Spring MVC 在接收/ai/date请求的时候如果 MCP Client 的 SSE 连接刚好占用线程可能在高并发下出现线程池不足。本机验证没问题生产环境一定要把 Server 和 Client 拆成独立服务。5.5 常见问题速查表我把最近被问得最多的几个问题整理成一个表方便你排查症状可能原因解决办法连接 MCP 失败报握手错误协议版本不匹配统一 Spring AI BOM 版本显式设置协议版本调用模型返回 401API Key 配置错误或为空检查spring.ai.dashscope.api-key配置工具列表为空ToolCallbackProvider 未注册成功检查 Bean 是否注入扫描包路径是否覆盖模型回答的日期是编的模型不支持工具调用或提示词未约束换 qwen-plus 及以上模型加系统提示词部署后健康检查失败内存不足或端口被占用调大 Java 堆内存检查宿主机端口首次构建很慢未利用依赖缓存在 Dockerfile 中先拷贝 pom.xml 并执行 go-offline表格里的这几项基本覆盖了从开发到部署最常见的故障。每次遇到异常先看服务端日志再看客户端日志大多数问题能在 Spring AI 自身的 DEBUG 日志里找到线索。5.6 关于 Spring AI Alibaba 与生态现状的一点看法最后关于 Spring AI Alibaba 相关的版本问题我再多说几句。近期总能看到“Spring AI Alibaba 停更了吗”类似的疑问。从我实际使用的体验看阿里云百炼的 starter 在 1.0.0 之后仍然有版本更新而且 Spring AI 官方本身也在把阿里云 DashScope 作为集成示例维护。技术选型时不要被零散的消息干扰判断依据只有一个你当前用的版本是否能满足需求、社区是否还在活跃维护。从这一点看Spring AI Alibaba 目前依然是 Java 生态里接入国产模型最顺滑的方案之一。我在实际项目中遇到的版本情况是如果跟随 Spring AI 官方 BOM 版本再引入对应版本的 spring-ai-alibaba-starter两者配合是比较可靠的。不要把各个依赖的版本各自为政地升级升级时最好一起升级并跑一遍完整的工具调用链路避免模型平台兼容性问题。写到这里整个 Spring AI MCP Server 的落地过程就算完整理清楚了。从协议本身的历史背景到环境选型、代码实现、Docker 部署再到我踩过的那些坑基本覆盖了一个 Java 开发者从零到生产会经历的全部环节。我个人最大的感受是MCP 让 AI 应用和业务系统之间的集成方式第一次有了标准答案而 Spring AI 又让这套标准在 Java 生态里落地得足够自然。后面如果再往深了做可以考虑接入更复杂的工具集、引入多 MCP Server 组合甚至把已有的 Agent 编排和 MCP Tool 调度串在一起但无论怎么扩展今天这套服务端封装和部署的思路都是一样的。作为 Java 开发者在一大片 Python 主导的 AI 内容里看到 Spring 生态能给出这么干脆的解决方案确实值得动手试一次。