v3/docs/adr/ADR-352-mcp-tool-permission-attestation.md
The 2026-06-26 Dream Cycle SOTA sweep surfaced two converging evidence streams that identify MCP tool-call boundaries as the primary unguarded attack surface in LLM agent systems:
ShareLock (arXiv 2026, Grade A) — Achieves >90% poisoning ASR on MCP-connected agents via Shamir's threshold scheme distributed across multiple benign-looking tool servers. The attack exploits the absence of tool-level permission declarations: any server can inject arbitrary behaviour if the agent does not enforce a privilege contract at dispatch time.
ToolPrivBench (arXiv 2026, Grade A) — 64.9% of Qwen3-8B tool calls escalate to higher-privilege tools than the task requires (OPUR metric). Post-training with privilege-aware objectives reduces OPUR to 27.02%. The benchmark formalises 544 scenarios across 8 domains and 5 risk patterns.
ControlPlane paper (arXiv 2026, Grade A) — Fewer than 1% of coding agents declare explicit permission boundaries, making over-privilege the default posture.
Ruflo's current security module (@claude-flow/security) provides SafeExecutor (command injection), InputValidator (Zod), and PathValidator (traversal). None operate at the MCP tool registration or dispatch layer. Tool calls are forwarded without a declared min-privilege contract.
Introduce a MCP Tool Permission Attestation layer with two integration points:
Every MCP tool registration (server-side and client-side stub) must carry a permissions manifest:
interface ToolPermissionContract {
toolName: string;
minPrivilege: {
filesystem?: 'none' | 'read' | 'write' | 'execute';
network?: 'none' | 'outbound' | 'inbound' | 'full';
process?: 'none' | 'spawn' | 'kill';
memory?: 'none' | 'read' | 'write';
agentScope?: 'none' | 'self' | 'team' | 'global';
};
declaredAt: string; // ISO date of contract signature
trustLevel: 'official' | 'community' | 'unverified';
}
Tools without a permissions field are assigned trustLevel: 'unverified' and routed through a stricter sandbox.
SafeExecutor is extended with a McpPermissionGuard that runs before every tool dispatch:
Request → McpPermissionGuard → {
if call.escalatesAbove(tool.minPrivilege) → REJECT (log + alert)
if tool.trustLevel === 'unverified' && call.privilege > 'read' → BLOCK
else → forward to SafeExecutor → execute
}
Rejection emits a structured event to the security audit log; repeated escalation triggers a circuit-breaker (ADR-097 pattern).
| Metric | Baseline | Target |
|---|---|---|
| OPUR (over-privilege usage rate) | ~64.9% (ToolPrivBench Qwen3-8B baseline) | ≤ 30% |
| ShareLock-style poisoning ASR | >90% (no defence) | < 10% |
| Tools with declared contracts | 0% | 100% of official tools; ≥ 80% of community tools |
Positive:
Negative / Risk:
permissions — requires migration period