工具权限与评分实战:scope拦截+健康度指标(MCP实战手记·篇12 延伸)
MCP工具权限 / 工具评分 / 可观测性
本篇是《MCP实战手记》篇12(企业级Toolbox治理)的延伸篇,只拆「权限拦截 + 健康度评分」两个点:把篇12 §3 两层 scope 拦截和 §7 看板指标拆开——scope 聚合防爆炸(踩坑5)、成功率/P99/报错率告警阈值给落地值,并接篇9 身份、篇13 多租户。仓库(tag
v12):Gitee
0. 为什么权限和评分要一起讲
篇12 开篇六类事故里,「权限泛滥」和「质量失控」是慢性毒:前者越权调工具,后者不知道哪个工具稳、哪个天天 500。
权限解决"谁能调",评分解决"调得稳不稳"——治理层两端:发现/调用之前先过权限,调用之后用指标反向淘汰劣质工具。
本文只做 caller+scope 两层拦截 + 健康度指标采集(代码量中,只给变化部分);真实 IdP、治理 UI 标待生产,见篇13。
1. 两层拦截:发现层 + 调用层
篇12 §3 已定:①发现层list()按 caller 过滤可见 Toolbox(看不到无权工具);②调用层实际调前校验requiredScope(调了不该调的被拦)。管理器只给元数据+查询 API,拦截发生在分发层/网关层。
// 发现层:caller 看不到它无 scope 的工具List<ToolboxDefinition>list(StringcallerId,List<String>scopes,Filtersf){returnregistry.list(f).stream().filter(def->scopes.containsAll(def.getRequiredScope()))// 可见需持有全部 requiredScope.toList();}// 调用层:resolve 时再拦一次ToolboxDefinitionresolve(StringcallerId,List<String>scopes,ToolboxIdid,Stringv){ToolboxDefinitiondef=registry.resolve(id,v);if(!scopes.containsAll(def.getRequiredScope()))// ② 调用层 scope 拦截thrownewAccessDeniedException("缺 scope: "+def.getRequiredScope());returndef;}两层都要有:发现层防"看见不该看的",调用层防"绕过列表直接调"。
默认最小权限:工具若未配
requiredScope视为公开(可见可调)。生产建议反设为"无 scope 不可见",公开工具显式打标,避免漏配变成越权。
2. scope 聚合:别让每工具一个 scope(踩坑5)
踩坑5原文:每工具一个 scope,配置膨胀难维护。例子:get_report配scope:report:gen、query_store配scope:store:query…… 工具 50 个就 50 个 scope,运维者记不住、配错就大面积 403。
修法——角色级 scope 聚合:把业务相近的工具归到一个角色 scope。
治理前(每工具一 scope,爆炸): get_report -> scope:report:gen query_store -> scope:store:query export_csv -> scope:report:export ...(50 个工具 = 50 个 scope) 治理后(角色级 scope,收敛): get_report / query_store / export_csv -> scope:toolbox:retail # 零售工具一组 audit_log -> scope:toolbox:admin # 管理面单独一组// 注册时把工具归到角色 scope,而非逐个细粒度voidregister(ToolboxDefinitiondef,StringroleScope){def.setRequiredScope(List.of(roleScope));// 角色级,而非 scope:report:genregistry.register(def);}caller 拿到scope:toolbox:retail就能调零售组全部工具,新增同组工具无需改 caller 授权——权限随工具规模线性收敛。
3. 健康度指标:成功率/P99/报错率
篇12 §7 看板给过阈值(参考值,按业务调):成功率<99%告警、P99>3s告警、报错率>1%告警。采集点在调用拦截处埋点:
// 埋点挂在【实际工具执行】处(MCP 分发层拦截器 / AOP 包裹 @Tool 方法),// 不要只挂在 resolve——resolve 只选定义不执行,否则成功率/P99 统计失真voidafterCall(ToolboxIdid,longcostMs,booleanok){metrics.timer("toolbox.call",id).record(costMs);if(!ok)metrics.counter("toolbox.error",id).increment();// 成功率 = 1 - 报错数 / 调用数,Prometheus 滑动窗口(如 5m)算}告警阈值(与篇14 全链路监控打通):
指标 阈值 含义 成功率 < 99% 接口不稳定,需排查 P99 耗时 > 3s 慢调用拖累调用方 报错率 > 1% 异常偏多,可能要 deprecate > 阈值需配「持续 5min 超阈 + 告警静默」再触发,避免瞬时抖动误报;窗口按业务调。低于阈值的工具在 §7 看板标红,配合 §6 弃用流程——质量差的先标期下线,形成"指标驱动治理"闭环。
4. 与篇9身份 / 篇13多租户衔接
- caller 身份来自篇9 身份透传(
open_id/user_id),scopes由 IdP 下发;本期用内存 caller→scope 映射模拟,真实集成待生产。 tenantId字段篇12 已落库并list()/resolve()过滤就位,多租户隔离标待篇13 消费——权限与隔离在同一分发层叠加,互不打架。
5. 岗位对照
本篇能力 -> 对应岗位 / 方向 scope 两层拦截 -> IAM / 权限管理 / 零信任 角色级 scope 聚合 -> 平台权限治理 健康度评分 + 告警阈值 -> SRE / 可观测性 指标驱动弃用闭环 -> 平台稳定性6. 边界声明
- 本机已验证:两层拦截(list 过滤 + resolve 拦 scope)、角色级 scope 聚合、指标采集埋点逻辑。
- 待生产验证:真实 IdP caller/scope 下发(篇9)、治理 UI、与篇14 监控告警链路。
- 本文只拆权限 + 评分两点;治理层总览见篇12 深度文。
同专题推荐:
篇12 深度文:企业级Toolbox治理
篇12·版本灰度实战:caller 路由到指定版本
篇12·弃用迁移实战:deprecate→removed 全链路
篇12·多实例一致性深潜:Redis 与本地缓存降级
代码仓库(tag
v12):Gitee