---
title: Resources & Templates
sidebarTitle: Resources & Templates
description: Expose data sources and dynamic content generators to your MCP client.
icon: database
---

Resources represent data or files that an MCP client can read, and resource templates extend this concept by allowing clients to request dynamically generated resources based on parameters passed in the URI.

FastMCP simplifies defining both static and dynamic resources, primarily using the `@mcp.resource` decorator.

## What Are Resources?

Resources provide read-only access to data for the LLM or client application. When a client requests a resource URI:

1.  FastMCP finds the corresponding resource definition.
2.  If it's dynamic (defined by a function), the function is executed.
3.  The content (text, JSON, binary data) is returned to the client.

This allows LLMs to access files, database content, configuration, or dynamically generated information relevant to the conversation.

## Defining Resources

### The `@resource` Decorator

The most common way to define a resource is by decorating a Python function. The decorator requires the resource's unique URI.

```python
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 (dict is auto-serialized)
@mcp.resource("data://config")
def get_config() -> dict:
    """Provides application configuration as JSON."""
    return {
        "theme": "dark",
        "version": "1.2.0",
        "features": ["tools", "resources"],
    }
```

**Key Concepts:**

*   **URI:** The first argument to `@resource` is the unique URI (e.g., `"resource://greeting"`) clients use to request this data.
*   **Lazy Loading:** The decorated function (`get_greeting`, `get_config`) is only executed when a client specifically requests that resource URI via `resources/read`.
*   **Inferred Metadata:** By default:
    *   Resource Name: Taken from the function name (`get_greeting`).
    *   Resource Description: Taken from the function's docstring.

### Return Values

FastMCP automatically converts your function's return value into the appropriate MCP resource content:

-   **`str`**: Sent as `TextResourceContents` (with `mime_type="text/plain"` by default).
-   **`dict`, `list`, `pydantic.BaseModel`**: Automatically serialized to a JSON string and sent as `TextResourceContents` (with `mime_type="application/json"` by default).
-   **`bytes`**: Base64 encoded and sent as `BlobResourceContents`. You should specify an appropriate `mime_type` (e.g., `"image/png"`, `"application/octet-stream"`).
-   **`None`**: Results in an empty resource content list being returned.

### Resource Metadata

You can customize the resource's properties using arguments in the decorator:

```python
from fastmcp import FastMCP

mcp = FastMCP(name="DataServer")

# Example specifying metadata
@mcp.resource(
    uri="data://app-status",      # Explicit URI (required)
    name="ApplicationStatus",     # Custom name
    description="Provides the current status of the application.", # Custom description
    mime_type="application/json", # Explicit MIME type
    tags={"monitoring", "status"} # Categorization tags
)
def get_application_status() -> dict:
    """Internal function description (ignored if description is provided above)."""
    return {"status": "ok", "uptime": 12345, "version": mcp.settings.version} # Example usage
```

- **`uri`**: The unique identifier for the resource (required).
- **`name`**: A human-readable name (defaults to function name).
- **`description`**: Explanation of the resource (defaults to docstring).
- **`mime_type`**: Specifies the content type (FastMCP often infers a default like `text/plain` or `application/json`, but explicit is better for non-text types).
- **`tags`**: A set of strings for categorization, potentially used by clients for filtering.


### Asynchronous Resources

Use `async def` for resource functions that perform I/O operations (e.g., reading from a database or network) to avoid blocking the server.

```python
import aiofiles
from fastmcp import FastMCP

mcp = FastMCP(name="DataServer")

@mcp.resource("file:///app/data/important_log.txt", mime_type="text/plain")
async def read_important_log() -> str:
    """Reads content from a specific log file asynchronously."""
    try:
        async with aiofiles.open("/app/data/important_log.txt", mode="r") as f:
            content = await f.read()
        return content
    except FileNotFoundError:
        return "Log file not found."
```

### Resource Classes

While `@mcp.resource` is ideal for dynamic content, you can directly register pre-defined resources (like static files or simple text) using `mcp.add_resource()` and concrete `Resource` subclasses.

```python
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.resources import FileResource, TextResource, DirectoryResource

mcp = FastMCP(name="DataServer")

# 1. Exposing a static file directly
readme_path = Path("./README.md").resolve()
if readme_path.exists():
    # Use a file:// URI scheme
    readme_resource = FileResource(
        uri=f"file://{readme_path.as_posix()}",
        path=readme_path, # Path to the actual file
        name="README File",
        description="The project's README.",
        mime_type="text/markdown",
        tags={"documentation"}
    )
    mcp.add_resource(readme_resource)

# 2. Exposing simple, predefined text
notice_resource = TextResource(
    uri="resource://notice",
    name="Important Notice",
    text="System maintenance scheduled for Sunday.",
    tags={"notification"}
)
mcp.add_resource(notice_resource)

# 3. Exposing a directory listing
data_dir_path = Path("./app_data").resolve()
if data_dir_path.is_dir():
    data_listing_resource = DirectoryResource(
        uri="resource://data-files",
        path=data_dir_path, # Path to the directory
        name="Data Directory Listing",
        description="Lists files available in the data directory.",
        recursive=False # Set to True to list subdirectories
    )
    mcp.add_resource(data_listing_resource) # Returns JSON list of files
```

