How does an MCP 2026-07-28 server ask a client for its list of roots?
answer
- servers can no longer originate requests
- the ask rides inside a result
- opaque blob you echo untouched
- new id, same method
- silence is how you decline
basics
~20 sBy 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.
solid answer
~40 sIn revision 2026-07-28 servers cannot originate JSON-RPC requests at all, so a server obtains roots by piggybacking on a request it is already answering. It returns an `InputRequiredResult` — `resultType: "input_required"` — whose `inputRequests` map holds a `roots/list` request (a `ListRootsRequest`) under a server-chosen key, alongside an opaque `requestState` blob. The client resolves it locally, then retries the *original* `tools/call`, `prompts/get` or `resources/read` under a **new** JSON-RPC id, supplying the `ListRootsResult` at the same key and echoing `requestState` verbatim without inspecting it. Only those three methods may return an input-required result. Declining is expressed by simply not retrying — there is no refusal message to send. Note this is deprecated territory: 2026-07-28 marks roots deprecated in favour of passing paths as tool parameters.
code
json · 11 lines{
"jsonrpc": "2.0",
"id": 7,
"result": {
"resultType": "input_required",
"inputRequests": {
"need-roots": { "method": "roots/list" }
},
"requestState": "c3RhdGUtYmxvYg"
}
}go deeper
Recall the direction of travel: servers cannot send requests in MCP 2026-07-28, so a roots request comes back inside a result and the client answers it by retrying.
Name the fields — resultType input_required, inputRequests keyed by the server, roots/list, requestState — and explain the retry rules: same method, new id, answers at the same keys.
Show you know the constraints around it: only three methods may return an interim result, declining is silence, and the client must re-declare the roots capability on every request because nothing is inferred from the connection.
Weigh whether to implement this at all: roots are deprecated as of 2026-07-28, so the durable design takes the path as an explicit tool parameter and avoids a round trip that will need removing.
## The problem this shape solves Roots are a client-side fact, so the server has to pull them from the client. Up to revision 2025-11-25 that was easy: a server could send a JSON-RPC request of its own, `roots/list`, at any point, and the client answered. Revision 2026-07-28 removed server-initiated requests entirely — there is no `ServerRequest` union any more. Every JSON-RPC request now flows client → server. So the question becomes: how does a server ask for anything? ## The answer: ask inside a result The replacement is the multi-round-trip pattern. When the server is part-way through work and needs something from the client, it does not error and does not block; it answers the request it is currently handling with an *interim* result. That result carries `resultType: "input_required"` instead of `"complete"`, and inside it an `inputRequests` map. Each entry is keyed by a string the server chooses, and its value is a request the client should fulfil. For roots that request is `roots/list`, typed `ListRootsRequest`, with no parameters to speak of. Alongside sits `requestState`, an opaque string the server uses to pick its work back up. The client must treat it as a blob: do not parse it, do not rewrite it, echo it back exactly. ## The client's half The client resolves the roots list from its own state — the folders the user has open — and then **re-issues the original request**, not a new method. If the interim result came back from a `tools/call`, the retry is that same `tools/call` with the same tool name and arguments. Two details matter: - The retry carries a **new JSON-RPC id**. The original id is spent; the interim result completed it as far as JSON-RPC is concerned. - The answers go back at the **same keys** the server used in `inputRequests`, as `inputResponses`, and `requestState` is echoed unchanged. A `ListRootsRequest` is answered with a `ListRootsResult`, whose `roots` array holds the `file://` entries with their optional names. A server may ask for several things in one interim result — the `InputRequest` union is `CreateMessageRequest | ListRootsRequest | ElicitRequest` — so a roots request can travel next to an elicitation in the same map, and the client fulfils each under its key. ## Which methods can do this Only `tools/call`, `prompts/get` and `resources/read` may return an input-required result. A `tools/list` cannot ask for roots mid-flight, and neither can `server/discover`. That is a deliberate narrowing: the three methods that may need user or client input are the three that execute something on the user's behalf. ## Declining There is no decline message. If the user does not want to disclose their open folders, or the client does not support roots at all, the client simply never retries. The server sees no further request and, being stateless, has nothing to clean up beyond whatever its own `requestState` was holding. This is a genuinely different mental model from the old server-initiated request, which had an error response to send back. ## The capability side A client that can answer roots requests advertises `roots` inside `io.modelcontextprotocol/clientCapabilities` in the `_meta` of *every* request — the protocol is stateless and servers MUST NOT infer capabilities from earlier requests on the same connection. In 2026-07-28 that capability is an empty object `{}`; the `listChanged` sub-capability it used to carry is gone. A server that requires roots and sees the capability absent should not ask; there is a dedicated error for a missing required client capability rather than an unanswerable interim result. ## The deprecation caveat All of the above is how roots work today, and today they are deprecated. Revision 2026-07-28 marks roots deprecated under SEP-2577, with the twelve-month policy meaning the earliest removal is a revision released on or after 2027-07-28. The migration is to stop asking: take the directory as a documented tool parameter, expose it as a resource URI, or read it from server configuration. A new server that implements the interim-result dance for roots is building on a feature with a known end date. ## Answering well Name the pieces precisely — `resultType: "input_required"`, `inputRequests`, `roots/list`, `requestState`, retry with a new id — and get the direction right: the server never sends a request, it answers one with a question.
- Why must the retry use a new JSON-RPC id rather than reusing the original?Because the original id was already consumed: the interim result was a real JSON-RPC response to it. Reusing the id would be a second response to a settled request, which JSON-RPC does not allow and which breaks any client-side correlation table. Continuity across the round trip is carried by `requestState`, not by the id — that separation is the whole point of the pattern.
- What happens if the client never retries after an input-required result?Nothing further, and that is the sanctioned way to decline. There is no refusal message in the protocol. The server treats the exchange as over; because MCP 2026-07-28 is stateless it holds no session to unwind, only whatever it encoded into its own `requestState`, which it can let expire. The user simply never disclosed their roots.
- Can a server ask for roots in response to tools/list?No. Only `tools/call`, `prompts/get` and `resources/read` may return an interim result with `resultType: "input_required"`. Listing methods must answer completely. If a server's tool set depended on the client's roots it would be pushing per-client variation into a listing, which 2026-07-28 forbids anyway — the tool set MUST NOT vary per connection, though it MAY vary by the authorization presented.
- May a server put a roots request and an elicitation in the same interim result?Yes. `inputRequests` is a map, keyed by strings the server chooses, and the `InputRequest` union covers `ListRootsRequest`, `ElicitRequest` and `CreateMessageRequest`. A server can ask for the roots and prompt the user for a value in one round trip; the client fulfils each entry and returns the answers at the matching keys. That saves a round trip compared with asking serially.
saying these in an interview costs you the question
- Says the server sends a roots/list request to the client
- Retries the original request with the same JSON-RPC id
- Parses or rewrites the opaque requestState blob
- Expects a decline error message when the user refuses
- Thinks any method may return an input-required result