What it does
The `roots/list` method is a server-initiated request sent from the server to the client over an established MCP session. When a server needs to discover which filesystem directories or URIs the client considers in-scope, it sends this request with no required parameters. The client responds with a `roots` array, where each entry contains a `uri` (a file:// URI or other scheme URI) and an optional human-readable `name`. Servers use this information to constrain resource enumeration, validate tool argument paths, and avoid exposing or operating on content outside the client's declared workspace.
Before sending `roots/list`, the server must verify that the client advertised the `roots` capability during the `initialize` handshake. Specifically, the client's `capabilities.roots` object must be present; if it is absent, the server must not send this request and should fall back to treating the workspace as unbounded or prompt-only. Sending the request without the capability declared by the client is a protocol violation and the client may respond with a `-32601` method-not-found error.
The response carries no cursor or pagination token — the full roots list is returned in a single reply. Because a user can add or remove workspace folders at any time, the server should re-issue `roots/list` whenever it receives a `notifications/roots/list_changed` notification rather than caching the result indefinitely. Failing to refresh after that notification can cause the server to operate on stale scope data, leading to resource leaks or permission bypasses.
When to use
After initialize, and after notifications/roots/list_changed.
When NOT to use
On every request — cache and listen for changes.
Notes
Capability gating is mandatory
The server must check `clientCapabilities.roots` before sending this request. If the client did not advertise the `roots` capability during `initialize`, calling `roots/list` is a spec violation. Well-behaved clients will return a -32601 (Method not found) error, but some may silently drop the request, causing the server to hang waiting for a reply.
No pagination — one-shot response
Unlike `resources/list` or `prompts/list`, `roots/list` has no `cursor` parameter and returns the complete set of roots in one response. Do not attempt to paginate by sending repeated calls with synthetic cursors; the spec does not define that behavior and clients will not honor it.
Refresh on roots/list_changed notification
Clients emit `notifications/roots/list_changed` when the workspace scope changes (e.g., a folder is added or removed in the editor). Servers must subscribe to this notification and re-issue `roots/list` upon receipt. Caching the roots list across this notification boundary will cause the server to act on stale scope and may expose or restrict resources incorrectly.
Race condition at session startup
Avoid issuing `roots/list` before the `initialize` / `initialized` handshake completes. Some SDK implementations process the request queue before the handshake is fully confirmed, leading to a race where the client's capability map is not yet populated. Always send `roots/list` after receiving the `initialized` notification, not immediately after sending your own `initialize` request.
URI scheme handling
The `uri` field in each Root entry is not guaranteed to be a `file://` URI — clients may return custom scheme URIs (e.g., `vscode-vfs://` for virtual filesystems). Servers should parse the scheme before performing filesystem operations and gracefully skip or flag roots whose schemes they do not support, rather than assuming all roots map to local disk paths.
Response fields
| Name | Type | Purpose |
|---|---|---|
| roots | Root[] | Current roots. |
Examples
Ask for roots
{ "method": "roots/list" }