Skip to main content
roots/list is a server → client request. GetMCP sends it down the outbound queue when a tool — or your own custom code — wants to know what local roots (directories, repositories) the user has exposed to the AI client. The client responds with an array of { uri, name? } entries. The server caches that response per session and invalidates the cache when the client sends notifications/roots/list_changed.

When This Fires

GetMCP doesn’t fire roots/list automatically — there’s no “browse the user’s project” tool in the box. The handler is exposed so your own code (a custom tool, an action hook) can ask the question:
$roots = GetMCP\Protocol\RootsHandler::request_for_session( $session_id );
foreach ( $roots as $root ) {
    error_log( "Root: {$root['uri']} ({$root['name']})" );
}
The first call queues a roots/list request; the result lands on the next inbound HTTP response from the client. Subsequent calls within the same session return the cached value until the client invalidates it.

Wire-level Shape

The server emits:
{
  "jsonrpc": "2.0",
  "id":      "srv-roots-1",
  "method":  "roots/list",
  "params":  {}
}
The client responds (on its next outbound request):
{
  "jsonrpc": "2.0",
  "id":      "srv-roots-1",
  "result": {
    "roots": [
      { "uri": "file:///Users/nitin/projects/getmcp", "name": "getmcp" },
      { "uri": "file:///Users/nitin/projects/blog",   "name": "blog" }
    ]
  }
}

Cache Invalidation

When the user changes their roots in the client UI, the client sends:
{
  "jsonrpc": "2.0",
  "method":  "notifications/roots/list_changed",
  "params":  {}
}
GetMCP drops its cached copy and the next request_for_session() call will issue a fresh roots/list.

Capability Negotiation

roots/list only works when the client declared the roots capability during initialize:
"capabilities": {
  "roots": { "listChanged": true }
}
If the capability is absent, RootsHandler::request_for_session() returns an empty array immediately without queuing a request.
Roots are advisory. The client may decline to share them, return an empty list, or list paths that don’t exist. Always handle the empty case gracefully.