Quick answer: A root is a URI the client exposes to a server through roots/list, usually pointing at a project directory such as file:///Users/me/project. The server uses those roots to scope its file operations instead of reaching anywhere on disk. Roots define the boundary the client intends, but they are not an enforced sandbox; the server is expected to respect them, and real isolation still comes from OS-level permissions.
What a root is
Roots flow from the client to the server, the opposite direction from a tool call. The client configures one or more roots, each with a URI and an optional name, and the server requests the list when it needs to know where it is allowed to work. A file-oriented server handling a codebase does not have to guess the project path or assume the whole home directory is fair game. It reads the roots and treats them as the set of locations the user has pointed it at.
How the scope works
Once the server has the roots, it should resolve every file path it touches against them and reject or refuse operations that resolve outside. The set is not fixed for the life of the connection: the client can send a roots-list-changed notification, at which point the server re-reads the list and adjusts its working set. A root URI is not guaranteed to be a file path; other schemes are allowed, and the list can be empty, so the server has to handle the case where no root is offered rather than falling back to a default location.
Where the boundary sits
| Control | Who sets it | Enforced |
|---|---|---|
| Roots | Client | Convention the server honors |
| OS permissions | Operating system | Enforced at the process level |
| Sandbox | Host/runtime | Hard isolation |
| Symlink handling | Server policy | Decided per path |
Resolving a path before you trust it
A string that looks inside a root is not enough. Relative segments such as .., redundant separators, and a trailing path that starts inside the root can all resolve somewhere else once normalized. The safe order is to normalize the requested path, resolve any symlink to its real target, and then compare that final absolute path against the root prefix before opening the file. Doing the comparison on the raw input string lets a crafted path pass the check while landing outside the scope. When a path does not resolve cleanly or points outside, the server should refuse it with a clear error rather than quietly following it.
Honest limits
Roots tell the server the intended boundary; they do not remove the underlying filesystem access of the server process. If that process holds broader permissions, nothing in the protocol physically blocks a path outside the roots, so a server that ignores the convention can still reach it, and hard isolation has to come from a sandbox or OS permissions. A root also does not prove the path exists or that every file inside it is readable. Symlinks are the common leak: a link inside a root can point outside it, and the server has to decide whether to follow it and re-check the resolved path. Before trusting the scope, run a real request against a path inside and a path outside the root; Powerduck keeps both the request and the response visible so you can confirm the server refuses the out-of-scope path instead of assuming the boundary held.
Frequently asked questions
Do roots restrict the server at the OS level? No. They are a client-declared boundary the server is expected to honor, not a kernel-enforced sandbox.
Can roots change during a session? Yes. The client can notify the server that the list changed, and the server should re-read it.
What if the client sends no roots? The server should handle an empty list rather than assuming a default directory.
Do symlinks stay inside the scope? Not always. A link inside a root can resolve outside, so the server should check the resolved path.
The bottom line
Roots give a filesystem server a clear, client-declared scope to work within and a signal to re-read when that scope changes. Treat them as a boundary the server respects, backed by real sandboxing and careful symlink checks rather than as isolation on their own. For more on operating these servers, see the MCP server guide hub.
Top comments (0)