skip to content

Roots

Roots hand a server advisory file:// directories, and 2026-07-28 deprecates them in favour of tool parameters. Interviewers ask because they look like access control and never were.

part ofAPI stylesoverview, primer and where to startread it →
on this pageshow

questions

5

Why are MCP roots advisory rather than an enforced sandbox for a server?

level: middleimportance: must knowfreq 58%

answer

  1. a request, not a restriction
  2. who actually mediates the syscall
  3. the same status as tool annotations
  4. enforcement lives outside the protocol
  5. one reason 2026-07-28 deprecated it

basics

~20 s

Roots are data the client sends, not a permission the protocol applies. MCP has no mechanism to intercept a server's file access, so a server keeps every byte its operating-system user can reach whether it honours the roots or ignores them.

solid answer

~50 s

Roots look like access control and never were. The client sends a list of `file://` locations; the server is expected to *prefer* them, and that is the whole guarantee. MCP is a JSON-RPC message protocol — it sits above the server process and has no hook into that process's `open()` calls, so nothing in the protocol can stop a server from reading `/etc/passwd` after being handed a root of `file:///home/alice/project`. A malicious or simply buggy server is not violating a protocol rule when it ranges outside; there is no rule to violate. Real confinement therefore has to come from the layer that actually owns the filesystem: run the server as a restricted user, in a container, or in a filesystem namespace. This is also part of why revision 2026-07-28 deprecates roots — an advisory field that reads as a boundary is worse than no field at all.

go deeper

for a junior

Know the headline: roots tell a server where to work and do not stop it going elsewhere. Say plainly that they are advisory.

for a middle

Explain why the guarantee is absent — the server is a separate process and MCP has no hook into its filesystem calls — and place roots alongside tool annotations as a declaration rather than a control.

for a senior

Demonstrate you would reach for process isolation, containers or restricted OS users when confinement is a real requirement, and that you would flag any design leaning on roots for security in review.

for a principal

Own the argument that a field which reads like a boundary but enforces nothing is a liability, and connect that to why 2026-07-28 pushed paths back into explicit, user-visible tool arguments.