**Common Resource Classes:**

-   `TextResource`: For simple string content.
-   `BinaryResource`: For raw `bytes` content.
-   `FileResource`: Reads content from a local file path. Handles text/binary modes and lazy reading.
-   `HttpResource`: Fetches content from an HTTP(S) URL (requires `httpx`).
-   `DirectoryResource`: Lists files in a local directory (returns JSON).
-   (`FunctionResource`: Internal class used by `@mcp.resource`).

Use these when the content is static or sourced directly from a file/URL, bypassing the need for a dedicated Python function.

## Defining Resource Templates

Resource Templates allow clients to request resources whose content depends on parameters embedded in the URI. Define a template using the **same `@mcp.resource` decorator**, but include `{parameter_name}` placeholders in the URI string and add corresponding arguments to your function signature.

```python
from fastmcp import FastMCP

mcp = FastMCP(name="DataServer")

# Template URI includes {city} placeholder
@mcp.resource("data://weather/{city}")
# Function accepts 'city' parameter matching the placeholder
def get_weather_for_city(city: str) -> dict:
    """Provides weather information for a specific city."""
    print(f"Server: Generating weather for city: {city}...")
    # In reality, call a weather API using the 'city' parameter
    temp = 20 + len(city) % 5 # Dummy logic
    condition = "Sunny" if len(city) % 2 == 0 else "Cloudy"
    return {"city": city.capitalize(), "temperature": temp, "unit": "celsius", "condition": condition}

# Template with an integer parameter
@mcp.resource("users://{user_id}/profile")
async def get_user_profile(user_id: int) -> dict:
    """Retrieves a user's profile information by ID."""
    print(f"Server: Generating profile for user ID: {user_id}...")
    # In reality, fetch from database using user_id
    # FastMCP uses Pydantic to auto-convert the string URI part to int
    if user_id == 1:
        return {"id": user_id, "name": "Alice", "email": "alice@example.com", "status": "active"}
    elif user_id == 2:
        return {"id": user_id, "name": "Bob", "email": "bob@example.com", "status": "inactive"}
    else:
        # Example of returning an error structure
        return {"error": f"User with ID {user_id} not found"}
```

**How Templates Work:**

1.  **Definition:** When FastMCP sees `{...}` placeholders in the `@resource` URI and matching function parameters, it registers a `ResourceTemplate`.
2.  **Discovery:** Clients list templates via `resources/listResourceTemplates`.
3.  **Request & Matching:** A client requests a specific URI, e.g., `data://weather/london`. FastMCP matches this to the `data://weather/{city}` template.
4.  **Parameter Extraction:** It extracts the parameter value: `city="london"`.
5.  **Type Conversion & Function Call:** It converts the extracted string `"london"` to the type hinted in the function (`str` in this case) and calls `get_weather_for_city(city="london")`. For `users://1/profile`, it converts `"1"` to `int` before calling `get_user_profile(user_id=1)`.
6.  **Response:** The function's return value is formatted (e.g., dict to JSON) and sent back as the content of the requested resource URI (`data://weather/london`).

Templates provide a powerful way to expose parameterized data access points following REST-like principles.

## Server Behavior

### Duplicate Resources

You can configure how the FastMCP server handles attempts to register multiple resources or templates with the same URI. Use the `on_duplicate_resources` setting during `FastMCP` initialization.

```python
from fastmcp import FastMCP
from fastmcp.settings import DuplicateBehavior

mcp = FastMCP(
    name="ResourceServer",
    on_duplicate_resources=DuplicateBehavior.ERROR # Raise error on duplicates
)

@mcp.resource("data://config")
def get_config_v1(): return {"version": 1}

# This registration attempt will raise a ValueError because
# "data://config" is already registered and the behavior is ERROR.
# @mcp.resource("data://config")
# def get_config_v2(): return {"version": 2}
```

The `DuplicateBehavior` enum options are:

-   `WARN` (default): Logs a warning, and the new resource/template replaces the old one.
-   `ERROR`: Raises a `ValueError`, preventing the duplicate registration.
-   `REPLACE`: Silently replaces the existing resource/template with the new one.
-   `IGNORE`: Keeps the original resource/template and ignores the new registration attempt.