Not everything is an action. Expose read-only data as resources with a stable URI scheme, and package reusable workflows as prompts the user can trigger.
Why: resources are how a server hands context to the host WITHOUT the model taking an action — like a GET, they are read, not called. When: expose files, config, or database rows the model should be able to read as context. Where: each resource has a URI; the host reads it and can attach it to the model’s context.
@mcp.resource("weather://cities")
def known_cities() -> str:
"""The list of cities this server can forecast."""
return "Cairo, Tokyo, Berlin, Nairobi, Lima"
# Read-only context, not an action. The host reads weather://cities and can
# show it to the user or feed it to the model — no side effects.Why: a URI template lets one resource serve many items (weather://forecast/Cairo), and a predictable, documented scheme is what makes a server easy to consume. When: use path parameters for "one of many" data; keep the scheme stable so clients can rely on it. Where: never put secrets or tokens in a URI — it is an identifier, often logged.
@mcp.resource("weather://forecast/{city}")
def forecast_resource(city: str) -> str:
"""Today's forecast for a specific city, as readable text."""
return f"{city}: 34C, clear skies, light wind."
# weather://forecast/Cairo, weather://forecast/Tokyo, ...
# Predictable, documented URIs -> clients can construct them confidently.Why: a prompt is a saved, parameterized message template the USER invokes (a slash command in the host) — it packages a good instruction so people do not retype it. When: capture recurring workflows ("summarize this text", "review this diff") as prompts. Where: prompts are user-triggered, unlike tools (model-triggered) and resources (read as context).
@mcp.prompt()
def summarize(text: str) -> str:
"""A reusable 'summarize this' instruction the user can invoke."""
return (
"Summarize the following in 3 bullet points, plainly and without "
f"hype:\n\n{text}"
)
# In the host this shows up as an invokable prompt (e.g. a slash command),
# so users get a good, consistent instruction without writing it themselves.