Spring AI企业级应用实战(6):MCP Client/Server接入、工具发现与生产治理

文章摘要

前五篇已经完成DeepSeek接入、统一ChatClient、流式输出、Chat Memory和Tool Calling。本篇把工具能力从本地Java方法扩展到标准MCP体系:使用Spring AI 2.0构建MCP Server,通过@McpTool暴露订单查询能力;在另一个Spring Boot应用中配置MCP Client,将远程工具自动接入ChatClient;最后补齐工具过滤、租户隔离、权限、超时、名称冲突、审计和协议迁移。完成后,业务Agent无需直接依赖订单系统SDK,即可通过标准协议调用企业工具。

一、本篇目标

最终架构:

用户请求
→ CustomerAgent
→ Spring AI ChatClient
→ ToolCallingAdvisor
→ MCP ToolCallback
→ Order MCP Server
→ Order Service
→ Database

工程拆成两个应用:

spring-ai-mcp-order-server
spring-ai-enterprise-agent

MCP Server负责

  • 暴露工具;
  • 业务参数校验;
  • 资源归属校验;
  • 返回结构化结果;
  • 工具级审计。

Agent应用负责

  • 连接MCP Server;
  • 发现工具;
  • 将工具交给模型;
  • 控制模型调用循环;
  • 管理用户对话和最终回答。

二、为什么不继续使用本地@Tool

本地Tool适合:

  • 当前应用内部能力;
  • 简单工具;
  • 与Agent同生命周期;
  • 不需要跨语言复用。

MCP适合:

  • 工具属于独立业务系统;
  • 多个Agent共享;
  • Java、Python和桌面客户端都要使用;
  • 需要独立扩容;
  • 需要统一权限与审计;
  • 工具团队和Agent团队独立发布。

订单系统不应该为了给Agent调用,就把SDK和数据库访问代码复制到每个Agent项目中。

三、创建MCP Server项目

依赖:

            org.springframework.ai
            spring-ai-bom
            2.0.0
            pom
            import






        org.springframework.ai
        spring-ai-starter-mcp-server-webmvc



        org.springframework.boot
        spring-boot-starter-validation



        org.springframework.boot
        spring-boot-starter-actuator

四、配置服务端

application.yml

server:
  port: 8091

spring:
  application:
    name: order-mcp-server

  ai:
    mcp:
      server:
        name: order-mcp-server
        version: 1.0.0
        protocol: STATELESS

本例中的订单查询工具是独立请求—响应,不需要服务端主动Sampling和Elicitation,因此选择STATELESS。

如果后续工具需要双向交互,应评估切换:

protocol: STREAMABLE

五、定义领域返回对象

package com.zyentor.order.mcp.domain;

import java.time.Instant;

public record OrderStatusResult(
        String orderId,
        String status,
        String logisticsStatus,
        Instant updatedAt
) {
}

不要让工具直接返回数据库Entity。

原因:

  • 字段过多;
  • 可能泄露内部信息;
  • Schema不稳定;
  • 懒加载异常;
  • 与表结构耦合。

六、定义工具参数

package com.zyentor.order.mcp.tool;

import jakarta.validation.constraints.NotBlank;

public record OrderStatusArgs(
        @NotBlank
        String orderId
) {
}

工具参数应尽量:

  • 字段少;
  • 类型明确;
  • 有校验;
  • 不包含tenantId和userId等可信上下文。

租户信息应来自认证和传输上下文。

七、实现订单服务

package com.zyentor.order.mcp.service;

import com.zyentor.order.mcp.domain.OrderStatusResult;

public interface OrderQueryService {

    OrderStatusResult queryStatus(
            String tenantId,
            String orderId
    );
}

实现层必须按租户查询:

WHERE tenant_id = ?
AND order_id = ?

不能先按订单号查出数据,再在Java中判断租户。

八、使用@McpTool暴露工具

package com.zyentor.order.mcp.tool;

import com.zyentor.order.mcp.domain.OrderStatusResult;
import com.zyentor.order.mcp.service.OrderQueryService;

import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.stereotype.Component;

@Component
public class OrderMcpTools {

    private final OrderQueryService orderQueryService;
    private final McpIdentityResolver identityResolver;

    public OrderMcpTools(
            OrderQueryService orderQueryService,
            McpIdentityResolver identityResolver
    ) {
        this.orderQueryService = orderQueryService;
        this.identityResolver = identityResolver;
    }

