# Resources

Course: [Model Context Protocol](https://www.devtutorials.ai/learn/mcp) · Section: Core Primitives
Source: https://www.devtutorials.ai/learn/mcp/core-primitives/resources

> The MCP resource model: URIs, names and MIME types, text and base64 blob contents, resource templates, common kinds of resources, and why reads must be read-only and deterministic.

Think about a library. You can walk in, take a book off the shelf, and read it. Reading doesn't change the book. The next person who reads it sees the same words. Each book also has an address: a shelf number that tells you exactly where it is.
 
MCP **resources** are the library's books. They are data an app can read, each at its own address, called a **URI**. Reading a resource never changes anything.

**The main idea:** a resource is read-only data with a URI. The app decides which resources to read and put into the model's context.

## A Real-World Example

Brightgrid's support assistant often answers questions about returns and shipping.

- At first, the team made a tool called `get_policy`. The model called it on almost every message, "just in case". That was slow and cost tokens.
- They turned the policies into resources: `kb://returns-policy` and `kb://shipping`.
- Now the **host** decides: when the question mentions returns, it reads `kb://returns-policy` and adds it to the context. The model doesn't have to ask.

Order records became resources too, through one template: `orders://{order_id}`.

**In simple words:** tools are for doing. Resources are for reading. The app chooses what to read.

## What You Will Learn

In this lesson, you will learn:

1. The resource model: URI, name, title, description and MIME type.
2. How text and binary resources are returned.
3. How resource templates give one URI pattern for many resources.
4. Common kinds of resources: static files, records, logs and schemas.
5. The rules resources must follow: read-only, no side effects, the same answer every time.

The **MCP Lab** Explorer now lists **Resource templates** too. Pick one, fill in the URI, and press **Read**. Images that a server returns are shown as pictures.

Now that you know why resources matter, let's look at how a server describes them. Before a host can read a resource, it needs to know what resources are available and what kind of data they contain.

## The Resource Model

The host discovers available resources through `resources/list`. Along with each resource's URI, the server can provide additional information that helps the host and users understand what the resource contains.

### What a Resource Has

`resources/list` returns a list of resources.

| Field | Meaning |
| --- | --- |
| `uri` | Its address, like `kb://shipping`. Unique and stable |
| `name` | A short name |
| `title` | A friendly label for people (optional) |
| `description` | What it holds, for the host and the model (optional) |
| `mimeType` | What kind of data it is, like `text/plain` or `image/png` (optional) |

```js runnable title="List resources by kind"
// The resources/list result from the lab server (trimmed).
const resources = [
  {
    uri: "kb://returns-policy",
    name: "returns-policy",
    title: "Returns policy",
    mimeType: "text/plain",
  },
  {
    uri: "kb://shipping",
    name: "shipping",
    title: "Shipping times",
    mimeType: "text/plain",
  },
  {
    uri: "assets://logo.png",
    name: "logo",
    title: "Brightgrid logo",
    mimeType: "image/png",
  },
  { uri: "orders://A-1001", name: "Order A-1001", mimeType: "application/json" },
];

for (const r of resources) {
  const kind = r.mimeType.startsWith("text/")
    ? "text"
    : r.mimeType.startsWith("image/")
      ? "image"
      : "data";
  console.log(r.uri.padEnd(22), kind.padEnd(6), r.title ?? r.name);
}
```

Expected output:

```text
kb://returns-policy    text   Returns policy
kb://shipping          text   Shipping times
assets://logo.png      image  Brightgrid logo
orders://A-1001        data   Order A-1001
```

### Application-Driven

Resources are **application-driven**. The host decides which ones to read and when:

- It may show a list and let the user attach one, like attaching a file to an email.
- It may pick some automatically, like the policy that matches the question.
- It may read one because a tool result pointed to it.

Knowing that a resource exists is only the first step. To use the data it contains, the host reads the resource using its URI.

### Reading a Resource

`resources/read` takes a URI and returns `contents`, a list of items. Each item has its `uri`, its `mimeType`, and the data:

- **`text`** for text, like a policy or a JSON record.
- **`blob`** for bytes, like an image or a PDF, written as **base64**. Base64 turns any bytes into plain letters, digits, `+` and `/`, so they can travel inside JSON.

```js runnable title="Text and blob contents"
// resources/read returns "contents". Text comes as text. Bytes come as base64 in "blob".
const contents = [
  {
    uri: "kb://shipping",
    mimeType: "text/plain",
    text: "Standard shipping takes 3 to 6 days.",
  },
  {
    uri: "assets://logo.png",
    mimeType: "image/png",
    blob: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMElEQVR42mPkndLxn4ECwMRAIRh4A1jQBT5ll+PVwDe1c7iFwagBWNIBejyPgDAAAO2SBnj02loiAAAAAElFTkSuQmCC",
  },
];

for (const c of contents) {
  if ("text" in c) {
    console.log(c.uri, "-> text,", c.text.length, "characters");
  } else {
    const bytes = atob(c.blob); // base64 text -> raw bytes
    console.log(
      c.uri,
      "-> blob,",
      c.blob.length,
      "base64 characters =",
      bytes.length,
      "bytes, starts with",
      bytes.slice(1, 4),
    );
  }
}
```

Expected output:

```text
kb://shipping -> text, 36 characters
assets://logo.png -> blob, 140 base64 characters = 105 bytes, starts with PNG
```

The lab reads the logo's base64 `blob` and shows it as a small picture.

Listing every resource works when there are only a few items. However, some data sets contain thousands or even millions of records. Resource templates solve this problem by describing many resources with a single URI pattern.

## Resource Templates

### One Pattern, Many Resources

Some data has too many items to list one by one, like every order. A **resource template** describes them all with one pattern: `orders://{order_id}`. The part in braces is a **placeholder**. The client fills it in to make a real URI, like `orders://A-1002`.

- `resources/templates/list` returns the templates, each with a `uriTemplate`.
- `resources/read` takes the filled-in URI.
- The server matches the URI to the template and reads the right record.

```js runnable title="Fill and match a template"
// A resource template: one pattern, many resources.
const template = "orders://{order_id}";

function expand(t, values) {
  return t.replace(/\{(\w+)\}/g, (_, name) => encodeURIComponent(values[name]));
}

function match(t, uri) {
  const names = [];
  const pattern = t.replace(/\{(\w+)\}/g, (_, name) => {
    names.push(name);
    return "([^/]+)";
  });
  const m = uri.match(new RegExp("^" + pattern + "$"));
  return m
    ? Object.fromEntries(names.map((n, i) => [n, decodeURIComponent(m[i + 1])]))
    : null;
}

console.log(expand(template, { order_id: "A-1002" }));
console.log(JSON.stringify(match(template, "orders://A-1003")));
console.log(JSON.stringify(match(template, "tickets://T-501")));
```

Expected output:

```text
orders://A-1002
{"order_id":"A-1003"}
null
```

Now try a missing order. In the Explorer, change the URI to `orders://A-9999` and press **Read**. The server answers with error `-32602`, "Resource not found".

In the SDK, a template is a `ResourceTemplate`. It can also list the items it knows and suggest placeholder values while the user types, through `completion/complete`.

Templates follow a standard called <b>URI templates</b> (RFC 6570). The simple <code>{"{name}"}</code> form is all most servers need. You will meet query parameters and other schemes in the Resource URIs lesson.

Resources can hold many kinds of information. Recognizing common types helps you choose a clear URI and MIME type for the data the host needs.

## Kinds of Resources

### Static Files

Files that do not change often: a README, a policy, or a style guide. Their URIs are often `file://` paths or a custom scheme like `kb://`.

### Records

One row or document from a database, often through a template: `orders://{order_id}`, `customers://{id}`.

### Logs

Recent log lines from a system, like `logs://api/today`. They change over time, but each read still only reads. The server should say clearly what time range a read covers.

### Schemas

The shape of a database or an API: table names, columns, and types. A model that writes queries needs this, and it is a perfect resource: read-only and rarely changed.

```js runnable title="Kinds of resources and their MIME types"
// Common kinds of resources, and the MIME type that tells the host how to show them.
const kinds = [
  ["Static file", "file:///project/README.md", "text/markdown"],
  ["Database schema", "db://shop/schema", "application/sql"],
  ["Log file", "logs://api/today", "text/plain"],
  ["Record via template", "orders://A-1002", "application/json"],
  ["Image", "assets://logo.png", "image/png"],
];

for (const [kind, uri, mime] of kinds) {
  console.log(kind.padEnd(20), uri.padEnd(26), mime);
}
```

Expected output:

```text
Static file          file:///project/README.md  text/markdown
Database schema      db://shop/schema           application/sql
Log file             logs://api/today           text/plain
Record via template  orders://A-1002            application/json
Image                assets://logo.png          image/png
```

No matter what they contain, resources should behave predictably. Hosts may cache resources, read them multiple times, or load them automatically. To support these scenarios, resources must follow a few important rules.

## The Rules for Resources

### Read-Only and No Side Effects

Reading a resource must **never change anything**: no 'mark as read', no counters stored in the data, no emails sent. A host may read the same resource many times, read it ahead of time, or keep a copy. If reading had side effects, all of that would cause trouble.

### Deterministic Reads

**Deterministic** means "the same input gives the same output." Two reads of the same URI, close together, should return the same data unless the data itself changed in between.

```js runnable title="A good read and a bad read"
// Reading a resource must not change anything. Two reads in a row give the same answer.
const store = { "kb://shipping": "Standard shipping takes 3 to 6 days." };
let reads = 0;

function goodRead(uri) {
  reads++;
  return store[uri];
}

let counter = 0;
function badRead(uri) {
  counter++;
  return store[uri] + " (viewed " + counter + " times)";
}

console.log(
  "good:",
  goodRead("kb://shipping") === goodRead("kb://shipping")
    ? "same both times"
    : "different",
);
console.log(
  "bad: ",
  badRead("kb://shipping") === badRead("kb://shipping")
    ? "same both times"
    : "different: the read changed something",
);
console.log("reads counted outside the data:", reads);
```

Expected output:

```text
good: same both times
bad:  different: the read changed something
reads counted outside the data: 2
```

### Immutability Where You Can

Some resources never change once made, like an invoice for a past month or a specific version of a file. Put the version or date in the URI, like `invoices://2026-09`. Then a host can keep a copy forever, and a URI always means the same thing.

Resource text goes into the model's context, so it can carry **prompt injection**: text that tries to give the model new instructions. A support ticket or a web page saying "ignore your rules" is data, not a command. Hosts should label resource text as data and never let it change the rules.

## Common mistakes

- **Using a tool where a resource fits.** If it only returns data and the app can choose it, make it a resource.
- **Forgetting templates.** Listing a million records is impossible. A template covers them all.
- **Sending big binaries as text.** Use `blob` with base64, and the right MIME type.
- **Side effects on read.** A read that changes data breaks caching and surprises users.
- **Trusting resource text.** It is data. Label it, and never let it override the host's rules.

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- A **resource** is read-only data with a **URI**, a name, an optional title and description, and a **MIME type**.
- Resources are **application-driven**: the host chooses what to read.
- `resources/read` returns **`text`** or base64 **`blob`** contents.
- **Templates** like `orders://{order_id}` cover many resources with one pattern.
- Typical resources: static files, records, logs and schemas.
- Reads must be **side-effect free** and **deterministic**. Resource text is data, never instructions.

Next: **Prompts**, ready-made message templates a user can pick.
