Meet Prefect Horizon, the enterprise MCP gateway built by the team behind FastMCP.

These are the docs for FastMCP 2.0. FastMCP 3.0 is now available.

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.

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.

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.

Decorator Arguments

You can customize the resource’s properties using arguments in the @mcp.resource decorator:

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
    meta={"version": "2.1", "team": "infrastructure"}  # Custom metadata
)
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

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.

Disabling Resources

You can control the visibility and availability of resources and templates by enabling or disabling them. Disabled resources will not appear in the list of available resources or templates, and attempting to read a disabled resource will result in an “Unknown resource” error. By default, all resources are enabled. You can disable a resource upon creation using the enabled parameter in the decorator:

@mcp.resource("data://secret", enabled=False)
def get_secret_data():
    """This resource is currently disabled."""
    return "Secret data"

Accessing MCP Context

Resources and resource templates can access additional MCP information and features through the Context object. To access it, add a parameter to your resource function with a type annotation of Context:

from fastmcp import FastMCP, Context

mcp = FastMCP(name="DataServer")

@mcp.resource("resource://system-status")
async def get_system_status(ctx: Context) -> dict:
    """Provides system status information."""
    return {
        "status": "operational",
        "request_id": ctx.request_id
    }

Async 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.

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 using mcp.add_resource() and concrete Resource subclasses.

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)

Notifications

FastMCP automatically sends notifications/resources/list_changed notifications to connected clients when resources or templates are added, enabled, or disabled. This allows clients to stay up-to-date with the current resource set without manually polling for changes.

@mcp.resource("data://example")
def example_resource() -> str:
    return "Hello!"

# These operations trigger notifications:
mcp.add_resource(example_resource)  # Sends resources/list_changed notification
example_resource.disable()          # Sends resources/list_changed notification
example_resource.enable()           # Sends resources/list_changed notification

Annotations

FastMCP allows you to add specialized metadata to your resources through annotations. These annotations communicate how resources behave to client applications without consuming token context in LLM prompts. You can add annotations to a resource using the annotations parameter in the @mcp.resource decorator:

@mcp.resource(
    "data://config",
    annotations={
        "readOnlyHint": True,
        "idempotentHint": True
    }
)
def get_config() -> dict:
    """Get application configuration."""
    return {"version": "1.0", "debug": False}

FastMCP supports these standard annotations:

Annotation Type Default Purpose
readOnlyHint boolean true Indicates if the resource only provides data without side effects
idempotentHint boolean true Indicates if repeated reads have the same effect as a single read

These components showcase how resources and templating work within the FastMCP framework.