What is the principle of least privilege as applied to MCP server permissions and roots?
Quick Answer
Least privilege means an MCP server (and the credentials/access it holds) should be scoped to exactly what its declared tools/resources genuinely need, and no more — a filesystem server should only be able to reach the directories declared as roots, not the whole disk; a database server's credential should have permissions matching only the operations its tools actually perform (e.g., read-only if none of its tools write); an API integration server should request the narrowest OAuth scopes that satisfy its functionality, not a broad admin scope for convenience. Applying this consistently limits the blast radius of every other risk covered in this topic — a compromised server, a successful prompt injection, or a confused-deputy scenario can only do as much damage as the server's actual granted privileges allow, regardless of what the attack itself managed to trick the model or server into attempting.
Detailed Answer
Least privilege is the one mitigation that shows up as a backstop against nearly every other risk in this topic. It doesn't prevent an attack from being attempted, but it caps how much damage a successful one can do.
Applying it at each layer
Filesystem access (roots + real sandboxing):
// Declared roots limit intended scope
{"roots": [{"uri": "file:///home/user/projects/my-app"}]}
Remember roots are a hint, covered earlier. Real enforcement needs the server process itself to be sandboxed — a restricted OS user, a container, a chroot — so it can't read outside that boundary even if it tried, not just that it's told not to.
Database credentials:
-- Instead of a superuser/admin credential "for convenience"
CREATE USER mcp_reporting_server WITH PASSWORD '...';
GRANT SELECT ON orders, customers TO mcp_reporting_server;
-- No INSERT/UPDATE/DELETE/DDL granted at all, if the server's tools never write
OAuth scopes for an upstream API:
Instead of requesting: admin, orders:write, orders:read, users:write, users:read
Request only: orders:read
If a server's tools only ever read order data, requesting write or admin scopes "in case a future tool needs it" is exactly the kind of unnecessary privilege expansion least-privilege design avoids.
Why this compounds with every other mitigation in this topic
- A tool-poisoning attack that tricks the model into trying something malicious can only succeed at whatever the server's actual privileges allow. A read-only database credential makes a poisoned "delete all records" instruction a no-op at the database level, regardless of what the model was fooled into attempting.
- A confused-deputy scenario is far less damaging if the server's shared credential is scoped to one narrow read-only operation rather than broad admin access.
- Prompt injection that successfully gets a model to attempt an unauthorized action still fails if the underlying tool or credential genuinely can't perform it.
The practical discipline
When designing a server, write down exactly which operations each tool performs. Then grant credentials, scopes, and roots matching only that list. Resist the urge to provision broader access "to save time later." Expanding privilege when a genuinely new tool needs it is a small, deliberate step. Walking back over-broad privilege that's already been granted and is already in production use is a much harder retrofit.