Roots
MCP roots allow clients to inform servers about the relevant locations in the filesystem or other hierarchical data sources. Roots are a client-provided feature—the client declares its root URIs during initialization, and the server can request them to understand the working context.
Overview
Roots provide a mechanism for the client to tell the server which directories, projects, or repositories are relevant to the current session. A server might use roots to:
- Scope file searches to the user's project directories
- Understand which repositories are being worked on
- Focus operations on relevant filesystem locations
Each root is represented by a Root with a URI and an optional human-readable name.
Declaring roots capability on the client
Clients advertise their support for roots in the capabilities sent during initialization. The roots capability is created automatically when a roots handler is provided. Configure the handler through RootsHandler:
var options = new McpClientOptions
{
Handlers = new McpClientHandlers
{
RootsHandler = (request, cancellationToken) =>
{
return ValueTask.FromResult(new ListRootsResult
{
Roots =
[
new Root
{
Uri = "file:///home/user/projects/my-app",
Name = "My Application"
},
new Root
{
Uri = "file:///home/user/projects/shared-lib",
Name = "Shared Library"
}
]
});
}
}
};
await using var client = await McpClient.CreateAsync(transport, options);
Requesting roots from the server
Servers can request the client's root list using RequestRootsAsync. This is a server-to-client request, so it requires stateful mode or stdio — it is not available in stateless mode.
[McpServerTool, Description("Lists the user's project roots")]
public static async Task<string> ListProjectRoots(McpServer server, CancellationToken cancellationToken)
{
var result = await server.RequestRootsAsync(new ListRootsRequestParams(), cancellationToken);
var summary = new StringBuilder();
foreach (var root in result.Roots)
{
summary.AppendLine($"- {root.Name ?? root.Uri}: {root.Uri}");
}
return summary.ToString();
}
Roots change notifications
When the set of roots changes (for example, the user opens a new project), the client notifies the server so it can update its understanding of the working context.
Sending change notifications from the client
Roots change notifications are automatically sent when the client's roots handler is updated. However, clients can also send the notification explicitly:
await mcpClient.SendNotificationAsync(
NotificationMethods.RootsListChangedNotification,
new RootsListChangedNotificationParams());
Handling change notifications on the server
Servers can register a handler to respond when the client's roots change:
server.RegisterNotificationHandler(
NotificationMethods.RootsListChangedNotification,
async (notification, cancellationToken) =>
{
// Re-request the roots list to get the updated set
var result = await server.RequestRootsAsync(new ListRootsRequestParams(), cancellationToken);
Console.WriteLine($"Roots updated. {result.Roots.Count} roots available.");
});
Multi Round-Trip Requests (MRTR)
MRTR is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision 2026-07-28. In that revision, the server-to-client roots/list request method is removed; the recommended way to ask the client for its roots from a server handler is to throw InputRequiredException and let the SDK emit an InputRequiredResult on the wire.
Important
RequestRootsAsync throws InvalidOperationException("Roots are not supported in stateless mode.") whenever the server is running stateless — including Streamable HTTP requests served under 2026-07-28 with Stateless = true. Stdio servers and initialize-handshake stateful Streamable HTTP sessions continue to work via the initialize-era server-to-client roots/list request flow; an HTTP server set to Stateless = false refuses 2026-07-28 so dual-path clients can fall back before using that flow. For code that needs to run on stateless servers — including 2026-07-28 Streamable HTTP — throw InputRequiredException from your handler instead. It works under both protocols and both session modes.
For example:
[McpServerTool, Description("Tool that requests roots via MRTR")]
public static string ListRootsWithMrtr(
McpServer server,
RequestContext<CallToolRequestParams> context)
{
// On retry, process the client's roots response
if (context.Params!.InputResponses?.TryGetValue("get_roots", out var response) is true)
{
var roots = response.Deserialize(InputResponse.ListRootsResultJsonTypeInfo)?.Roots ?? [];
return $"Found {roots.Count} roots: {string.Join(", ", roots.Select(r => r.Uri))}";
}
if (!server.IsMrtrSupported)
{
return "This tool requires MRTR support (2026-07-28, or a stateful current-protocol session).";
}
// First call — request the client's root list
throw new InputRequiredException(
inputRequests: new Dictionary<string, InputRequest>
{
["get_roots"] = InputRequest.ForRootsList(new ListRootsRequestParams())
},
requestState: "awaiting-roots");
}
Tip
See Multi Round-Trip Requests (MRTR) for the full protocol details, including load shedding, multiple round trips, and the compatibility matrix.