skip to content

In MCP, what do resources/list and resources/read return, and how is a resource identified?

level: juniorimportance: must knowfreq 70%

answer

  1. one identifier per piece of data
  2. list gives descriptors, read gives content
  3. the URI is the key
  4. contents is an array, not one blob
  5. resultType required on every result

basics

~20 s

A resource is identified by its URI. resources/list returns descriptors — uri, name, optional description and mimeType — while resources/read takes one uri and returns a contents array carrying the data itself. Both results carry the required resultType field in MCP 2026-07-28.

solid answer

~40 s

Resources are the application-controlled data primitive: the server publishes addressable data and the host decides what to pull into the model's context. `resources/list` returns a `resources` array of descriptors — each has a `uri` (the identifier), a `name`, and usually a `description` and `mimeType` — plus an optional `nextCursor` for pagination. The client then calls `resources/read` with a single `uri` param, and the server answers with a `contents` array whose entries carry the data, each repeating its own `uri` and `mimeType`. URIs follow RFC 3986, so `file://`, `git://`, `https://` and custom schemes are all legal; the scheme is the server's business, not the client's. Under revision 2026-07-28 every result carries a required `resultType`, `"complete"` for an ordinary answer, and `resources/list` is a cacheable result, so it also carries the required `ttlMs` and `cacheScope`.

code

json · 12 lines
json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "resources/read",
  "params": {
    "uri": "file:///srv/app/README.md",
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

go deeper

for a junior

Be able to say plainly that a resource is identified by a URI, that resources/list gives you descriptors and resources/read gives you the data, and that a read result is a contents array.

for a middle

Explain the descriptor fields (uri, name, description, mimeType), cursor pagination, and why the result envelope in revision 2026-07-28 requires resultType plus ttlMs and cacheScope on a listing.

for a senior

Show judgment about URI stability, caching scope under multi-tenant credentials, and the rule that the resource set must not vary per connection though it may vary by presented authorization.

for a principal

Own the addressing scheme: which scheme space a server publishes, how stable URIs stay across deployments, and how catalogue size, pagination and cache TTL interact with the hosts that will consume the server.

## What a resource is MCP defines three server primitives, and resources are the one meant for **data the application pulls in**, as opposed to tools, which the model decides to invoke. A resource is any addressable blob of content a server is willing to hand over: a file on disk, a database row rendered as JSON, a wiki page, a build log, a git object. The defining property is that it is named by a **URI** and read by that URI alone — a read takes no other arguments. Because resources are application-controlled, the spec expects the host to decide what actually reaches the model: a file picker, an @-mention, an attachment tray. Nothing in the protocol forces a host to expose every resource to the model automatically. ## resources/list `resources/list` returns the concrete, directly-readable resources a server knows about. The result carries a `resources` array; each entry is a descriptor, not the content: - `uri` — the identifier used later in `resources/read`. - `name` — a short programmatic name. - `description` — optional prose, useful to a human picker and as a hint to a model. - `mimeType` — optional media type, telling the client what it will get back. Large catalogues paginate: the result may include a `nextCursor`, and the client passes it back as `cursor` on the next call. Listing is a snapshot of what exists, not the content. Under revision 2026-07-28 the listing result is a cacheable result, so it carries the required `ttlMs` (how long the client may reuse it) and `cacheScope` (`"public"` or `"private"`). A server whose resource set depends on the caller's authorization must use `"private"` so a shared client never serves one user's catalogue to another. The same revision also states that the resource set **MUST NOT vary per connection** — it may vary by the authorization presented, but not because of what happened earlier on the same pipe. ## resources/read `resources/read` takes exactly one param that matters, `uri`, and answers with a `contents` array. The array is plural on purpose: one URI may legitimately expand to several pieces of content — several files behind a single logical resource, or a document split into parts. Every entry repeats its own `uri` and may carry its own `mimeType`, so a client that fans the entries out into context still knows what each one is. A read is expected to be a side-effect-free retrieval. If an operation needs parameters beyond a URI, or changes something, it does not belong behind `resources/read`. ## URIs and schemes URIs follow RFC 3986: a scheme, then whatever the scheme's authority and path rules allow. `file://` for local files, `git://` for repository objects, `https://` for fetched documents, and custom schemes for anything a server invents. The client treats the URI as an opaque key — it does not parse it to decide semantics — which is why servers should keep URIs stable across calls. Unstable URIs break caching, break user bookmarks in the host UI, and break anything that stores a reference for later. ## The result envelope in 2026-07-28 Two envelope details are new enough to be worth stating explicitly. First, every result carries `resultType`; `"complete"` means an ordinary finished answer. Clients must treat a missing `resultType` from an older server as `"complete"`. Second, `resources/read` may instead answer with `resultType: "input_required"` when the server needs something from the client before it can produce the content — that retry mechanism is a protocol pattern of its own, not part of the resource shape. Every request also carries per-request metadata in `params._meta`, including the protocol version and client capabilities, because 2026-07-28 removed the `initialize` handshake — there is no earlier call that established context for this one. ## Watching for changes `resources/subscribe` and `resources/unsubscribe` were **removed** in 2026-07-28. A client that wants `notifications/resources/list_changed` or `notifications/resources/updated` opts in through `subscriptions/listen`, naming `resourcesListChanged` or specific URIs in `resourceSubscriptions`. Change notification is now one opt-in mechanism for all three primitives instead of a resource-specific pair of methods. ## Common mistakes Treating `resources/list` as if it returned content is the usual one — it returns descriptors, and a client that wants bytes must read each URI. The second is assuming a URI must be dereferenceable by the client itself; an `https://` resource URI is still read through the server, which may add credentials the client does not have. The third is inventing per-connection resource sets, which the current revision forbids.

  • Can one resources/read call legitimately return more than one entry in contents?
    Yes. `contents` is an array precisely so a single URI can expand to several pieces of content — parts of a document, or the files behind one logical resource. Each entry repeats its own `uri` and may carry its own `mimeType`, so the host can attribute each piece correctly when it packs them into context. A client that reads only `contents[0]` will silently drop data.
  • Why does a resources/list result carry ttlMs and cacheScope, and what does cacheScope private mean?
    `resources/list` is a cacheable result, so 2026-07-28 requires both fields: `ttlMs` says how long the client may reuse the listing, and `cacheScope` is `"public"` or `"private"`. `"private"` means the listing depends on the authorization presented and must not be shared across users — a shared or multi-tenant client must key the cache per credential rather than per server.
  • May a server return a different resource set on a second connection from the same client?
    No. Revision 2026-07-28 states the resource set MUST NOT vary per connection. It MAY vary by the authorization presented, since different credentials can legitimately see different data, but it must not depend on connection identity or on what was asked earlier — MCP is stateless, and an open stdio process is not a conversation.

resources/list is the library catalogue — call numbers, titles and formats; resources/read is fetching the actual book by its call number.

saying these in an interview costs you the question

  • Saying resources/list returns the file contents themselves
  • Claiming resources/read takes arbitrary arguments beyond a uri
  • Assuming a resource URI must be fetchable by the client directly
  • Treating a stdio connection as a session that scopes the resource set
  • Saying resources/subscribe is how you watch a resource today

context