Spring AI 接入 MCP:工具调用不是“能调就行”,关键是边界治理

很多 Java 团队第一次做 AI 工具调用时,容易把问题简化成一句话:把内部接口注册成工具,让大模型自己决定什么时候调用。Demo 阶段这没问题,但一进入企业系统,很快会遇到三个现实问题:哪些工具能被调用、调用前后怎么审计、模型误调用时怎么兜底。

MCP 的价值不只是"让模型能调用工具"。它更像一层工具协议,把文件、数据库、内部 API、业务系统能力包装成 AI 应用可发现、可调用的上下文能力。而 Spring AI 的价值,是把这些能力纳入 Spring Boot 熟悉的配置、Bean、Advisor、Observability 和权限治理体系里。

本文只讲一个核心问题:在 Spring AI 里接入 MCP 时,不要把 MCP 工具裸塞给 ChatClient,应该把工具调用放进可治理的调用链。

1. MCP 解决的不是接口调用,而是工具协议统一

如果只是让大模型调用一个 Java 方法,Spring AI 自身的 Tool Calling 已经够用。比如把一个 @Tool 方法暴露给模型,模型根据上下文选择是否调用。

但企业系统里,工具来源往往不止一种:

  • 本地 Java 方法
  • 远程订单系统 API
  • CRM、工单、知识库、数据库查询
  • 文件系统、浏览器、DevOps 平台
  • 不同团队维护的工具服务

如果每个工具都按模型厂商的 function calling 格式单独接入,最终会变成一堆适配器。MCP 的思路是把这些能力抽象成标准协议,由 MCP Server 暴露工具、资源、提示词等能力,MCP Client 负责连接和调用。

在 Spring AI 中,应用可以作为 MCP Client 连接一个或多个 MCP Server,并把服务端暴露出来的工具转换成 Spring AI 的 Tool Callback,交给 ChatClient 使用。

一个典型链路如下:

这条链路里最容易被忽略的是 Advisor。很多示例会直接把工具挂到 ChatClient 上,但真实项目中,Advisor 往往才是工具治理的关键位置。

2. 为什么不能直接裸用 MCP 工具

假设我们接入了一个订单 MCP Server,里面有这些工具:

  • queryOrder
  • refundOrder
  • updateShippingAddress
  • queryUserProfile

如果直接把全部工具交给模型,风险很明显:模型可能在用户只是咨询时触发写操作,也可能在没有权限校验的情况下查询敏感信息。

更稳妥的做法是把工具分层:

  • 只读工具:允许模型在问答中自动调用,比如订单查询、库存查询。
  • 高风险工具:需要业务确认,比如退款、改地址、创建工单。
  • 敏感工具:需要额外鉴权,比如用户画像、合同、财务数据。
  • 禁用工具:当前业务场景不暴露给模型。

MCP 负责统一工具协议,Spring AI 负责把这些工具放进工程治理体系。二者结合时,不应该让模型成为最终权限决策者。

3. Spring Boot 中的接入方式

Spring AI 官方文档提供了 MCP Client Boot Starter。具体依赖名称和配置项可能随 Spring AI 版本变化,实际项目应以当前官方文档为准。下面代码展示的是接入思路,而不是完整脚手架。

Maven 依赖可以类似这样组织:

XML 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

一个常见配置是通过 stdio 或 SSE 连接 MCP Server。例如本地 stdio 方式通常会引用一个 MCP Server 配置文件:

XML 复制代码
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
    mcp:
      client:
        stdio:
          servers-configuration: classpath:/mcp-servers.json

mcp-servers.json 可以描述要启动或连接的 MCP Server。不同 MCP Server 的启动命令不同,比如 Node、Python 或独立二进制程序:

javascript 复制代码
{
  "mcpServers": {
    "order-tools": {
      "command": "java",
      "args": [
        "-jar",
        "order-mcp-server.jar"
      ]
    }
  }
}

在 Spring AI 中,MCP Client 可以把远端工具转换为 ToolCallbackProvider,再交给 ChatClient:

java 复制代码
@Configuration
class AiConfig {

    @Bean
    ChatClient chatClient(
            ChatClient.Builder builder,
            ToolCallbackProvider toolCallbackProvider
    ) {
        return builder
                .defaultSystem("""
                    你是订单客服助手。
                    查询类问题可以使用工具。
                    涉及退款、改地址、取消订单时,必须先向用户确认。
                    """)
                .defaultToolCallbacks(toolCallbackProvider)
                .build();
    }
}

这段代码的重点不是 API 本身,而是边界:系统提示词声明业务规则,工具回调提供可调用能力,但这还不够。因为提示词不是权限系统,不能只靠它防止误调用。

4. 用 Advisor 给工具调用加一道工程边界

Spring AI 的 Advisor 机制可以参与 ChatClient 调用链,适合放置记忆、RAG、日志、策略控制等逻辑。对于 MCP 工具调用,Advisor 可以承担三类职责:

  • 请求前:根据用户、租户、场景决定允许哪些工具。
  • 调用中:记录模型选择工具的原因、参数、耗时。
  • 响应后:检查是否出现敏感字段、危险操作或异常结果。

示意代码如下:

java 复制代码
@Component
class ToolGovernanceService {

