Resources & Prompts
MCP has three primitives: Tools, Resources, and Prompts. You already know Tools. This lesson covers the other two — data the AI can read (Resources) and interaction templates the user can select (Prompts) — with working code for both.
The Three MCP Primitives
Think of MCP primitives as three different directions of information flow:
⚙️
Tools
Actions the AI can take
📄
Resources
Data the AI can read
📝
Prompts
Templates the user selects
Side-by-Side Comparison
| Aspect | Tools | Resources | Prompts |
|---|---|---|---|
| Direction | AI → World | World → AI | User → AI |
| Triggered by | AI model decides | Client/user requests | User selects |
| Purpose | Execute actions | Provide context | Structure interactions |
| Example | Write a file | Read config data | Code review template |
| State change | Yes (side effects) | No (read-only) | No (templates only) |
| Discovery | tools/list | resources/list | prompts/list |
| Who initiates | The AI autonomously | The app or user | The user from a menu |
Resources: Giving AI Read-Only Data
A Resource is read-only data your server exposes to the AI through a URI. Unlike tools, resources do not execute actions or cause side effects. They are for providing context — configuration files, documentation, database schemas, API specs.
When to use a Resource instead of a Tool: If the data is read-only and the AI does not need to decide when to fetch it — use a Resource. If the AI needs to take an action or make a decision about when to fetch data — use a Tool. Example: A project README is a Resource. A database query is a Tool.
Resource ExampleTypeScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { readFileSync } from "fs";
const server = new McpServer({ name: "project-context", version: "1.0.0" });
// ── Static Resource: project README ──────────────────────
// URI scheme is up to you — file://, docs://, project:// all work.
// The URI is how the client requests this specific resource.
server.resource(
"project-readme", // unique name
"file:///project/README.md", // URI
async () => ({
contents: [{
uri: "file:///project/README.md",
text: readFileSync("./README.md", "utf-8"),
mimeType: "text/markdown",
}],
})
);
// ── Dynamic Resource: database schema ────────────────────
// Resources can be dynamic — the handler runs each time
// the resource is requested, returning fresh data.
server.resource(
"db-schema",
"db://schema/tables",
async () => {
const tables = await db.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'");
return {
contents: [{
uri: "db://schema/tables",
text: tables.rows.map(t => t.table_name).join("\n"),
mimeType: "text/plain",
}],
};
}
);
name
A human-readable identifier. Shows up in resource listings.
URI
The address the client uses to request this resource. You define the scheme (
file://, db://, etc.).
handler
Async function returning
{ contents: [{ uri, text, mimeType }] }. Runs when the resource is read.
This lesson is for Pro members
Unlock all 520+ lessons across 52 courses with Academy Pro.
Already a member? Sign in to access your lessons.