用Spring Boot搭建企业MCP工具网关:统一接入、租户隔离、白名单与审计

文章摘要

企业接入多个MCP Server后,如果让每个Agent直接连接订单、仓储、客户、知识库和文件工具,会快速出现认证分散、工具重名、权限不一致、审计缺失和服务端地址泄露等问题。本文使用Spring Boot设计一个MCP工具网关:上游连接多个MCP Server,下游向Agent提供统一工具目录,并在调用前执行租户校验、工具白名单、风险审批、参数脱敏、超时和审计。文章给出核心数据模型、路由代码和生产配置思路。

一、为什么需要MCP工具网关

没有网关时:

Agent A
├─ 订单MCP
├─ 仓储MCP
├─ CRM MCP
└─ 知识库MCP

Agent B
├─ 订单MCP
├─ 仓储MCP
└─ 财务MCP

问题包括:

  • 每个Agent保存多套凭证;
  • MCP Server地址暴露给业务应用;
  • 权限规则分散;
  • 工具名称冲突;
  • 无法统一限流;
  • 无法统一审计;
  • Server升级需要修改多个客户端;
  • 模型可能看到不该看到的工具;
  • 故障降级困难。

引入网关:

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

网关成为控制面,而不是简单反向代理。

二、网关应该负责什么

MCP Server注册
工具发现
工具名称规范化
租户与用户权限
工具白名单
风险分级
审批
限流
超时
重试
幂等
审计
可观测性
降级

网关不应该承载所有业务逻辑。

订单查询逻辑仍在订单服务,网关只负责是否允许调用以及如何安全路由。

三、项目结构

mcp-tool-gateway
├── config
│   ├── McpClientConfig.java
│   └── SecurityConfig.java
├── catalog
│   ├── ToolCatalog.java
│   ├── ToolDescriptor.java
│   └── ToolCatalogRefresher.java
├── policy
│   ├── ToolPolicyService.java
│   ├── RiskLevel.java
│   └── PermissionDecision.java
├── routing
│   ├── ToolRouter.java
│   └── UpstreamMcpServer.java
├── execution
│   ├── ToolExecutionService.java
│   ├── IdempotencyService.java
│   └── ApprovalService.java
├── audit
│   ├── ToolAuditService.java
│   └── ToolAuditEvent.java
└── web
    ├── ToolCatalogController.java
    └── ToolExecutionController.java

四、依赖

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






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



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



        org.springframework.boot
        spring-boot-starter-security



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



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

五、上游Server配置

enterprise:
  mcp:
    servers:
      order:
        url: https://internal.example.com/order/mcp
        timeout: 20s
        name-prefix: order
      warehouse:
        url: https://internal.example.com/wms/mcp
        timeout: 30s
        name-prefix: wms
      knowledge:
        url: https://internal.example.com/knowledge/mcp
        timeout: 15s
        name-prefix: knowledge

不要把Token直接写进YAML。

使用:

  • Vault;
  • Kubernetes Secret;
  • 云Secret Manager;
  • OAuth客户端凭证;
  • 工作负载身份。

六、统一工具描述模型

public record ToolDescriptor(
        String gatewayToolName,
        String upstreamServer,
        String upstreamToolName,
        String description,
        String inputSchema,
        RiskLevel riskLevel,
        Set requiredScopes,
        boolean approvalRequired,
        Duration timeout
) {
}

风险等级:

public enum RiskLevel {
    LOW,
    MEDIUM,
    HIGH,
    CRITICAL
}

示例:

knowledge_search_policy
→ LOW

order_query_status
→ LOW

order_cancel
→ HIGH

finance_refund
→ CRITICAL

七、工具名称规范化

上游可能都存在:

search
get_status
create

网关统一命名:

order_get_status
wms_get_inventory
crm_search_customer
knowledge_search_policy

映射:

public String gatewayName(
        String prefix,
        String upstreamName
) {
    return normalize(prefix)
            + "_"
            + normalize(upstreamName);
}

名称一旦对模型开放,应保持稳定。

上游改名时,网关可以保留旧别名,避免Prompt和评测集全部失效。

八、工具目录刷新

@Component
public class ToolCatalogRefresher {

    private final ToolCatalog catalog;
    private final List clients;

    @Scheduled(fixedDelayString = "${enterprise.mcp.refresh:PT5M}")
    public void refresh() {
        for (McpSyncClient client : clients) {
            refreshClient(client);
        }
    }

    private void refreshClient(McpSyncClient client) {
        var result = client.listTools();
        catalog.replace(
                client.getServerInfo().name(),
                result.tools()
        );
    }
}

生产代码需要处理:

  • 单个Server失败不清空旧目录;
  • 保存最后成功版本;
  • 记录刷新时间;
  • 校验工具Schema;
  • 检查高风险工具是否有策略;
  • 支持listChanged主动刷新。

九、租户和用户上下文

public record GatewayRequestContext(
        String requestId,
        String tenantId,
        String userId,
        Set scopes,
        String clientId
) {
}

这些信息应从认证系统获取,而不是信任模型生成的参数。

错误:

{
  "tenantId": "T002"
}

模型可以随意修改。

正确:

Access Token
→ SecurityContext
→ GatewayRequestContext

十、工具白名单

每个租户可以配置:

允许工具
禁止工具
按环境允许
按用户角色允许

数据模型:

