📚Academy
likeone
online

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

AspectToolsResourcesPrompts
DirectionAI → WorldWorld → AIUser → AI
Triggered byAI model decidesClient/user requestsUser selects
PurposeExecute actionsProvide contextStructure interactions
ExampleWrite a fileRead config dataCode review template
State changeYes (side effects)No (read-only)No (templates only)
Discoverytools/listresources/listprompts/list
Who initiatesThe AI autonomouslyThe app or userThe 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.

Academy
Built with soul — likeone.ai