
1. Spring 4.3.7 老项目接入 Spring AI MCP 的真实困境如果你手上跑着一个 Spring 4.3.7 的老系统看到 Spring AI 和 MCPModel Context Protocol这套东西第一反应大概率是「跟我没关系」。因为 Spring AI 的官方 starter 明确要求 Spring Boot 3.x底层是 Spring Framework 6.x而 Spring 4.3.7 属于 Spring Framework 4.x 时代中间隔着 5.x 一整个大版本。直接升框架等于把整个业务系统的地基换掉风险不可控。但现实需求又摆在那里业务方希望老系统里的订单查询、库存检查、客户信息这些能力能被 AI 助手或 Agent 调用。你不可能把老系统推倒重来也不可能让 AI 直接连生产数据库。这时候 MCP 的价值就出来了——它本质上是一套标准化的工具调用协议让 AI 模型通过统一接口去调用外部能力而不是把业务逻辑塞进模型里。我试过的思路是不碰老系统的框架版本把 MCP 当成一个独立的「能力层」来建设。老系统继续用 Spring 4.3.7 跑它的业务新起一个 Spring Boot 3.x 的 MCP 服务端模块通过 HTTP 调用老系统暴露的 REST 接口把结果包装成 MCP 工具返回给 AI。这样改造范围被严格限制在新模块里老代码一行不动。这个方案适合谁适合那些系统稳定运行多年、业务逻辑复杂、但又有 AI 能力接入诉求的团队。核心判断标准是老系统有没有可用的 REST API。如果有接入成本很低如果没有需要先补一层 HTTP 接口但依然不需要动框架版本。下面我会把整个渐进式接入的路径拆开讲从 MCP 服务端最小配置到与老 Service 层解耦的调用示例再到用 curl 验证端点连通性最后是常见报错排查。每一步都有可复制的代码和配置你可以直接拿去改。2. TaoToken 前置准备MCP 服务端调用模型能力的接入配置MCP 服务端本身不产生智能它只是把工具暴露给模型。真正让 AI 理解用户意图、决定调用哪个工具的是背后的模型。所以你需要一个能稳定调用模型的入口。这里我用 TaoToken 来做模型接入层它提供 OpenAI 兼容的 API 接口Spring AI 可以直接配置。先拿 API Key。打开 https://taotoken.net/api-keys 登录后创建一个新的 Key复制保存。这个 Key 后面会写进 Spring AI 的配置文件里。然后确认你要用的模型 ID。TaoToken 支持多种模型MCP 场景下建议选工具调用能力强的比如 claude 系列或 gpt 系列。模型 ID 在模型对话页面可以看到也可以直接调接口查。Spring AI 的 MCP 客户端配置里模型和 MCP 服务端是分开配的。模型走 OpenAI 兼容协议MCP 服务端走 stdio 或 Streamable HTTP。两者在同一个 Spring Boot 3.x 模块里共存互不干扰。这里有个关键点老项目是 Spring 4.3.7新模块是 Spring Boot 3.x两个进程独立部署。新模块通过 HTTP 调老系统的 REST 接口老系统完全不知道 MCP 的存在。这种解耦方式让改造边界非常清晰——出问题只可能在新模块回滚就是停掉新模块。配置模型接入时Base URL 填https://taotoken.net/apiAPI Key 填你刚创建的那个Model ID 填你选定的模型。这三个要素在 Spring AI 的application.yml里对应spring.ai.openai.base-url、spring.ai.openai.api-key、spring.ai.openai.chat.options.model。如果你还没决定用哪个模型可以先到模型对话页面试一下工具调用的效果确认模型能正确理解工具描述再写进配置。这一步花几分钟能省掉后面很多调试时间。3. 可复制配置Spring Boot 3.x MCP 服务端最小可用片段新模块的pom.xml需要引入 Spring AI 的 MCP 服务端 starter 和 OpenAI 兼容的模型 starter。版本用 1.0.0-M6 或更高稳定版。注意这个模块的 Spring Boot 版本必须是 3.x和老的 4.3.7 项目完全隔离。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId version1.0.0-M6/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0-M6/version /dependencyapplication.yml里配置模型接入和 MCP 服务端。模型部分指向 TaoToken 的 API 地址MCP 部分声明服务端名称和版本。spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-3-5-sonnet-20241022 temperature: 0.7 mcp: server: name: legacy-business-mcp version: 1.0.0 protocol: STREAMABLE_HTTPAPI Key 不要硬编码在 yml 里用环境变量注入。启动命令里加-DTAOTOKEN_API_KEY你的key或者用.env文件配合启动脚本。MCP 服务端的工具定义用Tool注解。这里的关键是工具方法本身不包含业务逻辑它只负责调用老系统的 REST 接口把结果转成 MCP 能识别的格式。业务逻辑还在老系统里新模块只做适配。Component public class LegacyOrderTools { private final RestClient restClient; public LegacyOrderTools(RestClient.Builder builder) { this.restClient builder.baseUrl(http://legacy-system:8080).build(); } Tool(description 根据用户ID查询订单列表返回订单号、金额、状态) public String getOrders(ToolParam(description 用户ID) String userId) { return restClient.get() .uri(/api/orders?userId{userId}, userId) .retrieve() .body(String.class); } Tool(description 检查商品库存返回可用数量) public String checkInventory(ToolParam(description 商品ID) String productId) { return restClient.get() .uri(/api/inventory?productId{productId}, productId) .retrieve() .body(String.class); } }老系统的 REST 接口返回 JSONMCP 工具直接透传字符串。模型拿到 JSON 后自己解析。如果你想让模型更容易理解可以在工具方法里做一层轻量转换把 JSON 转成自然语言描述但这不是必须的。Streamable HTTP 协议比 stdio 更适合服务端部署因为它无状态不依赖长连接能直接挂在负载均衡后面。配置里protocol: STREAMABLE_HTTP就是启用这个模式。MCP 端点默认暴露在/mcp路径下你可以通过spring.ai.mcp.server.endpoint改。启动新模块后MCP 服务端会监听一个 HTTP 端口等待客户端连接。老系统那边什么都不用改只要 REST 接口能正常访问就行。4. 验证请求用 curl 确认 MCP 端点连通性与工具调用结果配置写完后先别急着接 AI 客户端。用 curl 直接打 MCP 端点确认服务端正常启动、工具注册成功。这一步能排除掉大部分配置问题。先确认 MCP 服务端健康状态。Streamable HTTP 模式下MCP 端点支持标准的 JSON-RPC 请求。发一个initialize请求curl -X POST http://localhost:8081/mcp \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 1, method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: {name: curl-test, version: 1.0} } }正常返回会包含serverInfo和capabilities说明 MCP 服务端已经就绪。如果返回 404检查spring.ai.mcp.server.endpoint配置和实际路径是否一致。如果返回 500看启动日志里有没有工具注册失败的报错。接着列出已注册的工具curl -X POST http://localhost:8081/mcp \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 2, method: tools/list, params: {} }返回的tools数组里应该能看到getOrders和checkInventory每个工具带name、description、inputSchema。如果工具没出现检查Tool注解的类有没有被 Spring 扫描到以及ToolParam的参数名是否保留编译时加-parameters。最后实际调用一次工具curl -X POST http://localhost:8081/mcp \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 3, method: tools/call, params: { name: getOrders, arguments: {userId: U10086} } }如果老系统的 REST 接口正常这里会返回订单 JSON。如果返回错误先单独用 curl 打老系统的/api/orders?userIdU10086确认老系统那边没问题再排查新模块的 RestClient 配置。这三个 curl 请求覆盖了 MCP 服务端的核心链路初始化、工具发现、工具调用。全部通过后再接 AI 客户端就有把握了。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 报错对照接入过程中最容易卡住的几个报错我按实际遇到的频率排一下。401 Unauthorized这个通常出在模型调用环节不是 MCP 本身。检查spring.ai.openai.api-key是否填对环境变量有没有生效。如果 Key 是从 TaoToken 复制的确认没有多余空格。另外注意 Base URL 末尾不要带/v1Spring AI 的 OpenAI starter 会自动拼路径写成https://taotoken.net/api就行。local proxy failed / connection refusedMCP 客户端连不上服务端。先确认服务端端口和路径curl 能通但客户端不通多半是客户端配置的 URL 写错了。Streamable HTTP 模式下客户端配置的是完整 URL比如http://localhost:8081/mcp不是只写 host。reading choices 报错模型返回格式不符合预期。常见原因是模型 ID 写错或者模型不支持工具调用。换一个支持 function calling 的模型比如 claude 系列。另外检查temperature不要设太高工具调用场景建议 0.3 以下。OAuth 相关报错如果你用的是需要 OAuth 的 MCP 服务端客户端配置里要带 token。但大多数自建 MCP 服务端不需要 OAuth如果报这个错检查是不是误配了spring.ai.mcp.client.oauth相关属性。不需要就删掉。还有一个隐蔽的坑老系统返回的 JSON 字段名和 MCP 工具描述不一致模型理解偏差导致调用参数错误。解决办法是在ToolParam的 description 里写清楚参数格式比如「用户ID字符串格式如 U10086」。排查顺序建议先 curl 打 MCP 端点再 curl 打老系统接口最后看模型调用日志。三层分开验证定位很快。6. 渐进式接入的边界判断与后续扩展回到最初的问题Spring 4.3.7 老项目到底该怎么接 MCP。核心结论是——不要试图在老的 Spring 4.3.7 进程里跑 Spring AI那条路走不通。正确的做法是新起一个 Spring Boot 3.x 模块把 MCP 服务端和模型接入都放在新模块里老系统只负责提供 REST 接口。这个方案的改造边界非常清晰老系统零改动新模块独立部署、独立回滚。你甚至可以先在新模块里只接一个工具跑通全链路后再逐步增加。风险可控收益明确。后续扩展方向有几个。一是把更多老系统的 Service 方法包装成 MCP 工具但要注意工具粒度——太细会导致模型调用次数多太粗又不够灵活。建议按业务场景聚合比如「查询用户订单」一个工具而不是「查订单号」「查订单金额」分开。二是引入 Streamable HTTP 的会话恢复能力应对网络抖动。三是用 Micrometer 监控 MCP 调用延迟和错误率把可观测性补上。如果你还在犹豫要不要动可以先做一个最小验证新模块只暴露一个查询工具用 curl 跑通再接一个 AI 客户端试一次对话。整个过程半天以内能完成。跑通之后你对改造范围的判断会清晰很多。模型接入这块TaoToken 的 API 兼容性做得比较干净Spring AI 配置里改个 base-url 就能用。需要的话可以去 https://taotoken.net/api-keys 拿个 Key 先试。MCP 服务端的配置和工具定义代码在上面都能直接复制改改 URL 和参数就能跑起来。