skip to content

What is an MCP resource template, and when do you publish one instead of a resource?

level: middleimportance: should knowfreq 45%

answer

  1. some resource spaces cannot be enumerated
  2. the URI has a hole in it
  3. RFC 6570 expansion
  4. its own method, not resources/list
  5. expand first, then read the concrete URI

basics

~20 s

A resource template is a parameterized URI published by resources/templates/list: a uriTemplate such as git://repo/{path} written in RFC 6570 syntax. Publish one when the resource space is too large or too dynamic to enumerate with resources/list.

solid answer

~50 s

`resources/list` enumerates concrete, directly-readable resources. That breaks down when the space is unbounded — every file in a repository, every row keyed by id, every log by date. For those, a server publishes a **resource template** through `resources/templates/list`. Each entry carries a `uriTemplate` written in **RFC 6570** URI Template syntax, such as `git://repo/{+path}` or `db://orders/{orderId}`, alongside the same descriptive fields a resource has: `name`, optional `description` and `mimeType`. The client expands the variables into a concrete URI and then calls `resources/read` with that URI — templates are addressing metadata, never something you read directly. Because the client has to supply the variable values, servers usually pair a template with `completion/complete` using a `ref/resource` reference, so a host can offer suggestions as the user types. Templates and concrete resources coexist: publish the handful worth showing in a picker, and template the long tail.

code

json · 16 lines
json
{
  "resourceTemplates": [
    {
      "uriTemplate": "git://repo/{+path}",
      "name": "repository-file",
      "description": "Any file in the checked-out repository",
      "mimeType": "text/plain"
    },
    {
      "uriTemplate": "db://orders/{orderId}",
      "name": "order-record",
      "description": "One order rendered as JSON",
      "mimeType": "application/json"
    }
  ]
}

go deeper

for a junior

Know that a template is a URI with variables in it, that it comes from resources/templates/list, and that you fill in the variables before calling resources/read.

for a middle

Explain RFC 6570 expansion — especially simple {var} versus reserved {+var} for paths — and why an unbounded resource space is templated rather than enumerated.

for a senior

Show judgment about which variables a client can actually supply, when to back a template with completion/complete, and why the server still validates the expanded URI rather than trusting expansion.

for a principal

Own the addressing design: how much of the data estate is enumerable versus templated, how stable those URI shapes stay across releases, and where the line to tools/call sits for anything that is a query rather than an address.

## The problem templates solve `resources/list` returns a finite array of descriptors. That is fine for a server exposing a dozen configuration documents and hopeless for one exposing every file in a monorepo, every ticket in a tracker, or every row in a table. Enumerating those is either impossibly large, impossibly slow, or both — and the listing would be stale by the time the client used it. A **resource template** describes the *shape* of an address instead of listing every address. The server says "anything matching this pattern is readable", and the client constructs the specific URI it wants. ## resources/templates/list Templates are published by their own method, `resources/templates/list`, which returns a `resourceTemplates` array. Each entry looks like a resource descriptor with one substitution: instead of a fixed `uri`, it carries a `uriTemplate`. The other fields are the same — `name`, optional `description`, optional `mimeType` — and serve the same purpose: telling a human or a model what this family of resources is and what reading one will produce. Keeping templates on a separate method matters. A client that only understands concrete resources can call `resources/list` and ignore templates entirely; nothing in a listing is ever unexpandable. ## RFC 6570 syntax `uriTemplate` is an **RFC 6570 URI Template**. The basics: `{var}` is a simple variable expansion that percent-encodes reserved characters, so `db://orders/{orderId}` with `orderId=42` becomes `db://orders/42`. The `+` operator, as in `{+path}`, is reserved expansion — it allows `/` and other reserved characters through unencoded, which is exactly what you want for a path variable like `git://repo/{+path}` expanding to `git://repo/src/main/App.kt`. That distinction is the one to get right. Using `{path}` for a multi-segment path percent-encodes the slashes into `%2F`, producing a URI the server will not recognise. Using `{+path}` where you meant a single opaque segment lets a value smuggle extra path structure into the URI, which is why a server must still validate what it receives rather than trusting that expansion preserved its intent. ## Reading a template There is no "read a template" call. The client expands the template into a concrete URI and calls `resources/read` with that URI as normal. Everything else follows the ordinary resource rules: the result is a `contents` array of text or blob entries with a required `resultType`, and a missing target is an error rather than an empty result. This is also why a template's variables must be things the client can plausibly know. A template whose variable is an internal surrogate key nobody can guess is a template nobody can use. ## Helping the client fill the variables Since the client supplies the values, servers can support argument completion through `completion/complete` with a `ref/resource` reference naming the template. The host then offers suggestions as the user types a path or an id, turning an unguessable space into a browsable one. It is optional, but a template without completion often means the user must already know the exact identifier. ## Templates and concrete resources together The two are not exclusive, and good servers publish both. List the small set worth surfacing in a picker — the README, the current config, today's log — and template the long tail so anything else remains reachable. The revision 2026-07-28 rule that the resource set MUST NOT vary per connection applies to templates as well: they may differ by the authorization presented, but not because of what happened earlier on the same connection, since MCP is stateless and an open stdio process is not a session. ## Where templates end and tools begin A template is still an *address*. If reaching the data needs several independent parameters, a query, ranking, or anything with a side effect, that is not addressing any more and belongs behind a tool. A URI template with six variables encoding a search query is a tool wearing a costume. The honest test: could a user bookmark the expanded URI and get the same content back later? If yes, it is a resource; if not, it is an operation. ## Common mistakes Calling `resources/read` with an unexpanded template string; using `{var}` where `{+var}` is needed for paths; publishing templates whose variables nobody can supply; and assuming `resources/list` will also return the templates — it will not, they have their own method.

  • Why would you write {+path} rather than {path} in a uriTemplate for a file path?
    RFC 6570's simple expansion, `{path}`, percent-encodes reserved characters, so `src/main/App.kt` becomes `src%2Fmain%2FApp.kt` and the server no longer sees a path. The `+` operator is reserved expansion: it lets `/` through unencoded, which is what a multi-segment path needs. The server must still validate the expanded value rather than assume it stayed inside the intended space.
  • How does a client discover valid values for a template variable?
    Optionally through `completion/complete` with a `ref/resource` reference naming the template, which lets the server suggest values as the user types a partial path or id. Without it the client can only accept whatever the user knows already, which is why a template over an unguessable key space is close to unusable in practice.
  • When does a parameterized resource stop being a resource and become a tool?
    When the address stops being an address. One or two identifying variables that yield the same content on every read is addressing. Several independent parameters, a query, ranking, or any side effect is an operation, and operations belong behind tools/call. The test is whether the expanded URI could be bookmarked and re-read later with the same result.

resources/list is a printed index of named documents; a template is the filing rule — "any file under this path is retrievable" — that lets you request one that was never indexed.

saying these in an interview costs you the question

  • Passing an unexpanded uriTemplate to resources/read
  • Expecting resources/list to include templates
  • Using {var} for a multi-segment path variable
  • Encoding a whole search query as template variables
  • Assuming the server expands the template for you

context