Resources and resource templates

Model Context Protocol (MCP) · Practical · 10 min

Resources let hosts attach your data as context without spending a tool call.

A resource is data the host can read and add to the model's context. Each one has a URI, a unique address. You choose the scheme (the part before ://): config://app, notes://index, file:///....

@mcp.resource("notes://index") registers a fixed resource. Clients find it with resources/list and read it with resources/read.

Put placeholders in the URI and it becomes a resource template: @mcp.resource("notes://{title}") maps {title} to the function's title parameter. Templates are listed separately (resources/templates/list) because nothing exists to list until someone supplies a value. SDK v2 follows the URI template standard (RFC 6570) strictly and, by default, rejects values that try path tricks such as ../.

The content type matters. Set mime_type= (a standard label for the data format, like text/markdown, application/json, image/png). Return str for text, bytes for binary data, or a dict or list to send JSON.

When to use a resource instead of a tool:

  • the data is read-only and identified by an address,
  • the user or app should choose to attach it (a file, a schema, a log),
  • you want it cacheable.

If the model itself needs to decide to fetch it mid-task, offer a tool too; many hosts don't let the model pick resources on its own. Servers can also notify subscribed clients when a resource changes, covered later.

Example

import json
from mcp.server import MCPServer

mcp = MCPServer("Shop")
PRODUCTS = {"p1": {"name": "Mug", "price": 12}}


@mcp.resource("shop://products", mime_type="application/json")
def products() -> str:
    """All products as JSON."""
    return json.dumps(PRODUCTS)


@mcp.resource("shop://products/{product_id}")
def product(product_id: str) -> str:
    """One product, as readable text."""
    p = PRODUCTS[product_id]
    return f"{p['name']}: ${p['price']}"

# Client side:
#   await client.list_resources()          -> shop://products
#   await client.list_resource_templates() -> shop://products/{product_id}
#   await client.read_resource("shop://products/p1")  -> "Mug: $12"

Remember

  • Resources are read-only data at a URI you design
  • A {placeholder} in the URI makes a resource template
  • Set mime_type; return str, bytes, or JSON-able data
  • Pair with a tool when the model must fetch it itself

Check yourself

  1. Why does notes://{title} not appear in resources/list?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: py.sdk.modelcontextprotocol.io/servers/resources/

Builds on

Unlocks

Nothing builds on this yet.