Resources are the read-only data primitive in MCP. While tools perform actions with side effects, resources simply expose data for AI models to read and reason about. Each resource is identified by a URI (like file:///path/to/file or db://users/123) and returns content with a MIME type. Resources are analogous to GET endpoints in a REST API -- they retrieve data without modifying state. MCP supports two types of resources: static resources at fixed URIs and dynamic resources defined with URI templates that accept parameters. Clients discover available resources via resources/list, read them via resources/read, and can optionally subscribe to changes via resources/subscribe. Resources are ideal for exposing configuration files, database schemas, documentation, log files, and any other data the model needs for context.
Static resources use a fixed URI string, while dynamic resources use ResourceTemplate with parameter placeholders. The list callback for dynamic resources returns the available instances so clients can discover what is available. The handler receives the parsed URI and extracted parameters (userId) as arguments.
The @mcp.resource() decorator takes a URI or URI template as its argument. For static resources, use a plain URI string. For dynamic resources, use curly braces for parameters -- the function parameter names must match the template placeholders. The function's docstring becomes the resource description. Return a string for text content.
The client discovers resources via listResources, then reads specific resources by URI. For dynamic resources, the client substitutes template parameters directly in the URI. The result contains a contents array -- each item has the URI, MIME type, and text or blob data. This pattern lets a client scan available resources before selecting which ones to read.
Resources can return either text content (via the text field) or binary content (via the blob field with base64 encoding). Text is used for documents, configs, logs, and structured data. Binary is used for images, PDFs, and other non-text files. The MIME type tells the client how to interpret the content.
Resource subscriptions allow clients to be notified when data changes without polling. The client subscribes to a URI, and the server sends a notification when the resource updates. The client then re-reads the resource to get the new content. This is useful for monitoring dashboards, log tailing, and live configuration. The server must declare the subscribe capability during initialization.
Implementing side effects inside a resource handler (writing to a database, sending notifications, modifying files) instead of keeping resources read-only.
Resources must be read-only. If an operation modifies state, define it as a tool instead. Resources are for data retrieval only -- think of them as GET requests that should be safe to call repeatedly without consequences.
Using inconsistent or poorly structured URI schemes, making it hard for clients to discover and navigate resources programmatically.
Define a consistent URI scheme for your server: use a custom protocol prefix (db://, config://, log://) and a logical path hierarchy. Document the scheme so clients know what URIs to expect.
Returning enormous resources (entire database tables, multi-megabyte log files) without pagination or size limits, causing memory issues and slow responses.
Limit resource size to what is useful for the model. For large datasets, return summaries or the most recent entries. For log files, return the last N lines. Consider using dynamic resources with parameters for pagination (e.g., logs://{date}?page=1).
Not implementing the list callback for dynamic resource templates, so clients cannot discover which resource instances are available.
Always implement the list callback for ResourceTemplate. Without it, clients must guess valid parameter values. The list returns concrete URIs and names that clients can enumerate and present to users or AI models.
Sending resource update notifications too frequently (e.g., on every database write) which floods the client with notifications and degrades performance.
Debounce or throttle resource update notifications. Batch rapid changes into a single notification after a short delay (100-500ms). For high-frequency data, consider updating only on a fixed interval rather than every change.
MCP resources are read-only data endpoints identified by URIs that expose information to AI models without side effects. Static resources live at fixed URIs for configuration and schemas; dynamic resources use URI templates with parameters for collections like user profiles and log files. Resources return content with MIME types as text or base64-encoded binary data. Subscriptions enable live update notifications when resource data changes. In TypeScript, register resources with McpServer.registerResource and ResourceTemplate; in Python, use the @mcp.resource() decorator with URI templates. Keep resources focused, size-limited, and always implement list callbacks for dynamic resources.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.