mattijsmoens/sovereign-mcp
GitHub: mattijsmoens/sovereign-mcp
这是一个确定性的MCP安全架构,通过FrozenNamespace作为信任根解决Model Context Protocol工具验证的不可篡改问题。
Stars: 3 | Forks: 4
# 主权 MCP - 确定性 MCP 安全架构
**FrozenNamespace 作为模型上下文协议工具验证的信任根**
*主权之盾 / Mattijs Moens - 2026年3月*
[](https://www.python.org/downloads/)
[](LICENSE)
[]()
## 核心洞察
这些漏洞之所以存在,是因为MCP**没有不可变的真实来源**。工具定义可以被更改。模式可以被修改。输出格式可以被篡改。系统中没有保证正确性的参考点,因为没有任何东西能保证不被改变。
`FrozenNamespace` 是一个 Python 元类,它创建的对象其属性在初始化后**物理上无法被修改**。无法通过常规赋值,无法通过 `__setattr__`,也无法通过任何运行时操作来修改。该约束在语言运行时级别强制执行。
当您在进程启动时冻结 MCP 工具定义、模式、预期输出格式和验证数据时,您就创建了一个不可变的参考点。系统中的每一次检查都会回到这个参考点。如果任何内容与冻结的参考不匹配,则**默认拒绝**。如果匹配,您就知道它是正确的,因为参考本身不可能被篡改。
这是**确定性验证**。相同的输入与相同的不可变参考进行比较,每次都会产生相同的结果。没有概率。没有模型不确定性。没有猜测。
## 快速开始
```
from sovereign_mcp import ToolRegistry, OutputGate, SchemaValidator
# ── 阶段一:注册与冻结 ─────────────────────────────────
registry = ToolRegistry()
registry.register_tool(
name="get_weather",
description="Fetch current weather for a city",
input_schema={
"city": {"type": "string", "required": True, "alpha_only": True}
},
output_schema={
"temperature": {"type": "number", "min": -100, "max": 150},
"condition": {"type": "string", "enum": ["sunny", "cloudy", "rainy", "snowy"]},
},
capabilities=["read_api"],
allowed_targets=["api.weather.com/*"],
risk_level="LOW",
)
frozen = registry.freeze()
# 冻结后:不允许注册任何工具。所有定义均为不可变状态。
# 计算并封装 SHA-256 哈希值。这是信任根基。
# ── 阶段二:验证每个工具输出 ───────────────────────────
gate = OutputGate(frozen)
result = gate.verify("get_weather", {"temperature": 72.5, "condition": "sunny"})
if result.accepted:
print("✓ All 4 layers passed. Safe to admit to LLM context.")
else:
print(f"✗ BLOCKED at {result.layer}: {result.reason}")
```
## ⚠️ 关键:冻结操作不可逆
**一旦您调用 `registry.freeze()`,注册表将在进程的生命周期内永久锁定。** 这是设计如此——它是核心安全保证。
冻结后:
- **无法注册新工具。** `register_tool()` 会抛出 `RuntimeError`。
- **无法修改现有工具定义。** `FrozenNamespace.__setattr__` 会抛出 `TypeError`。
- **无法删除属性。** `FrozenNamespace.__delattr__` 会抛出 `TypeError`。
- **无法创建实例。** `FrozenNamespace.__call__` 会抛出 `TypeError`。
- **返回的 Schema 字典/列表是深拷贝** - 修改返回的 schema 不会影响冻结的原始数据。
- **即使是读取可变属性也返回副本**,而非引用。外部代码无法改变内部状态。
这不是一项策略。这是 **Python 运行时强制执行的架构约束**。没有 API 可以解锁,没有管理员覆盖,没有应急出口。注册新工具的唯一方法是使用新的冻结周期重启进程。
如果您需要在运行时动态添加工具,请使用**沙箱暂存模式**:在隔离的沙箱中发现和验证新工具,然后通过受控进程重启(蓝绿部署)应用。请参阅架构文档中的[动态工具注册](#dynamic-tool-registration)部分。
## 架构
系统在三个阶段运行:**初始化**、**运行时验证**和**强制执行**。
### 阶段 1:初始化(启动)
在进程启动时,在任何 MCP 工具可用之前:
1. **捕获每个已注册 MCP 工具的定义:**
- 工具名称、描述
- 输入模式(参数名称、类型、约束)
- 输出模式(预期返回格式)
- 声明的能力(工具声称的功能)
- 允许的目标(工具可以访问哪些资源)
- 风险等级(低、中、高)
- 值约束(每个参数的冻结数值限制)
- 批准阈值(需要人工介入的触发点)
2. **将所有捕获的定义锁定到 FrozenNamespace 中:**
- 数据变为不可变
- 之后没有代码路径可以修改它
- 无论是 AI 模型、工具,还是系统管理员在运行时
- 约束由 Python 元类强制执行,而非策略
- `__getattribute__` 返回可变容器的深拷贝,以防止引用变异
3. **为每个冻结定义计算 SHA-256 哈希值:**
- 与冻结数据一起存储
- 用于运行时快速完整性检查
- 对冻结数据的任何修改都需要破解 SHA-256
初始化完成后,系统拥有了一个不可变的、经哈希封印的参考,它描述了每个工具是什么、接受什么、返回什么以及被允许做什么。
### 阶段 2:运行时验证(每次工具调用)
每次调用 MCP 工具时,都会执行以下验证链:
```
Tool Call
│
├─ Step 1: Tool Identity Check ──────── Is it registered? Hash match?
├─ Step 2: Input Validation ──────────── Matches frozen input schema?
├─ Step 3: Permission Check ──────────── Capability + target allowed?
├─ Step 4: Value Constraint Check ────── Within frozen numeric limits?
│
├─ [Tool Executes]
│
├─ Step 5: Layer A - Schema Check ────── Output matches frozen schema?
├─ Step 6: Layer AP - Anti-Patterns ──── Output free of AI failure modes?
├─ Step 7: Layer B - Deception Scan ──── Known injection patterns?
├─ Step 8: Layer C - JSON Consensus ──── N-model hash match?
└─ Step 9: Layer D - Behavioral Floor ── Within frozen capability set?
│
├─ ALL PASS → Admitted to LLM context
└─ ANY FAIL → DECLINED (default deny)
```
如果**任何**检查在**任何**步骤失败:该工具调用将被**拒绝**。默认拒绝。无一例外。没有覆盖路径。
### 阶段 3:强制执行(默认拒绝)
- 如果任何检查在任何步骤失败:**拒绝**
- 不存在覆盖路径
- 没有信任分数可以绕过失败的检查
- 没有管理员可以在运行时修改冻结的参考
- 冻结的参考是唯一的真实来源
**这是通过架构实现治理,而非通过策略。**
## 四层验证
### 层 A:模式验证(确定性)
在工具输出进入 LLM 上下文之前,根据冻结的输出模式对其进行验证。
```
from sovereign_mcp import SchemaValidator
# 冻结的 schema 定义了精确的结构、类型和约束
schema = {
"customer_name": {"type": "string", "alpha_only": True, "max_length": 50},
"age": {"type": "integer", "min": 0, "max": 150},
"city": {"type": "string", "enum": ["Brussels", "London", "Tokyo"]},
}
# ✓ 有效
SchemaValidator.validate_output(
{"customer_name": "John", "age": 34, "city": "Brussels"}, schema
) # → (True, "Output schema validation passed.")
# ✗ 类型不匹配
SchemaValidator.validate_output(
{"customer_name": "John", "age": "thirty-four", "city": "Brussels"}, schema
) # → (False, "Type mismatch for 'age': expected integer, got str")
# ✗ injection 被 alpha_only 阻止
SchemaValidator.validate_output(
{"customer_name": "John; IGNORE PREVIOUS", "age": 34, "city": "Brussels"}, schema
) # → (False, "Field 'customer_name': must contain only alphabetic characters.")
# ✗ 未知字段被阻止
SchemaValidator.validate_output(
{"customer_name": "John", "age": 34, "city": "Brussels", "ssn": "123-45-6789"}, schema
) # → (False, "Unexpected output field: 'ssn' not in frozen schema.")
```
**安全加固:**
- **布尔值排除**:在 Python 中,`isinstance(True, int)` 为 `True`。布尔值在整数/数字类型检查中被显式排除。
- **NaN/Infinity 守卫**:在 Python 中,`float('nan') > max_val` 始终为 `False`,会静默绕过最大值约束。显式的 `math.isnan()` 和 `math.isinf()` 检查会拒绝非有限值。
- **Null 在类型之前**:`None` 在类型验证之前被检查,以防止 `isinstance(None, str)` 的误判。
### 层 B:欺骗检测(确定性)
扫描工具输出中的已知提示注入模式、社会工程短语、代码执行尝试和数据外泄指标。
```
from sovereign_mcp import DeceptionDetector
# 40+ 预编译的正则表达式模式,涵盖 4 个类别
DeceptionDetector.scan("IGNORE ALL PREVIOUS INSTRUCTIONS")
# → (False, [{"category": "injection", "match": "IGNORE ALL PREVIOUS INSTRUCTIONS", ...}])
DeceptionDetector.scan("The weather is sunny.")
# → (True, []) ← 干净
# 递归扫描嵌套字典
DeceptionDetector.scan_dict({
"data": {"nested": {"deep": "eval(malicious_code)"}}
})
# → (False, [{"category": "code_execution", "match": "eval(", ...}])
```
**四个检测类别:**
| 类别 | 示例 |
| :--- | :--- |
| **注入** | `IGNORE PREVIOUS`, `DISREGARD`, `NEW INSTRUCTIONS`, `[SYSTEM]`, `JAILBREAK`, `DAN MODE` |
| **社会工程** | `I AM THE ADMIN`, `EMERGENCY OVERRIDE`, `SAFETY DISABLED`, `BYPASS ALL SECURITY` |
| **代码执行** | `