MCP servers can share context as **resources** and expose actions as **tools**. Per the resources spec (2026-07-28), resources "allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information," each identified by a URI. Resources are application-driven: the host decides how to include them. Tools, by contrast, are model-controlled: the language model can discover and invoke them automatically (tools spec, User Interaction Model). This post is about that choice inside MCP. For packaging a workflow as written instructions versus an MCP tool, see skill vs MCP tool.
Resource or tool
All rows follow the resources and tools specs, 2026-07-28.
| Need | Prefer | Why |
|---|---|---|
| Readable context (file, schema, app data) for the host to include | Resource | Application-driven; host decides how to incorporate context |
| An action the model should choose to run | Tool | Model-controlled; discovered and invoked automatically |
| Discover what's available | `resources/list` or `tools/list` | Servers that declare the capability MUST respond |
| Fetch content by URI | `resources/read` | Params include `uri`; servers MAY return multiple contents (e.g. a directory) |
| Run an operation with arguments | `tools/call` | Model invokes a named tool |
Servers that support resources MUST declare the `resources` capability and MUST respond to `resources/list` with the set currently available to the requesting client. That set MAY be empty, MAY change over time, and MAY vary by the authorization on the request (resources spec, Capabilities).
What list and read return
From the resources Protocol Messages and Data Types sections (spec 2026-07-28):
- `resources/list` returns resource objects with `uri`, `name`, and optional `title`, `description`, `mimeType`, `size`, and `icons`. Supports pagination and caching.
- `resources/read` takes a `uri` and returns `contents`. Contents are text (`text`) or binary (`blob`, base64). Servers MAY return multiple contents for one read.
Common URI schemes on the resources page: `https://` (client may fetch directly when able), `file://` (filesystem-like; need not map to a physical filesystem), `git://`, plus custom schemes under RFC 3986.
If a resource URI does not exist, servers MUST return JSON-RPC error `-32602` (Invalid Params) and MUST NOT return an empty `contents` array. Servers SHOULD use `-32603` for internal errors (Error Handling).
Security rules from the spec
The resources Security Considerations section (2026-07-28) requires:
- Servers MUST validate all resource URIs.
- Access controls SHOULD apply to sensitive resources.
- Servers MUST sanitize `file://` paths against directory traversal.
Optional pieces (templates, `listChanged` / `subscribe`, annotations such as `audience` / `priority` / `lastModified`) are in the same resources page; they are client hints, not the decision between resource and tool. Tool-behavior hints are covered in MCP tool annotations.
Pitfalls
- Wrapping every read behind a tool. If the host only needs context, a resource matches the application-driven model; a tool invites the model to call it.
- Returning an empty `contents` array for a missing URI. The spec forbids that; use `-32602`.
- Skipping URI validation on `file://`. Path sanitization against directory traversal is a MUST.
- Re-arguing skills here. Skills are written instructions; resources and tools are protocol primitives. Use skill vs MCP tool for that contrast.
How to check it
Call `resources/list` on your server and confirm the URIs you expect. Then `resources/read` a known URI and a missing one: the known path should return contents, and the missing one should be `-32602`, not an empty array.
Where the kit fits
The MCP Starter Kit ($27) is built for teams putting OAuth-protected remote MCP servers in front of Cursor and Claude Connect. Soft path: start at /free; Cheat Sheet for the skim checklist mindset; kit when you want the auth-ready layout.
Soft next step
List what your server currently exposes as tools, mark anything that is only readable context, and move those to `resources/list` + `resources/read` with URI validation. Soft ladder: /free, then the MCP Starter Kit ($27). Related: OAuth pillar, skill vs MCP tool, MCP tool annotations.