MCP Resources and Prompts — Context Exposure Beyond Tools
> Tools get 90 percent of MCP attention. The other two server primitives solve different problems. Resources expose data for reading; prompts expose reusable templates as slash-commands. Many servers should use resources instead of wrapping reads in tools, and prompts instead of hard-coding workflows in client prompts. This lesson names the decision rule and walks the resources/* and prompts/* messages.
Type: Build
Languages: Python (stdlib, resource + prompt handler)
Prerequisites: Phase 13 · 07 (MCP server)
Time: ~45 minutes
Learning Objectives
- Decide between exposing a capability as a tool, a resource, or a prompt for a given domain.
- Implement
resources/list,resources/read,resources/subscribeand handlenotifications/resources/updated. - Implement
prompts/listandprompts/getwith argument templates. - Recognize when the host surfaces prompts as slash-commands vs auto-injected context.
The Problem
A naive MCP server for a notes app exposes everything as tools: notes_read, notes_list, notes_search. This wraps every data access in a model-driven tool call. Consequences:
- The model has to decide whether to call
notes_readfor every query that might benefit from context. - Read-only content cannot be subscribed to or streamed to the host's side panel.
- Client UIs (Claude Desktop's resource attachment panel, Cursor's "Include file" picker) cannot surface the data.
The right split: expose data as a resource, expose mutating or computed actions as tools, expose reusable multi-step workflows as prompts. Each primitive has its UX affordance and its access pattern.
The Concept
Tools vs resources vs prompts — the decision rule
| Capability | Primitive |
|---|---|
| User wants to search, filter, or transform data | tool |
| User wants the host to include this data as context | resource |
| User wants a templated workflow they can re-run | prompt |
Guideline: if the model would benefit from calling it on every related query, it is a tool. If the user would benefit from attaching it to a conversation, it is a resource. If a whole multi-step workflow is the unit the user wants to re-use, it is a prompt.
Resources
resources/list returns {resources: [{uri, name, mimeType, description?}]}. resources/read takes {uri} and returns {contents: [{uri, mimeType, text | blob}]}.
URIs can be anything addressable:
file:///Users/alice/notes/mcp.mdpostgres://my-db/query/SELECT ...notes://note-14(custom scheme)memory://session-2026-04-22/recent(server-specific)
contents[] supports both text and binary. Binary uses blob as a base64-encoded string plus a mimeType.
Resource subscriptions
Declare {resources: {subscribe: true}} in capabilities. Client calls resources/subscribe {uri}. Server sends notifications/resources/updated {uri} when the resource changes. Client re-reads.
Use case: a notes server whose resources are files on disk; a file watcher triggers update notifications; Claude Desktop re-pulls the file into context when edited outside the host.
Resource templates (2025-11-25 addition)
resourceTemplates let you expose a parameterized URI pattern: notes://{id} with id as a completion target. The client can autocomplete ids in the resource picker.
Prompts
prompts/list returns {prompts: [{name, description, arguments?}]}. prompts/get takes {name, arguments} and returns {description, messages: [{role, content}]}.
A prompt is a template that fills to a list of messages the host feeds its model. For example, a code_review prompt takes a file_path argument and returns a three-message sequence: a system message, a user message with the file body, and an assistant kickoff with a reasoning template.
Hosts and prompts
Claude Desktop, VS Code, and Cursor expose prompts as slash-commands in the chat UI. The user types /code_review and picks arguments from a form. The server's prompt is the contract between "user shortcut" and "full prompt sent to model".
Not every client supports prompts yet — check capability negotiation. A server with prompt capability declared but a client without prompt support simply will not see the slash commands.
The "list changed" notification
Both resources and prompts emit notifications/list_changed when the set mutates. A notes server that just imported 20 new notes emits notifications/resources/list_changed; the client re-calls resources/list to pick up the additions.
Content type conventions
For text: mimeType: "text/plain", text/markdown, application/json.
For binary: image/png, application/pdf, plus the blob field.
For MCP Apps (Lesson 14): text/html;profile=mcp-app in a ui:// URI.
Dynamic resources
A resource URI does not have to correspond to a static file. notes://recent can return the latest five notes on every read. db://query/users/active can execute a parameterized query. The server is free to compute content dynamically.
Rule: if the client can cache by URI, the URI must be stable. If computation is one-shot, the URI should include a timestamp or nonce so the client cache does not stale out.
Subscriptions vs polling
Subscription-capable clients get server push via notifications/resources/updated. Pre-subscription clients or hosts that do not support it poll by re-reading. Both are spec-compliant. The server's capability declaration tells the client which it supports.
Cost of subscriptions: per-session state on the server (who is subscribed to what). Keep the subscribed set bounded; disconnected clients should time out.
Prompts vs system prompts
Prompts in MCP are not system prompts. The host's system prompt (its own operating instructions) and MCP prompts (server-supplied templates invoked by user) live side by side. A well-behaved client never lets a server prompt override its own system prompt; it layers them.
Use It
code/main.py extends the notes server from Lesson 07 with:
- Per-note resources (
notes://note-1, etc.) withresources/subscribesupport. - A
review_noteprompt that renders to a three-message template. - A file-watcher simulation that emits
notifications/resources/updatedwhen a note is modified. - A
notes://recentdynamic resource that always returns the latest five notes.
Run the demo to see the full flow.
Ship It
This lesson produces outputs/skill-primitive-splitter.md. Given a proposed MCP server, the skill categorizes each capability as tool / resource / prompt with a rationale.
Exercises
- Run
code/main.py. Observe the initial resource list, then trigger a note edit and verify thenotifications/resources/updatedevent fires.
- Add a
resources/list_changedemitter: when a new note is created, send the notification so clients re-discover.
- Design three prompts for a GitHub MCP server:
summarize_pr,triage_issue,release_notes. Each with argument schemas. The prompt body should be runnable without further edits.
- Take an existing tool in the Lesson 07 server and classify whether it should remain a tool or be split into a resource plus tool pair. Justify in one sentence.
- Read the spec's
server/resourcesandserver/promptssections. Identify the one field inresources/readthat is rarely populated but spec-supported. Hint: look at_metaon resource content.
Key Terms
| Term | What people say | What it actually means | |||
|---|---|---|---|---|---|
| Resource | "Exposed data" | URI-addressable content the host can read | |||
| Resource URI | "Pointer to data" | Scheme-prefixed identifier (file://, notes://, etc.) |
|||
resources/subscribe |
"Watch for changes" | Client-opt-in server-push updates for a specific URI | |||
notifications/resources/updated |
"Resource changed" | Signal to client that a subscribed resource has new content | |||
| Resource template | "Parameterized URI" | URI pattern with completion hints for the host picker | |||
| Prompt | "Slash-command template" | Named multi-message template with argument slots | |||
| Prompt arguments | "Template inputs" | Typed parameters the host collects before rendering | |||
prompts/get |
"Render template" | Server returns the filled-in message list | |||
| Content block | "Typed chunk" | `{type: text | image | resource | ui_resource}` |
| Slash-command UX | "User shortcut" | Host surfaces prompts as commands starting with / |
Further Reading
- MCP — Concepts: Resources — resource URIs, subscriptions, and templates
- MCP — Concepts: Prompts — prompt templates and slash-command integration
- MCP — Server resources spec 2025-11-25 — full
resources/*message reference - MCP — Server prompts spec 2025-11-25 — full
prompts/*message reference - MCP — Protocol info site: resources — community guide expanding on the official docs