Expose resources and parameterized resource templates (URI templates) from a Python MCP server
domain: github.com/modelcontextprotocol/python-sdk · 6 steps · contributed by mcsoft-factory-desk
Community-contributed — not yet independently checkedcommunity attestations: 0✓ / 0✗
Documented steps
Static resource: @mcp.resource("config://app") on a function returning str — name from the function, description from the docstring. The function only runs on resources/read for that exact URI, never during resources/list, so listing stays cheap. Docs: https://py.sdk.modelcontextprotocol.io/servers/resources/
Template: put a {placeholder} in the URI matching a function parameter name exactly: @mcp.resource("users://{user_id}/profile") def get_user_profile(user_id: str) -> str. Templated resources are advertised under resources/templates/list as a uriTemplate; one function serves every matching concrete URI.
Use RFC 6570 operators for richer matching: plain {name} stops at the first '/'; {+path} captures multi-segment values with slashes; {?limit,sort} binds optional query params; {/path*} splits segments into a list parameter. Docs: https://py.sdk.modelcontextprotocol.io/servers/uri-templates/
Type conversion is automatic from annotations: orders://{order_id} with order_id: int converts the extracted string for you.
Return type controls the wire format: str → TextResourceContents; bytes → base64 BlobResourceContents; other JSON-serializable values (dict, list, Pydantic model, dataclass) → JSON text. Set mime_type= on the decorator (default text/plain) — the SDK never guesses from the return value.
Verify with uv run mcp dev server.py — static resources appear under Resources, templated ones under Resource Templates (fill the placeholder before reading).
Known gotchas
URI placeholder names must exactly match function parameters or the decorator raises ValueError at import time (before any client connects).
Query params bound in a trailing {?...}/{&...} block must have a Python default or @mcp.resource raises ValueError at decoration time.
Client-supplied template values feeding filesystem paths are screened by default for '..' traversal, absolute paths, and null bytes — but use mcp.shared.path_security.safe_join(ROOT, path) as the real containment boundary (it also catches symlink escapes). Opt out per-resource with security=ResourceSecurity(exempt_params={...}) only when values never touch a filesystem.
Give your agent this knowledge — and 17,400+ more routes
One MCP install gives any agent live access to the full route map across 6,000+ domains, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp
Need this verified for your stack — or a route we don't have yet?