用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/
智元界将持续分享可运行的技术实战、架构设计、问题排查与企业应用案例。