    Set<String> allowedTools(String userRole, String scene) {
        if ("customer_service".equals(userRole)) {
            return Set.of("queryOrder", "queryLogistics");
        }
        if ("manager".equals(userRole)) {
            return Set.of("queryOrder", "queryLogistics", "createRefundRequest");
        }
        return Set.of();
    }

    void audit(String userId, String toolName, Map<String, Object> args) {
        // 写入审计日志:用户、工具名、参数摘要、traceId、时间
    }
}

真实项目中,工具过滤最好不要写死在 Prompt 里,而应该落在服务端策略中。原因很简单:Prompt 可以被用户输入干扰,而服务端权限判断必须稳定、可测试、可追踪。

如果当前版本的 Spring AI 暂时不能满足你对"动态过滤工具"的细粒度要求,也可以在 MCP Server 层做隔离:不同业务场景连接不同 MCP Server,或者同一个 Server 根据调用方身份只暴露部分工具。

5. MCP Server 侧也要控制工具粒度

很多团队设计 MCP Server 时,会直接把内部 API 一比一暴露成工具。这通常不是好设计。

比如内部订单系统可能有一个接口:

java 复制代码
POST /internal/order/update

它可以更新地址、备注、状态、金额等多个字段。如果直接包装成一个 MCP 工具,模型就拥有了一个过宽的操作面。

更合理的做法是面向 AI 场景重新设计工具:

java 复制代码
public class OrderTools {

    @Tool(description = "根据订单号查询订单基础信息,只返回客服问答需要的字段")
    public OrderView queryOrder(String orderNo) {
        // 调用内部订单服务,并裁剪敏感字段
    }

    @Tool(description = "创建退款申请,不直接执行退款,需要人工或规则系统审核")
    public RefundTicket createRefundRequest(String orderNo, String reason) {
        // 只创建申请,不直接退款
    }
}

工具不是内部接口的透传层,而是 AI 应用的业务能力边界。它应该具备三个特征:

  • 参数少且语义明确。
  • 返回值经过裁剪,不泄露无关字段。
  • 高风险操作改成"提交申请"或"生成草稿",不要直接执行最终动作。

这也是 Java 后端经验能发挥价值的地方。AI 应用不是绕过工程体系,而是更依赖工程体系。

6. 实际落地时的几个坑

第一个坑是把 MCP 当成 API 网关。MCP 不是替代 REST、RPC 或消息队列的通用网关,它面向的是 AI Client 与工具之间的上下文协议。业务系统仍然应该保留原有 API 边界、鉴权、限流和审计。

第二个坑是只记录最终回答,不记录工具调用。AI 应用排查问题时,只看模型回复基本没用。至少要记录用户问题、模型选择的工具、工具参数摘要、工具响应摘要、耗时、异常和 traceId。

第三个坑是把写操作交给模型自动执行。即使模型能力再强,也不应该让它在没有确认机制的情况下完成退款、删除、发券、改配置等动作。更稳的方案是"模型生成操作意图,业务系统执行规则判断"。

第四个坑是工具描述写得太像接口文档。工具描述是给模型看的,不是给后端开发看的。它应该说明"什么时候用、不能用来做什么、参数怎么填",而不是只写"查询订单接口"。

7. 一个更稳的企业架构

企业项目里,可以把 Spring AI + MCP 工具调用拆成四层:

  • Chat 层:负责会话、流式响应、用户体验。
  • Governance 层:负责 Advisor、权限、审计、工具策略。
  • MCP 层:负责连接不同 MCP Server。
  • Business Tool 层:负责真正访问订单、CRM、知识库等系统。

这样做的好处是职责清楚。模型负责理解意图和组织回答,MCP 负责工具协议,Spring Boot 负责治理和集成,业务系统负责最终规则。

MCP 让 AI 应用接入工具变得统一,但统一不等于放开。对 Java 后端团队来说,真正值得投入的不是把更多接口暴露给模型,而是把工具设计成可控、可审计、可回滚的业务能力。只有这样,Spring AI 接入 MCP 才不会停留在 Demo,而是能进入真实系统的生产链路。

相关推荐
YueJoy.AI1 小时前
创业团队如何进行绩效管理
人工智能·ai·语言模型
春日见1 小时前
RL精华知识
人工智能·机器学习
向量引擎1 小时前
从零起步,如何打造专属向量引擎 API 中转工作流?
java·服务器·前端
LJianK11 小时前
普通接口,用到getter和setter方法的地方,jackson转换
java
辰海Coding2 小时前
MiniSpring框架学习-分解 Dispatcher
java·学习·spring·架构
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第84题】【Mysql篇】第14题:为什么用 InnoDB 存储引擎的表建议用整型的自增主键?
java·开发语言·数据库·mysql·面试
小江的记录本2 小时前
【JVM虚拟机】JVM调优:常用JVM参数、调优核心指标、OOM排查、GC日志分析、Arthas工具使用(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
东方佑2 小时前
波动力学语言模型(Wave Dynamics Language Model, WDLM)
人工智能·语言模型·自然语言处理
John_ToDebug2 小时前
CLAUDE.md 与 Skills 的区别:一张表彻底分清
人工智能·经验分享·ai