Skip to main content
Resources are the data primitive: read-only values the client fetches by URI. Where a tool runs code and a prompt seeds a conversation, a resource answers a read — the client asks for a URI and your handler returns what lives there. A resource is addressed rather than called, which is why it carries a URI instead of an input schema, and why a single registration can serve either one fixed address or a whole family of them through a template.

Registration

You register a resource with server.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
The two kinds surface differently to clients. Static resources appear in 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 MCP ReadResourceResult. 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 with server.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
The handler fires whenever the server sends 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. The complete 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
FastMCP caps a bare string[] to the 100-item wire maximum and computes total and hasMore for you. The same callback shape completes prompt arguments.

Configuration

Beyond uri 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 after server.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