    @McpTool(
        name = "order_query_status",
        description = "当用户询问某个具体订单的当前状态、物流状态或是否完成签收时调用。必须提供orderId。不能用于查询商品库存或其他租户订单。"
    )
    public OrderStatusResult queryStatus(
            OrderStatusArgs args
    ) {
        McpIdentity identity =
                identityResolver.current();

        return orderQueryService.queryStatus(
                identity.tenantId(),
                args.orderId()
        );
    }
}

具体注解包名和上下文参数应以当前Spring AI 2.0.x文档及项目依赖为准。

九、为什么Description必须写清楚边界

模型主要通过:

工具名称
+Description
+JSON Schema

选择工具。

错误:

查询订单

推荐:

当用户询问某个具体订单的当前状态、物流状态或是否完成签收时调用。必须提供orderId。不能用于查询商品库存。

Description既帮助选择,也减少误调用。

十、身份上下文不能由模型提供

错误工具参数:

{
  "tenantId": "T001",
  "userId": "U1001",
  "orderId": "A1001"
}

模型可以修改前两个字段。

正确结构:

tenantId、userId
→ Access Token和SecurityContext

orderId
→ 模型工具参数

定义:

public record McpIdentity(
        String tenantId,
        String userId,
        Set scopes
) {
}

十一、服务端工具权限

public void requireScope(
        McpIdentity identity,
        String requiredScope
) {
    if (!identity.scopes().contains(requiredScope)) {
        throw new AccessDeniedException(
                "缺少Scope:" + requiredScope
        );
    }
}

调用工具时:

requireScope(identity, "order:read");

即使客户端工具列表已经过滤,服务端仍必须再次授权。

十二、创建Agent客户端项目

依赖:

    org.springframework.ai
    spring-ai-starter-model-deepseek



    org.springframework.ai
    spring-ai-starter-mcp-client-webflux



    org.springframework.boot
    spring-boot-starter-webflux

十三、配置MCP Client

Spring AI MCP客户端支持多个命名连接。

配置结构应根据当前2.0.x官方文档填写,核心信息包括:

客户端启用
客户端类型SYNC或ASYNC
初始化开关
请求超时
服务端名称
Streamable或Stateless地址
认证

通用配置:

spring:
  ai:
    mcp:
      client:
        enabled: true
        initialized: true
        type: SYNC
        request-timeout: 20s
        toolcallback:
          enabled: true

生产流式应用可使用:

spring-ai-starter-mcp-client-webflux

十四、自动工具适配

当:

toolcallback.enabled: true

MCP工具可以被适配为Spring AI ToolCallback,进入统一Tool Calling体系。

完整链路:

MCP tools/list
→ Tool定义
→ ToolCallback
→ ChatClient
→ ToolCallingAdvisor
→ MCP tools/call

因此,Spring AI本地Tool与MCP Tool可以共享模型调用机制。

十五、构建Agent ChatClient

@Configuration
public class AgentClientConfig {

    @Bean
    ChatClient orderAgentChatClient(
            ChatClient.Builder builder
    ) {
        return builder
                .defaultSystem("""
                        你是企业订单助手。

                        规则:
                        1. 订单实时状态必须调用工具查询,不得猜测。
                        2. 用户没有提供订单号时,先要求补充。
                        3. 不得查询或透露其他租户的订单。
                        4. 工具失败时明确说明,不得编造结果。
                        """)
                .build();
    }
}

如果自动配置没有将MCP工具接入目标ChatClient,应显式检查或注入对应ToolCallback Provider。

十六、业务Service

@Service
public class OrderAgentService {

    private final ChatClient chatClient;

    public OrderAgentService(
            @Qualifier("orderAgentChatClient")
            ChatClient chatClient
    ) {
        this.chatClient = chatClient;
    }