## The misconception this question exists to catch Ask an engineer what roots do and a common answer is "they limit the server to those folders". They do not, and the gap between how the feature reads and what it does is exactly why interviewers ask about it. ## What MCP is, structurally MCP is a JSON-RPC protocol carried over stdio or Streamable HTTP. Its vocabulary is requests, results, notifications and errors. When a client hands a server a list of roots, what physically happens is that a JSON array of `file://` strings crosses a pipe or an HTTP body. The server receives some strings. The server is a separate program. On stdio it is a subprocess the host launched; on HTTP it is a service somewhere else entirely. When that program opens a file it makes a system call to its own kernel. There is no point in that path where the MCP client sits, and no place where a protocol message could veto it. A client cannot revoke a file descriptor a server already holds and cannot inspect what the server reads. So the only thing roots can be is a request: *please work here*. ## Advisory in the specification's own terms The specification describes servers as expected to respect roots — a should, aimed at cooperative implementations. That is a useful contract between honest parties: a search server told the project directory indexes the project directory, which is faster and less surprising than scanning a home folder. It is not a contract that survives contact with a server you do not trust, because it is enforced by the server's own good behaviour. A useful way to phrase it in an interview: roots are a *hint from the client to the server*, in the same family as `ToolAnnotations` being hints from the server to the client. Both are declarations by one party about intent. Neither is checked by the protocol, and MCP is explicit that annotations must be treated as untrusted unless the server itself is trusted. Roots are the mirror image of that asymmetry. ## Where confinement actually lives If the requirement is "this server must not read anything outside the project", the answer is never roots. It is one of: - Process isolation — run the stdio server as a dedicated OS user with read access only to the intended tree, or in a container with only that tree mounted. - Filesystem namespacing or sandbox profiles — the kernel refuses paths outside the mount, regardless of what the server code attempts. - Not running the server locally at all — a remote server behind OAuth 2.1 authorization gets exactly the data the token's audience and scopes permit, and the boundary is the resource server's own checks. All three share a property roots lack: they are enforced by something other than the code being constrained. ## Consent sits with the host, not with roots MCP's security model puts enforcement in the host application: User Consent and Control, Data Privacy, Tool Safety. The host decides which servers run, which tool calls proceed, and what the user is shown before data leaves. Roots never carried any of that weight. Sending a root is not asking the user's permission for anything and does not record a decision; it just tells the server where the interesting files are. ## Why this fed into the deprecation Revision 2026-07-28 deprecates roots (SEP-2577) and points implementers at explicit tool parameters, resource URIs, or server configuration. Part of the argument is exactly the confusion described here. A path passed as a tool argument is visible in the tool call the user approves; it is obviously data, and nobody mistakes an argument for a sandbox. A separate protocol channel that hands a server "the directories it should stay in" invites the wrong mental model. Deprecated does not mean removed — the feature stands for at least twelve months, so the earliest removal is a revision on or after 2027-07-28 — but new servers should not adopt it. ## Answering well Say the guarantee out loud: none. Then explain structurally why — the protocol cannot mediate a separate process's filesystem calls — and name where real isolation comes from. Candidates who say "roots restrict the server" have revealed they have never had to reason about an untrusted server.

  • If roots guarantee nothing, why would a well-behaved server bother honouring them?
    Because they make the server better, not safer. A code-search server told the project directory indexes far less and returns more relevant hits; a build server resolves relative paths the way the user expects. Honouring roots is a usability and performance contract between cooperating parties. It just cannot be leaned on when the server is the thing you are worried about.
  • How would you actually confine a locally-run stdio MCP server to one directory?
    Outside MCP entirely. Launch it as a dedicated OS user whose read permissions cover only that tree, or run it in a container with just that directory bind-mounted, or apply a sandbox profile or mount namespace. The kernel then refuses paths outside the tree regardless of what the server code attempts, which is the property roots can never provide.
  • Is a server that reads outside its given roots violating the MCP specification?
    Not in any enforceable sense. Servers are expected to respect roots, but it is a cooperative expectation with no protocol-level check, no error code and no client-side detection. Treat it as a quality-of-implementation issue rather than a compliance one, and never build a security argument on the assumption that a server complied.
  • How do roots compare to ToolAnnotations in terms of trust?
    They are mirror images. Roots are a client declaration of intent that the server may ignore; annotations such as readOnlyHint and destructiveHint are server declarations of intent that MCP explicitly tells clients to treat as untrusted unless the server itself is trusted. In both directions the protocol carries a claim, not a control, and the receiving side decides how much weight to give it.

saying these in an interview costs you the question

  • Says roots stop the server reading other directories
  • Calls roots a sandbox or a permission grant
  • Thinks the client can block the server's file reads
  • Believes reading outside roots triggers a protocol error
  • Proposes roots as the answer to an untrusted server

context

open as a page

In MCP, what are roots, and what may a Root's uri point to?

level: juniorimportance: should knowfreq 45%

basics

~20 s

Roots are file:// URIs a client gives a server to say which directories or files it is meant to work within. Each Root carries that uri plus an optional human-readable name. MCP revision 2026-07-28 deprecates the whole feature.

open as a page

How does an MCP 2026-07-28 server ask a client for its list of roots?

level: middleimportance: should knowfreq 40%

basics

~20 s

By answering the client's in-flight tools/call, prompts/get or resources/read with an interim result whose resultType is input_required, carrying a roots/list request in its inputRequests map. The client then re-sends the original request with the roots supplied.

open as a page

MCP deprecated roots in 2026-07-28 — what replaces them, and by when?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Revision 2026-07-28 deprecates roots under SEP-2577 and directs implementers to pass directories and files as tool parameters, resource URIs, or server configuration. Deprecated features stay at least twelve months, so the earliest removal is a revision released on or after 2027-07-28.

open as a page

In MCP 2026-07-28, how does a server learn that the client's roots changed?

level: seniorimportance: should knowfreq 34%

basics

~20 s

It does not get told. Revision 2026-07-28 removed notifications/roots/list_changed and the roots.listChanged sub-capability, so a roots list is only ever valid for the request that fetched it; a server needing current roots asks again on the next request.

open as a page