1. 企业为什么需要 MCP 治理
在个人开发者手里,MCP 是效率工具;在企业里,它是「一群能改生产数据的自治代理」。治理要回答的不是「能不能用」,而是「谁能用、能用到什么程度、出了事谁负责、怎么追溯」。
1.1 企业引入 MCP 的三类风险
# 1) 权限风险: 一个连接了云账号的 MCP 服务器 = 一把万能钥匙
# 2) 合规风险: 数据出境、PII 泄漏、操作不可审计
# 3) 影子风险: 员工私自接入未审批的服务器/工具
# 治理的目标不是"禁止",而是"可控地放开"
1.2 治理框架的分层
| 层 | 关注 | 典型手段 |
|---|---|---|
| 身份层 | 谁 | SSO、OAuth、服务身份 |
| 授权层 | 能做什么 | RBAC/ABAC、工具级策略 |
| 执行层 | 怎么做 | 审批、沙箱、配额 |
| 记录层 | 做了什么 | 审计、追踪、留存 |
| 生命周期 | 怎么演进 | 上线评审、版本管理、退役 |
一句话:治理不是给创新踩刹车,而是给「不可逆的操作」装上刹车。
2. 权限模型与 RBAC
MCP 的权限粒度天然是「工具」:tools/list 决定模型看得见什么,tools/call 决定模型能做什么。RBAC 就建立在这两个动作之上。
2.1 角色设计
| 角色 | 可见工具范围 | 写操作 | 审批 |
|---|---|---|---|
| viewer | 只读类(search/list/get) | 否 | 否 |
| developer | 只读 + 开发类写(branch、draft PR) | 有限 | 否 |
| operator | 运维类(重启、扩缩容) | 是 | 部分需审批 |
| admin | 全部 | 是 | 高危需双人 |
2.2 权限判定
// 工具级 RBAC:先看可见性,再看可执行性
interface Policy {
effect: "allow" | "deny";
roles: string[];
tools: string[]; // 支持通配符 github__*
conditions?: {
requireApproval?: boolean;
maxPerHour?: number;
timeWindow?: string; // 如 "09:00-18:00"
};
}
function authorize(principal: Principal, tool: string): Decision {
const rules = policies.filter(
(p) => p.roles.includes(principal.role) && matchGlob(p.tools, tool)
);
// deny 优先于 allow(显式拒绝覆盖一切)
if (rules.some((r) => r.effect === "deny")) return { allow: false };
const allow = rules.find((r) => r.effect === "allow");
if (!allow) return { allow: false, reason: "no matching allow rule" };
return {
allow: true,
requireApproval: allow.conditions?.requireApproval ?? false,
};
}
2.3 工具可见性裁剪
# 关键原则: 不可见的工具不存在
# 1) tools/list 只返回该主体被授权的工具
# 2) 模型无法"猜测"未列出的工具名(tools/call 也会被拒)
# 3) 描述里不泄漏未授权工具的存在
# 4) 按角色/部门/租户动态裁剪,而非静态全局列表
# 权限的边界要在"列表层"就收紧,而不是只靠"调用层"拦截
一句话:RBAC 在 MCP 里要落到「工具」这个粒度——列表里看不见,调用时也调不动。
3. 审批与人工确认
有些操作不可逆:删库、发版、转账、删云资源。这类操作不能只靠权限,还要靠「人在回路」(human-in-the-loop)的审批。
3.1 分级策略
| 风险级 | 示例 | 处理 |
|---|---|---|
| 低 | 查询、搜索 | 直接执行 |
| 中 | 创建草稿、开分支 | 记录,事后可查 |
| 高 | 合并 PR、部署预发 | 执行前确认 |
| 极高 | 删生产资源、改权限 | 双人审批 + 二次确认 |
3.2 审批拦截实现
# 高危工具在网关侧拦截,返回"待审批"而非直接执行
async def call_tool(name: str, args: dict, principal: Principal):
decision = authorize(principal, name)
if not decision.allow:
raise McpError(-32003, "forbidden")
if decision.require_approval:
ticket = approvals.create(
principal=principal.id,
tool=name,
args=args,
risk=assess_risk(name, args),
approvers=resolve_approvers(name),
)
# 不是错误,而是"已受理":模型知道要等人
return {
"status": "pending_approval",
"ticket_id": ticket.id,
"hint": "该操作需人工审批,审批通过后会自动执行",
}
return await execute(name, args)
3.3 二次确认模式
# 模型驱动的二次确认(MCP 的 elicitation/sampling 思路)
# 1) 工具返回"确认请求",列出将被影响的具体资源
# 2) 客户端展示给用户,用户明确确认
# 3) 模型带确认令牌再次调用,服务器校验令牌后执行
# 确认要"具体到资源": "确认删除 3 个 Pod: a/b/c",而非"确认执行吗?"
一句话:审批的价值在于「不可逆操作前的那一次停顿」——停顿越具体,越有效。
4. 审计日志与合规
审计不是「有日志就行」,而是要能回答监管的问题:谁、何时、对什么、做了什么、结果如何、谁批准的。
4.1 审计字段
| 字段 | 说明 | 合规价值 |
|---|---|---|
| principal | 主体身份 | 追责 |
| tool | 工具全名(含命名空间) | 行为还原 |
| args_digest | 参数摘要/脱敏 | 隐私 + 还原 |
| decision | allow/deny/approval | 策略生效验证 |
| approver | 审批人 | 责任链 |
| result | 成功/失败/错误码 | 效果 |
| trace_id | 全链路 ID | 关联 |
| ts | 时间戳(含时区) | 时序 |
4.2 不可篡改的审计流
// 审计事件追加写 + 哈希链,防篡改
interface AuditEvent {
seq: number;
prev_hash: string;
payload: Record<string, unknown>;
hash: string;
}
function appendAudit(prev: AuditEvent | null, payload: Record<string, unknown>): AuditEvent {
const seq = (prev?.seq ?? 0) + 1;
const prevHash = prev?.hash ?? "genesis";
const hash = sha256(prevHash + JSON.stringify(payload));
return { seq, prev_hash: prevHash, payload, hash };
}
// 校验时重算整条链,任一环节被改动即被发现
4.3 留存与脱敏
# 1) 留存周期: 按合规要求(金融常 5-7 年,一般 1 年)
# 2) 参数脱敏: 密码/token/PII 落库前替换为 [REDACTED]
# 3) 内容留存: 是否存参数全文取决于合规;至少存摘要 + 哈希
# 4) 导出接口: 支持按主体/时间导出,应对监管问询
# 5) 冷热分层: 近 90 天热存可查,更早转冷归档
# 审计的"可用性"和"完整性"同等重要——查不到等于没有
5. 多租户隔离
SaaS 或大企业里,MCP 平台常常服务多个租户(部门/客户)。隔离不彻底,A 租户的操作会影响或泄漏 B 租户。
5.1 隔离维度
| 维度 | 弱隔离 | 强隔离 |
|---|---|---|
| 身份 | 共享用户池 | 每租户独立 IdP |
| 数据 | 行级 tenant_id 过滤 | 独立库/独立存储 |
| 凭证 | 共享服务账号 | 每租户独立凭证 |
| 配额 | 全局配额 | 每租户独立配额 |
| 运行时 | 共享沙箱 | 每租户独立沙箱池 |
| 日志 | 混合存储 | 分租户存储 |
5.2 租户上下文注入
// 租户 ID 由网关从令牌解析,绝不接受模型传参
function buildBackendContext(ctx: CallContext) {
const tenant = ctx.token.tenant_id; // 来自已验证的令牌
if (!tenant) throw new McpError(-32001, "missing tenant");
return {
tenant,
// 后端据此做数据隔离;模型无法伪造
credential: credentialVault.get(tenant, ctx.backendId),
quota: quotaStore.scope(tenant),
};
}
5.3 隔离失败模式
# 1) 模型传 tenant_id: 可被提示注入篡改 → 必须来自令牌
# 2) 缓存键不含租户: A 的结果被 B 命中 → 缓存键含 tenant
# 3) 连接池跨租户复用: 凭证串号 → 按租户分池
# 4) 日志混存: 越权读取 → 分租户存储 + 访问控制
# 多租户的黄金法则: 租户身份只在"可信边界"内产生,永不来自输入
一句话:多租户隔离的关键是「租户上下文只能由可信来源注入」,一旦允许模型传参,隔离即告失守。
6. 策略即代码
手工在控制台点权限,规模一大就失控。企业治理的趋势是把策略写成代码:可评审、可版本化、可测试、可回滚。
6.1 策略仓库结构
# policies/
# roles.yaml # 角色定义
# bindings.yaml # 主体 → 角色绑定
# tools.yaml # 工具清单与风险级
# tenants.yaml # 租户配置
# tests/ # 策略测试用例
# CI: 策略变更 → 语法校验 → 单元测试 → 评审 → 生效
6.2 策略定义示例
# policies/tools.yaml
tools:
- name: github__create_pr
risk: medium
allowed_roles: [developer, operator, admin]
- name: k8s__delete_namespace
risk: critical
allowed_roles: [admin]
require_approval: true
approvers: [sre-lead]
max_per_hour: 2
- name: aws__terminate_instance
risk: critical
allowed_roles: [admin]
require_approval: true
deny_if:
- "instance.env == 'prod' and principal.role != 'sre-admin'"
6.3 策略测试
def test_critical_tool_requires_approval():
d = authorize(principal(role="admin"), "k8s__delete_namespace")
assert d.allow is True
assert d.require_approval is True
def test_developer_cannot_delete_namespace():
d = authorize(principal(role="developer"), "k8s__delete_namespace")
assert d.allow is False
def test_prod_terminate_denied_for_non_sre():
d = authorize(principal(role="admin"), "aws__terminate_instance",
ctx={"instance": {"env": "prod"}})
assert d.allow is False
一句话:策略即代码的意义在于「权限变更走 Code Review」——而不是某个人在控制台点了一下没人知道。
7. 影子工具治理
「影子工具」指未经审批就接入的 MCP 服务器或工具:员工自己装了个连数据库的 server,或者某个工具悄悄新增了危险能力。这是企业治理最难的一环。
7.1 影子来源
# 1) 员工本地: 在 IDE 里直连未审批的 server
# 2) 服务器漂移: 已审批的 server 新增了未审批的工具
# 3) 依赖漂移: 服务器升级带入了新的下游能力
# 4) 命名伪装: 工具名看似无害,实际是写操作
# 影子工具的危险在于"看不见",治理的第一步是"看得见"
7.2 发现与收敛
| 手段 | 说明 |
|---|---|
| 网络出口管控 | 只允许访问已注册的 MCP 端点 |
| 客户端配置基线 | 强制 IDE 走统一网关,禁直连 |
| 工具清单巡检 | 定期 diff 实际 tools/list 与注册目录 |
| 写操作探测 | 对未登记的工具做行为探测(是否改数据) |
| 上报通道 | 允许员工申报,简化审批使其愿意合规 |
7.3 工具清单漂移检测
# 定期比对:注册目录 vs 实际能力,发现未登记工具
def detect_drift(registry: dict, actual: dict) -> list[str]:
findings = []
for ns, tools in actual.items():
registered = set(registry.get(ns, {}).get("tools", []))
for t in tools:
if t not in registered:
findings.append(f"unregistered tool: {ns}__{t}")
# 反向:注册了但实际没有(可能被摘除)
for t in registered - set(tools):
findings.append(f"missing tool: {ns}__{t}")
return findings
8. 供应链风险
MCP 生态里大量服务器来自社区,安装即运行第三方代码。企业必须像对待依赖一样对待 MCP 服务器。
8.1 风险面
# 1) 恶意服务器: 以工具为名窃取凭证/数据
# 2) 依赖投毒: 服务器依赖链中的恶意包
# 3) 提示注入: 服务器返回的内容诱导模型越权
# 4) 过度索取: 申请远超功能的权限(如全盘读写)
# 5) 无维护: 停更服务器积累未修复漏洞
8.2 准入清单
| 检查项 | 要求 |
|---|---|
| 来源 | 官方/知名组织/内部自研 |
| 权限 | 最小权限,符合功能所需 |
| 依赖 | 无已知高危 CVE,锁版本 |
| 网络 | 明确其外联需求 |
| 代码 | 可审计(开源或提供源码) |
| 维护 | 近期有更新、有 issue 响应 |
| 许可 | 许可证合规 |
8.3 服务器准入流水线
# CI 中的 MCP server 准入检查
stages:
- name: sbom
run: syft packages dir:. -o spdx-json > sbom.json
- name: vuln_scan
run: grype sbom.json --fail-on high
- name: capability_review
run: mcp-inspect tools --server ./server --assert-minimal
- name: egress_check
run: mcp-inspect network --server ./server --expect none
一句话:把 MCP 服务器当作「会读你数据的第三方依赖」来管——准入、锁版本、持续扫描。
9. 治理运营与度量
治理是持续运营,不是一次性项目。要有度量,才知道治理是否有效。
9.1 治理指标
# 1) 覆盖率: 接入网关的服务器占比(目标 100%)
# 2) 影子率: 未登记工具数 / 总工具数(目标 → 0)
# 3) 审批时效: 高危操作从申请到审批的时长
# 4) 拒绝率: 被策略拒绝的调用比例(异常升高需排查)
# 5) 审计完整率: 有完整审计记录的调用占比(目标 100%)
# 6) 准入周期: 新服务器从申请到上线的时长
9.2 运营节奏
# 1) 每日: 影子工具漂移报告
# 2) 每周: 高危操作审批复盘
# 3) 每月: 权限回收(离职/转岗)、配额调整
# 4) 每季: 服务器准入复审、策略评审
# 5) 按需: 安全事件响应
# 治理要"有节奏",否则会退化成"出事才管"
10. 常见陷阱
- 只在调用层拦截:工具列表不裁剪,模型仍能看到无权工具——列表与调用双层收紧。
- 权限写在控制台:无法评审、无法回滚——策略即代码。
- 审批流于形式:审批人不看内容就点同意——审批项要具体到资源与影响。
- 审计存明文参数:密码、token 落库——脱敏 + 哈希。
- 租户 ID 由模型传:提示注入即可越权——只从令牌解析。
- 忽略工具漂移:已审批的服务器悄悄加了写工具——定期 diff 巡检。
- 社区服务器即装即用:无 SBOM、无扫描——准入流水线。
- 治理无度量:不知道影子率、覆盖率——建立指标看板。
11. 总结
MCP 企业治理的核心命题是「在放开能力的同时守住边界」。五个支柱缺一不可:权限模型(工具级 RBAC,列表与调用双层收紧)、审批机制(按风险分级,高危不可逆操作人在回路)、审计合规(字段完整、防篡改、可脱敏、可留存)、多租户隔离(租户上下文只来自可信来源)、策略即代码(权限变更走评审与测试)。再叠加影子工具治理(发现并收敛未登记能力)与供应链管控(把 MCP 服务器当第三方依赖管理),才能让 MCP 在企业里既高效又可控。治理的成熟度不是靠「禁止」堆出来的,而是靠「看得见、管得住、查得到、可演进」一步步建起来的。建议与 https://plumephp.com/mcp-security-practices/、https://plumephp.com/mcp-oauth-authorization-session/ 和 https://plumephp.com/mcp-observability-debugging/ 结合,形成从认证到审计的完整闭环。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。