Who this is for and what you will learn
This article is for readers who can already run a FastMCP server and want clients to read data, or to request reusable prompt templates. By the end you should be able to expose data with @mcp.resource, generate resources from URI templates, define reusable prompts with @mcp.prompt, and mask error details when needed.
What resources and prompts are
Resources are data or files that an MCP client can read. Resource templates extend them, so a client can request a dynamically generated resource based on parameters in the URI[1]. Resources provide read-only access. When a client requests a resource URI, FastMCP finds the definition, runs the function if the resource is dynamic, and returns the content[1].
Prompts are reusable message templates that help LLMs generate structured, purposeful responses. FastMCP defines them mainly with the @mcp.prompt decorator[2]. When a client requests a prompt, FastMCP finds the definition, validates the parameters against the function signature, runs the function, and returns the generated messages to the model[2].
Define a resource with @mcp.resource
The most common approach is to decorate a Python function with @mcp.resource. The decorator requires the resource's unique URI[1]. The example below defines two resources, one returning a string and one returning JSON data[1]:
import json
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# Basic dynamic resource returning a string
@mcp.resource("resource://greeting")
def get_greeting() -> str:
"""Provides a simple greeting message."""
return "Hello from FastMCP Resources!"
# Resource returning JSON data
@mcp.resource("data://config")
def get_config() -> str:
"""Provides application configuration as JSON."""
return json.dumps({
"theme": "dark",
"version": "1.2.0",
"features": ["tools", "resources"],
})A few points about this code[1]:
- The first argument is the URI, which clients use to request the data, such as
"resource://greeting". - The function is lazy. It runs only when a client requests its URI through
resources/read. - By default, the resource name comes from the function name, and the description comes from the docstring.
- The decorator also accepts
uri,name,description,mime_type,tagsandmeta. For example,mime_type="application/json"sets an explicit MIME type.
Return values decide the content type
- A
stris sent asTextResourceContents, withtext/plainas the default MIME type[1]. bytesare base64-encoded and sent asBlobResourceContents. Declare a suitable MIME type, such asimage/png[1].- JSON-native values (
dict,list,tuple, numbers, booleans andNone) are serialized to JSON text. They keep the declared MIME type, which istext/plainunless you set another. To make clients parse the payload as JSON, setmime_type="application/json"explicitly[1]. - For custom classes that are not JSON-native, return a
ResourceResult, or a list ofResourceContent, so that serialization and MIME types are explicit[1].
Resource templates: parameters in the URI
A resource template uses the same @mcp.resource decorator. The URI contains {parameter_name} placeholders, and the function signature has matching arguments[1]. Each distinct set of parameters yields its own resource. If "user://profile/{name}" is registered, a client can request "user://profile/ford" without any separate registration for that profile[1].
The example below defines two templates. One has a {city} placeholder, and the other has two parameters and annotations[1]:
# Template URI includes {city} placeholder
@mcp.resource("weather://{city}/current")
def get_weather(city: str) -> str:
"""Provides weather information for a specific city."""
return json.dumps({
"city": city.capitalize(),
"temperature": 22,
"condition": "Sunny",
"unit": "celsius"
})A few rules apply[1]:
- Required function parameters, those without default values, must appear in the URI path template.
- Resource templates do not accept
*args, but they do accept**kwargs, because the URI template names the parameters. - FastMCP implements RFC 6570 URI templates, including simple expansion, wildcard path parameters and form-style query parameters[1].
Error handling for resources
A resource function can raise a standard Python exception, such as ValueError or FileNotFoundError, or a FastMCP ResourceError[1]. By default, exceptions and their details are logged and converted into an MCP error response that goes back to the client[1].
To hide internal details, set mask_error_details=True when you create the server, as in FastMCP(name="SecureServer", mask_error_details=True)[1]. Then only ResourceError messages include details, and other exceptions become a generic message[1]. Note that ResourceError contents are sent back to clients whatever the mask_error_details setting is. Its messages should therefore be safe to show to callers[1].
Define a prompt with @mcp.prompt
Decorate a function with @mcp.prompt to define a prompt. The function name becomes the prompt's identifier[2]. The examples below define two prompts. One returns a string, which becomes a user message. The other returns a conversation made of several messages[2]:
from fastmcp import FastMCP
from fastmcp.prompts import Message
mcp = FastMCP(name="PromptServer")
# Basic prompt returning a string (converted to user message automatically)
@mcp.prompt
def ask_about_topic(topic: str) -> str:
"""Generates a user message asking for an explanation of a topic."""
return f"Can you please explain the concept of '{topic}'?"
# Prompt returning multiple messages
@mcp.prompt
def generate_code_request(language: str, task_description: str) -> list[Message]:
"""Generates a conversation for code generation."""
return [
Message(f"Write a {language} function that performs the following task: {task_description}"),
Message("I'll help you write that function.", role="assistant"),
]By default, the prompt name comes from the function name, and the description comes from the summary of the docstring. Parameter descriptions in the docstring fill in the description of each argument[2].
The MCP specification requires prompt arguments to be strings. FastMCP lets you use annotations such as list[int] and dict[str, str], converts incoming string arguments to those types, and adds JSON format descriptions to the arguments, so that people and models know how to fill them in[2].
Prompt return values
A prompt function returns one of three shapes[2]:
- A
str, sent as a single user message. - A
list[Message | str], which is a conversation. Strings are converted to user messages. - A
PromptResult, when you need full control over messages, description and metadata.
The roleplay example below returns two messages, a user message and an assistant message[2]:
from fastmcp.prompts import Message
@mcp.prompt
def roleplay_scenario(character: str, situation: str) -> list[Message]:
"""Sets up a roleplaying scenario with initial messages."""
return [
Message(f"Let's roleplay. You are {character}. The situation is: {situation}"),
Message("Okay, I understand. I am ready. What happens next?", role="assistant")
]Parameters without default values are required. Prompts do not accept *args or **kwargs either[2].
Limits
- Resource templates do not accept
*args. Prompts accept neither*argsnor**kwargs[1][2]. - The MCP specification allows only string arguments, so clients must fill complex types in the JSON format that FastMCP describes[2].
- With
mask_error_detailson, onlyResourceErrormessages keep their details. Keep the messages you expose to callers within safe bounds[1].
Next steps
For parameter options, component visibility, async resources and context access, read the full resources and prompts pages of the FastMCP documentation[1][2].
Adapted from the Resources and Prompts pages of the FastMCP official documentation[1][2], released under the Apache-2.0 License.
Sources
- 1FastMCP Resources & TemplatesFastMCP docs (Apache-2.0)
- 2FastMCP PromptsFastMCP docs (Apache-2.0)
Cite this page: Resources and prompts in FastMCP. TellWorld, https://www.tellworld.net/practice/practice-fastmcp-resources-prompts. Updated 2026-11-14