public record ToolAccessPolicy(
        String tenantId,
        String toolName,
        boolean enabled,
        Set allowedRoles,
        Set requiredScopes,
        int callsPerMinute
) {
}

决策:

public PermissionDecision decide(
        GatewayRequestContext context,
        ToolDescriptor tool
) {
    if (!tenantPolicy.enabled(tool.gatewayToolName())) {
        return PermissionDecision.deny(
                "租户未启用该工具"
        );
    }

    if (!context.scopes().containsAll(
            tool.requiredScopes()
    )) {
        return PermissionDecision.deny(
                "缺少必要Scope"
        );
    }

    return PermissionDecision.allow();
}

十一、执行前参数校验

模型提交参数后先做:

JSON Schema校验
Bean Validation
业务范围校验
资源归属校验
敏感字段检测

例如:

public record CancelOrderArgs(
        @NotBlank String orderId,
        @NotBlank String reason
) {
}

还要验证:

订单是否属于当前租户
订单是否允许取消
当前用户是否有操作权限

JSON Schema合法并不代表业务合法。

十二、高风险工具审批

if (tool.approvalRequired()) {
    ApprovalRequest approval = approvalService.create(
            context,
            tool,
            sanitizedArguments
    );

    return ToolExecutionResult.pendingApproval(
            approval.id()
    );
}

审批页面展示:

  • 工具名称;
  • 业务影响;
  • 参数;
  • 当前用户;
  • 当前租户;
  • 风险原因;
  • 幂等键;
  • 预计执行结果。

批准后重新读取最新权限与业务状态,不能直接使用旧审批上下文永久执行。

十三、幂等设计

写操作需要幂等键:

tenantId
+toolName
+businessObjectId
+requestIntentHash
public String buildIdempotencyKey(
        GatewayRequestContext context,
        ToolDescriptor tool,
        String objectId,
        String argumentHash
) {
    return String.join(
            ":",
            context.tenantId(),
            tool.gatewayToolName(),
            objectId,
            argumentHash
    );
}

重复请求:

返回第一次执行结果
而不是再次取消订单或重复退款

十四、调用路由

@Service
public class ToolRouter {

    private final Map clients;
    private final ToolCatalog catalog;

    public CallToolResult route(
            String gatewayToolName,
            Map arguments
    ) {
        ToolDescriptor descriptor =
                catalog.require(gatewayToolName);

        McpSyncClient client = clients.get(
                descriptor.upstreamServer()
        );

        return client.callTool(
                descriptor.upstreamToolName(),
                arguments
        );
    }
}

实际API方法应按使用的MCP Java SDK版本调整,但架构原则一致。

十五、超时与重试

查询类工具:

可有限重试

写操作:

只有确认幂等后才能重试

策略:

工具 超时 重试
知识检索 10秒 1次
订单查询 5秒 1次
取消订单 15秒 默认0次
退款 30秒 默认0次

不要让HTTP客户端、MCP客户端、网关和Agent四层同时重试。

十六、审计事件

public record ToolAuditEvent(
        String requestId,
        String tenantId,
        String userId,
        String toolName,
        String upstreamServer,
        String argumentHash,
        String resultStatus,
        long durationMs,
        String approvalId,
        Instant timestamp
) {
}

日志中不要直接记录:

  • 密码;
  • Token;
  • 身份证;
  • 银行卡;
  • 完整客户隐私;
  • 文件正文。

保存:

脱敏参数
参数Hash
结果状态
影响对象ID

十七、向Agent暴露工具

网关可以有两种方式:

方式一:网关本身作为MCP Server

Agent MCP Client
→ Gateway MCP Server
→ Upstream MCP Servers

优点是协议统一。

方式二:转换为Spring AI ToolCallback

ChatClient
→ ToolCallback
→ Gateway内部路由

适合只服务Spring AI应用。

企业更通用的方式是让网关对外暴露标准MCP Server。

十八、健康检查

每个上游记录:

connected
protocol_version
tool_count
last_refresh
last_success
error_rate
P95_latency

网关整体不能因为一个非核心Server失败就完全不可用。

工具级降级:

知识工具不可用
→ 隐藏知识工具

订单查询不可用
→ 返回明确错误

退款工具不可用
→ 禁止执行并转人工

十九、监控指标

mcp_gateway_tool_call_count
mcp_gateway_tool_denied_count
mcp_gateway_approval_count
mcp_gateway_upstream_latency
mcp_gateway_upstream_error
mcp_gateway_catalog_tool_count
mcp_gateway_catalog_refresh_failure
mcp_gateway_idempotency_hit
mcp_gateway_cross_tenant_denied

二十、生产检查清单

□ 上游Server统一注册
□ 工具名称稳定且不冲突
□ 凭证不下发给业务Agent
□ 工具目录按租户过滤
□ 执行时再次鉴权
□ 高风险工具要求审批
□ 写操作具有幂等键
□ 参数和结果日志已脱敏
□ 超时与重试按工具配置
□ 上游故障支持工具级降级
□ 每次调用可以追溯
□ 跨租户请求默认拒绝

总结

企业MCP工具网关的价值不是把多个URL合并成一个URL,而是建立统一的工具控制面:

发现
+命名
+权限
+审批
+幂等
+审计
+观测

当工具数量、Agent数量和租户数量增长后,这一层会成为MCP进入生产环境的关键基础设施。

延伸阅读

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

https://www.zyentor.com/

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