    public String chat(String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

测试:

查询订单A1001现在到哪里了?

模型应:

选择order_query_status
→ 生成orderId=A1001
→ MCP调用
→ 根据结果回答

十七、为什么工具已经发现,模型仍可能不调用

检查:

  • 模型支持Tool Calling;
  • 工具Description;
  • Schema;
  • ChatClient实际工具数量;
  • Prompt是否要求实时数据必须查询;
  • 工具名称是否冲突;
  • 工具是否被过滤;
  • 模型是否直接使用常识回答。

建立测试用例:

模型不可能从训练数据知道A1001实时状态

十八、多个MCP Server的名称冲突

假设:

订单Server:search
知识Server:search
CRM Server:search

必须使用前缀:

order_search
knowledge_search
crm_search

命名规则:

领域_动作_对象

例如:

order_query_status
wms_query_inventory
crm_search_customer
knowledge_search_policy

十九、工具过滤

不要把所有工具一次性交给所有Agent。

订单Agent只需要:

order_query_status
order_query_logistics

不需要:

finance_refund
user_delete
admin_update_role

过滤维度:

  • Agent类型;
  • 租户;
  • 用户角色;
  • 环境;
  • 风险等级;
  • 模型能力。

二十、超时、取消与重试

MCP客户端通用超时:

spring:
  ai:
    mcp:
      client:
        request-timeout: 20s

但不同工具需要不同策略。

查询订单:

5秒
可重试1次

取消订单:

15秒
默认不自动重试
除非有幂等键

用户取消流式回答时,还应传播取消信号到:

ChatClient
→ Tool Calling
→ MCP Client
→ 上游HTTP请求

二十一、高风险工具不能直接开放

本篇只实现只读查询工具。

如果增加:

order_cancel
order_refund

必须增加:

参数校验
资源归属
人工确认
幂等键
审批状态
执行审计

不能仅依赖模型问一句:

你确认吗?

审批必须由业务控制面记录和验证。

二十二、工具调用审计

服务端记录:

request_id
tenant_id
user_id
tool_name
object_id
argument_hash
result_status
duration_ms
protocol_version
client_name

Agent端记录:

conversation_id
model
工具选择
工具调用次数
最终回答
Token
成本

两端通过request_id关联。

二十三、MCP工具缓存

工具列表可以缓存,但需要:

ttlMs
cacheScope
listChanged
租户缓存键

工具新增、删除和权限变化后应触发失效。

执行时仍需重新鉴权,不能把列表缓存视为永久授权。

二十四、协议选择

本例选择:

STATELESS

因为订单查询是独立请求。

如果后续需要:

  • 服务端请求用户补充参数;
  • 服务端请求客户端模型执行Sampling;
  • 双向进度交互;

应评估:

STREAMABLE

本地开发工具则可以使用STDIO。

二十五、MCP 2026-07-28兼容注意事项

Spring AI项目应确认:

Spring AI具体版本
MCP Java SDK具体版本
支持的协议版本
STATELESS是否只是传输能力
请求路由头是否经过网关
列表缓存是否正确实现
客户端和服务端互操作性

不要仅因为配置项存在,就宣布完整支持全部新规范扩展。

二十六、健康检查

应用启动后检查:

MCP Client是否初始化
Server协议版本
工具数量
必需工具是否存在
工具Schema是否合法
认证是否成功

如果核心工具缺失,可以让实例健康检查失败,避免接收生产流量。

二十七、测试

1. 工具发现测试

必须存在order_query_status

2. 正常调用

输入A1001
→ 返回正确状态

3. 缺少订单号

模型请求用户补充
不得调用空参数

4. 跨租户

T001用户查询T002订单
→ 拒绝

5. 上游超时

返回明确错误
不得编造订单状态

6. 工具被删除

缓存失效
Agent不再选择旧工具

二十八、生产架构升级

单一Server:

Agent
→ Order MCP

多业务系统后:

Agent
→ MCP Gateway
→ Order MCP
→ WMS MCP
→ CRM MCP
→ Knowledge MCP

网关负责:

  • 统一发现;
  • 名称映射;
  • 权限;
  • 审批;
  • 限流;
  • 审计;
  • 降级。

二十九、完整调用链

HTTP用户请求
→ 认证上下文
→ OrderAgentService
→ ChatClient
→ ToolCallingAdvisor
→ MCP ToolCallback
→ MCP Client
→ Order MCP Server
→ 租户权限校验
→ OrderQueryService
→ 数据库
→ Tool Result
→ 模型生成最终回答

三十、下一步

下一篇将继续实现:

Spring AI企业级应用实战(7):RAG知识库、权限过滤与引用溯源。

将把企业文档检索接入当前统一ChatClient、Memory和MCP工具体系。

总结

Spring AI与MCP的组合解决了企业AI工具接入的两个问题:

Spring AI
→ 管理模型、Advisor和工具执行循环

MCP
→ 标准化远程工具、资源和Prompt

生产落地不能停留在“工具能调用”,还必须补齐:

协议选择
+工具发现
+名称治理
+租户权限
+超时重试
+高风险审批
+审计
+兼容测试

完成这些控制面后,MCP才能真正成为企业Agent的统一工具协议。

延伸阅读

如果你正在关注企业级 AI 应用、Spring AI、RAG、Agent 与 MCP 工程化落地,欢迎访问 智元界

https://www.zyentor.com/

智元界将持续分享可运行的技术实战、架构设计、问题排查与企业应用案例。