1. 电商订单处理为什么需要 Spring AI 与 MCP Server 协同订单处理这条链路做过电商后端的同学都清楚它从来不是「创建订单 → 扣库存 → 发货」这么线性。真实场景里一笔订单要经过风控校验、优惠券核销、库存预占、支付确认、履约拆单、物流回传等七八个环节每个环节背后可能是一个独立服务。传统写法是把这些调用硬编码在业务代码里OrderService里塞满if-else和try-catch一旦某个下游接口改了参数或者加了新规则就得改代码、重新发版。我接触过的一个中型电商项目日均订单十万笔左右大促峰值能冲到五十万。原来的订单服务是同步串行调用创建订单接口平均响应 8 到 12 秒超时和库存不一致的问题每天都有几十单。后来他们把订单校验、库存查询、状态流转这几块拆出来用 Spring AI 做智能决策层用 MCP Server 做工具能力的统一暴露层整个链路才顺过来。这里要先说清楚两个概念不然后面配置看不懂。Spring AI 是 Spring 生态里用来对接大模型的框架它提供了一套统一的ChatClient、ToolCallback、Advisor抽象让你在 Spring Boot 项目里用几行代码就能把大模型接进来并且把本地方法注册成模型可以调用的「工具」。它的核心价值不是让你调个 API 聊天而是让模型能够根据用户意图自主决定调用哪个业务方法、传什么参数。MCP Server 则是 Model Context Protocol 的服务端实现。MCP 是一套让模型和外部工具、数据源通信的协议标准你可以把它理解成「模型世界的 USB 接口」。MCP Server 把订单查询、库存扣减、状态更新这些能力包装成标准化的 tool任何支持 MCP 的客户端包括 Spring AI 应用、Claude Code、Cline 等都能直接调用不用为每个客户端单独写适配。两者结合后的分工是这样的Spring AI 负责「理解意图 编排决策」MCP Server 负责「暴露能力 执行动作」。用户说一句「帮我查一下订单 20240512001 现在到哪了如果还没发货就取消掉」Spring AI 解析出意图决定先调queryOrder再根据返回状态决定是否调cancelOrder而这两个方法实际都注册在 MCP Server 上。这套架构适合谁我认为三类团队收益最明显一是订单链路已经拆成微服务、但调用关系混乱的团队二是想引入 AI 能力做智能客服、智能订单助手的团队三是希望把内部业务能力标准化输出、让多个 AI 客户端复用的团队。如果你还在单体应用阶段订单逻辑就几百行那暂时不用上这套杀鸡用牛刀。下面我按「前置准备 → 配置 → 代码 → 联调验证 → 排错」的顺序把整个流程拆开讲每一步都给可复制的内容。2. TaoToken 前置准备拿到 Base URL、API Key 与模型 ID在写 Spring AI 代码之前得先有一个能调用大模型的服务端点。Spring AI 本身不提供模型它只是客户端框架你需要一个兼容 OpenAI 协议的服务地址。这里我用 TaoToken 来做演示因为它同时提供模型对话、Coding Plan 和 API Key 管理配置起来比较直接。第一步打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册并登录。登录后在控制台左侧找到「API Keys」入口对应地址是 https://taotoken.net/console/api-keys 。点「创建密钥」给它起个名字比如spring-ai-order-demo创建完立刻复制保存因为页面刷新后就看不到完整 Key 了。第二步确认你要用的模型 ID。在「模型对话」页面 https://taotoken.net/model-chat 里可以看到当前可用的模型列表。做订单意图解析和工具调用建议选支持 function calling 的模型比如claude-sonnet-4-5或者gpt-4o这类。把模型 ID 记下来后面配置里要用。第三步确认 Base URL。Spring AI 的 OpenAI 兼容配置里base-url填https://taotoken.net/api注意这里不要加任何 UTM 参数就是干净的 API 地址。完整的 API 文档在 https://taotoken.net/doc 里面有各语言的调用示例遇到参数不确定的时候可以对照。如果你打算长期跑编码类 Agent 任务比如让模型自动改代码、跑测试那可以看一下 Coding Plan 页面 https://taotoken.net/coding-plan 它针对高频调用场景做了额度优化比按量付费更划算。不过订单处理这种业务场景按量调用就够了不用一上来就买套餐。拿到这三样东西后建议先在本地用 curl 验证一下 Key 是否可用避免后面 Spring AI 报错时排查方向跑偏curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: claude-sonnet-4-5, messages: [{role: user, content: 回复 OK}] }如果返回里有choices字段和正常内容说明 Key 和网络都没问题。如果返回 401那就是 Key 错了或者没带上Bearer前缀如果返回连接超时检查一下本地网络能不能访问taotoken.net。这一步做完前置条件就齐了Base URL、API Key、Model ID 三件套。接下来进入 Spring AI 项目配置。3. 可复制配置Spring AI 接入 MCP Server 的完整片段这一节是全文最核心的部分我把pom.xml依赖、application.yml配置、MCP Server 的mcp.json定义、以及 Spring AI 的工具注册代码全部给出来你直接复制改改就能跑。先看 Maven 依赖。Spring AI 的版本迭代比较快我这里用 1.0.0 稳定版MCP 相关的能力在spring-ai-starter-mcp-client里dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId version1.0.0/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId version1.0.0/version /dependency /dependencies然后是application.yml这里把 TaoToken 的 Base URL、Key、模型 ID 都配上同时配置 MCP Server 的连接方式。注意base-url结尾不要带/v1Spring AI 会自动补spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-5 temperature: 0.2 mcp: client: enabled: true name: order-mcp-client version: 1.0.0 type: SYNC request-timeout: 30s stdio: connections: order-server: command: java args: - -jar - ./mcp-order-server/target/order-mcp-server.jar这里api-key用环境变量注入不要硬编码在 yml 里生产环境尤其注意。temperature设 0.2 是因为订单场景要的是稳定输出不需要模型发挥创意。接下来是 MCP Server 端的工具定义。我用一个mcp.json来描述暴露给模型的能力这个文件放在 MCP Server 项目根目录{ mcpServers: { order-server: { command: java, args: [-jar, order-mcp-server.jar], env: { DB_URL: jdbc:mysql://localhost:3306/order_db, DB_USER: order_user }, tools: [ { name: queryOrder, description: 根据订单号查询订单详情返回状态、金额、商品列表, inputSchema: { type: object, properties: { orderId: { type: string, description: 订单号 } }, required: [orderId] } }, { name: checkStock, description: 查询指定 SKU 的可用库存数量, inputSchema: { type: object, properties: { skuId: { type: string }, quantity: { type: integer } }, required: [skuId] } }, { name: updateOrderStatus, description: 更新订单状态支持 CREATED/PAID/SHIPPED/CANCELLED, inputSchema: { type: object, properties: { orderId: { type: string }, status: { type: string } }, required: [orderId, status] } } ] } } }这个mcp.json就是 MCP 协议里的服务描述文件客户端读到它就知道有哪些工具、参数是什么。注意tools数组里的inputSchema用的是 JSON Schema 标准模型会根据这个 schema 生成调用参数。最后是 Spring AI 侧的工具注册代码。如果你用的是 MCP 客户端模式Spring AI 会自动把 MCP Server 暴露的工具注册成ToolCallback你只需要在ChatClient里开启工具调用Configuration public class OrderAiConfig { Bean public ChatClient orderChatClient(ChatClient.Builder builder, ToolCallbackProvider mcpToolProvider) { return builder .defaultSystem(你是电商订单助手负责解析用户意图并调用工具完成订单查询、库存校验和状态流转。调用工具前先确认参数完整。) .defaultToolCallbacks(mcpToolProvider) .build(); } }如果你不想走 MCP想直接在 Spring AI 里用Tool注解注册本地方法也可以这样写Component public class OrderTools { private final OrderRepository orderRepository; public OrderTools(OrderRepository orderRepository) { this.orderRepository orderRepository; } Tool(description 根据订单号查询订单详情) public Order queryOrder(ToolParam(description 订单号) String orderId) { return orderRepository.findById(orderId) .orElseThrow(() - new IllegalArgumentException(订单不存在: orderId)); } Tool(description 更新订单状态) public boolean updateOrderStatus( ToolParam(description 订单号) String orderId, ToolParam(description 目标状态) String status) { return orderRepository.updateStatus(orderId, status); } }然后在ChatClient里加上.defaultTools(new OrderTools(orderRepository))即可。两种方式选一种MCP 适合跨客户端复用Tool适合单体项目快速接入。配置到这里就完整了。三件套对应关系再强调一遍Base URL 是https://taotoken.net/apiKey 是你从 API Keys 页面拿的sk-开头字符串Model ID 是claude-sonnet-4-5这类。这三个任何一个错了后面都会报错。4. 验证请求一次订单从创建到履约的完整联调配置写完得实际跑一遍才知道通不通。我设计了一个完整的验证动作模拟用户下单让 Spring AI 解析意图依次调用checkStock、queryOrder、updateOrderStatus最后确认订单状态从CREATED流转到SHIPPED。先写一个测试 ControllerRestController RequestMapping(/api/order) public class OrderAiController { private final ChatClient orderChatClient; public OrderAiController(ChatClient orderChatClient) { this.orderChatClient orderChatClient; } PostMapping(/assist) public String assist(RequestBody AssistRequest request) { return orderChatClient.prompt() .user(request.getMessage()) .call() .content(); } }启动应用后用 curl 发一条自然语言指令curl -X POST http://localhost:8080/api/order/assist \ -H Content-Type: application/json \ -d { message: 订单 20240512001 现在什么状态如果还没发货帮我确认下 SKU-8899 的库存够的话就发货。 }预期行为是这样的模型先调用queryOrder拿到订单状态发现是PAID未发货然后调用checkStock查SKU-8899库存库存充足则调用updateOrderStatus把状态改成SHIPPED最后返回一段自然语言总结。实际返回大概长这样订单 20240512001 当前状态为 PAID已确认 SKU-8899 库存充足可用 120 件 已将订单状态更新为 SHIPPED履约流程已触发。如果你在日志里看到类似下面的工具调用记录说明 MCP 链路是通的Tool call: queryOrder({orderId: 20240512001}) Tool result: {status: PAID, amount: 299.00, ...} Tool call: checkStock({skuId: SKU-8899, quantity: 1}) Tool result: {available: 120} Tool call: updateOrderStatus({orderId: 20240512001, status: SHIPPED}) Tool result: {success: true}这里有个细节要注意模型决定调用哪些工具、按什么顺序调用是它自己根据 system prompt 和工具描述推理出来的。所以description写得越清楚模型调用越准。我见过有人把description写成「查询订单」结果模型经常传错参数改成「根据订单号查询订单详情返回状态、金额、商品列表」之后就稳定多了。验证通过的标准有三个一是 HTTP 返回 200 且内容合理二是日志里能看到完整的工具调用链三是数据库里订单状态确实变了。三个都满足说明 Spring AI MCP Server 的协同链路完全打通。如果你想验证更复杂的场景比如「批量查询今天所有未发货订单库存不足的自动取消」可以把指令改成这个观察模型是否会循环调用工具。这种多轮工具调用是 MCP 架构的真正价值所在。5. 本篇常见错误排查401、local proxy failed 与 choices 解析异常这一节把我踩过的坑和社区里高频出现的报错整理出来对照着排查能省不少时间。报错一401 Unauthorizedorg.springframework.ai.openai.api.OpenAiApiException: 401 Unauthorized {error:{message:Invalid API key,type:invalid_request_error}}原因通常是三个Key 复制时漏了字符、环境变量TAOTOKEN_API_KEY没生效、或者Authorization头没带上Bearer前缀。排查方法先在终端echo $TAOTOKEN_API_KEY确认变量有值再用第 2 节的 curl 命令直接测 Key。如果 curl 通但 Spring AI 不通那就是 yml 里api-key的引用写错了检查是不是写成了${TAOTOKEN_API_KEY:}这种带默认值的格式导致取到空串。报错二local proxy failed / Connection refusedjava.net.ConnectException: Connection refused: no further information Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException这个报错一般出现在 MCP Server 的 stdio 连接上。Spring AI 启动时会去拉起mcp.json里配置的command如果order-mcp-server.jar路径不对、或者 jar 没打包成功就会连接失败。排查步骤先手动执行java -jar ./mcp-order-server/target/order-mcp-server.jar看能不能正常启动如果报no main manifest attribute说明打包时没配spring-boot-maven-plugin的repackage目标。另外路径建议用绝对路径相对路径在不同工作目录下容易出问题。报错三reading choices 解析异常com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.util.List... from Object value at [Source: (String)...; line: 1, column: 1] (through reference chain: ...ChatCompletionMessage[choices])这个报错说明返回的 JSON 结构和 Spring AI 期望的不一致。常见原因是 Base URL 配错了比如写成了https://taotoken.net/api/v1/v1多了一层/v1导致请求打到了错误的路由上返回的是 HTML 错误页而不是 JSON。正确写法就是https://taotoken.net/apiSpring AI 内部会拼/v1/chat/completions。另一个可能是模型 ID 写错了服务端返回了错误结构检查model字段是不是和模型对话页面里列出的完全一致。报错四OAuth / token 过期类错误{error:invalid_token,error_description:The access token expired}如果你用的是 Coding Plan 或者带 OAuth 的接入方式token 是有有效期的。这种情况重新在控制台生成一个 Key 替换即可。注意不要把 OAuth token 和 API Key 混用两者格式和用途不同。报错五工具调用参数为空模型返回了工具调用意图但参数是空的或者类型不对。这通常是inputSchema写得不严谨比如quantity定义成string但模型传了数字。解决办法是把 schema 写严格required字段列全类型明确。另外 system prompt 里加一句「调用工具前确认所有必填参数已提供」也能明显改善。排查这类问题的通用思路是先看 HTTP 层通不通curl再看 Spring AI 配置对不对yml最后看 MCP 工具描述准不准mcp.json。三层逐层排除基本都能定位到。6. 把订单能力标准化输出下一步怎么走走到这里你已经有了一个能跑通的 Spring AI MCP Server 订单处理链路。但真正落地到生产还有几件事值得做。第一件是把 MCP Server 的工具集扩展完整。我上面只给了queryOrder、checkStock、updateOrderStatus三个实际业务里还需要createOrder、applyCoupon、splitOrder、refundOrder等。每加一个工具就在mcp.json的tools数组里加一段 schema然后在 MCP Server 里实现对应方法。工具描述要写得像给新人看的接口文档模型才能用对。第二件是加一层权限和审计。MCP Server 暴露的工具是模型可以直接调的像updateOrderStatus、refundOrder这种写操作一定要加权限校验和操作日志。我的做法是在 MCP Server 里加一个ToolInterceptor每次工具调用前检查当前会话的权限范围调用后把orderId、toolName、params、result写进审计表。这样出了问题能追溯。第三件是监控模型调用质量。订单场景对准确性要求高模型偶尔会理解错意图。建议在 Spring AI 侧加一个Advisor记录每次请求的 prompt、工具调用链、最终响应定期抽样人工检查。如果发现某类指令经常出错就回去优化 system prompt 或工具描述。关于接入方式的选择再补充一点如果你只是自己项目里用Spring AI 的Tool注解方式最省事不用额外部署 MCP Server。但如果你希望这套订单能力被多个 AI 客户端复用比如同时给 Spring AI 应用、Claude Code、Cline 用那就值得把 MCP Server 独立部署。MCP 的价值就在于「一次定义多处调用」。最后给一个实用建议先在测试环境把整条链路跑通用真实订单数据验证工具调用的准确性确认没问题再上生产。生产环境的 Key 单独申请一个和测试环境隔离方便做额度控制和问题定位。API Key 管理在 https://taotoken.net/console/api-keys 接入文档在 https://taotoken.net/doc 遇到配置问题先翻文档大部分坑里面都有说明。