Background
Introduced in PR #2454 (MCP Roots protocol, #2445).
validate_roots() in crates/zeph-mcp/src/manager.rs filters non-file:// URIs but does not canonicalize paths, so file:///etc/../secret passes through unchanged.
Fix
Apply std::fs::canonicalize() to the resolved path after URI parsing to reject traversal payloads and resolve symlinks before passing roots to MCP servers.
Low risk: roots are operator-configured (not from untrusted input), but defense-in-depth applies.
Priority
P3 — low urgency, no known exploit vector.