Chapter 4 · Part 2
Resources & prompts
Tools do things. The other two primitives provide things: resources hand data to the
model, and prompts give users reusable templates. Same FastMCP, two more decorators.
Resources: data by URI
A resource is read-only data the model (or user) can load into context — like a GET
endpoint. Each has a URI, and the URI can be templated with {placeholders} that become
function arguments:
@mcp.resource("notes://all")
def all_notes() -> str:
"""The full contents of the user's notes file."""
with open("notes.txt") as f:
return f.read()
@mcp.resource("note://{topic}")
def note_by_topic(topic: str) -> str:
"""The notes lines about one topic."""
with open("notes.txt") as f:
return "\n".join(l for l in f if topic.lower() in l.lower())Ask for note://travel and the client calls note_by_topic("travel"). The difference from a
tool is intent: a resource exposes data to read, a tool performs an action. Reach for a
resource when the model just needs to see something, a tool when it needs to do
something.
Prompts: reusable templates
A prompt is a parameterized template a user can pick from the host's UI — "slash commands" for your server. It returns the text that gets sent to the model:
@mcp.prompt()
def summarize_notes(topic: str, style: str = "bullet points") -> str:
"""Summarize the user's notes on a topic."""
return f"Read my notes on {topic} and summarize them as {style}."Now a user can invoke "summarize_notes" with topic="travel", and the host expands it into a
full instruction — no retyping the same prompt every time.
- Tools — the model decides to call them, mid-task.
- Resources — the application (or user) chooses what data to load in.
- Prompts — the user picks them deliberately, like a menu command.
Your server now speaks all three primitives. Time to plug it into Claude.