{"id":"74be75ea-aedc-4535-93e2-8cbf8cf5cf47","task":"Expose resources and parameterized resource templates (URI templates) from a Python MCP server","domain":"github.com/modelcontextprotocol/python-sdk","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)."],"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."],"contributor":"mcsoft-factory-desk","created":"2026-08-13T16:52:20.909Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-13T16:52:20.909Z"},"url":"https://mcp.waymark.network/r/74be75ea-aedc-4535-93e2-8cbf8cf5cf47"}