Registration
You register a resource withserver.resource(config, handler) — the same config-first shape as every other primitive. FastMCP decides whether you’ve registered a static resource or a template by looking for a { in the URI string, so there is no separate method to learn. A plain URI is a static resource; a URI with a {param} placeholder is a template, and the extracted parameters are passed to your handler as an object.
server.ts
resources/list and are fetched with resources/read. Templates appear in resources/templates/list so a client knows the address shape it can fill in, and each concrete read is matched back to your template at request time using RFC 6570 extraction.
URI templates
A template lets one handler serve an open-ended set of addresses, and the placeholder style controls how much of the URI each parameter captures. The default captures a single path segment, which suits identifiers; a wildcard captures many segments, which suits file paths; and a query style captures query-string parameters, which suits search and filtering. Choose the style that matches the shape of the data you’re addressing.server.ts
Return values
Your handler returns a plain value and FastMCP converts it into an MCPReadResourceResult. The conversion mirrors the tool conversion but is tuned for data: strings become text, raw bytes become a base64 blob, and structured values become JSON. The MIME type is inferred from the return type, and you override it with the mimeType config field when the default is wrong.
When you need full control over the contents array — multiple parts, custom MIME types per part, or hand-built MCP output — return a
ResourceResult. It bypasses conversion so what you construct is exactly what the client receives.
server.ts
Subscriptions
A resource’s value can change after a client has read it, and subscriptions let the client find out instead of polling. Two sides meet here: your server announces that a resource changed, and a subscribed client re-reads it at its own pace. Announce a change from server code withserver.notifyResourceUpdated(uri). Call it whenever the data behind a URI changes — after a write, on a timer, or from an event handler. FastMCP delivers notifications/resources/updated to every connection that subscribed to that URI and nothing to the rest, so calling it for a URI with no subscribers is safe and cheap. The notification carries only the URI, never the new value, which keeps updates light; each client re-reads the fresh data when it is ready to use it.
server.ts
notifyResourceUpdated is the single change signal for both protocol eras. The wire transport differs underneath — a legacy client subscribes with the resources/subscribe and resources/unsubscribe RPCs, while a modern client opens one long-lived subscriptions/listen stream — but your server calls the one method and FastMCP routes the notification to each subscriber on its own era. The legacy subscribe and unsubscribe requests both run through your middleware chain, and subscribe honors a resource’s auth guard exactly as a read does.
Subscribing is a client-side action, so the subscribe API lives on the client. The client passes a handler that fires on each update.
client.ts
notifications/resources/updated for that URI. Because the notification names only the URI, the client reads the fresh value on its own schedule.
Variable completion
When a resource is a template, each RFC 6570 variable can suggest values as the client fills it in. Thecomplete config field maps a variable name to a completion callback, and a completion/complete request for that template routes to the matching callback. This lets a client offer real identifiers for {id} rather than expecting the user to already know them.
Each callback receives the partial value the user has typed and an optional context holding the template’s other resolved variables, and it returns a plain string[] or a CompletionResult with explicit total and hasMore hints. The callback may be async, so it can query a store. Completion applies to templates only — a static URI has no variables to complete.
server.ts
string[] to the 100-item wire maximum and computes total and hasMore for you. The same callback shape completes prompt arguments.
Configuration
Beyonduri and the display metadata, the config object accepts these fields. Each is optional and each shapes how the resource is advertised or read.
Dynamic registration
You can register a resource before or afterserver.run(). Adding one to a running server sends notifications/resources/list_changed to connected clients automatically, exactly as adding a tool sends the tools notification — the registration system is uniform across the three primitives.
server.ts