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

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 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:

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:

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

  1. Run code/main.py. Observe the initial resource list, then trigger a note edit and verify the notifications/resources/updated event fires.
  1. Add a resources/list_changed emitter: when a new note is created, send the notification so clients re-discover.
  1. 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.
  1. 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.
  1. Read the spec's server/resources and server/prompts sections. Identify the one field in resources/read that is rarely populated but spec-supported. Hint: look at _meta on 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