MCP

MCP Resources👨‍💻

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.

Key Takeaways

  • 1Resources are read-only data endpoints identified by URIs. They return content with a MIME type (text/plain, application/json, etc.) and must not have side effects. The model can read resources to gather context before deciding which tools to invoke.
  • 2Static resources have fixed URIs (e.g., config://app, schema://database). They are registered with a specific URI string and always point to the same data source. Use them for configuration, schemas, and documentation.
  • 3Dynamic resources use URI templates with parameters (e.g., user://{userId}/profile, log://{date}/errors). The server defines a template pattern, and clients substitute parameters to access specific instances. A list callback enumerates available instances.
  • 4Resource subscriptions let clients watch for changes. When a subscribed resource updates, the server sends a notifications/resources/updated notification. The client can then re-read the resource to get the latest content. This is declared via the subscribe capability.
  • 5Resources return content as an array of items, each with a URI, MIME type, and either text content or base64-encoded binary data (blob). A single resource read can return multiple content items, which is useful for resources that aggregate data from several sources.
  • 6The resources/list method returns all available resources with their URIs, names, descriptions, and MIME types. Clients use this to present available resources to the user or to the AI model for context selection.

Examples

Static and dynamic resources in TypeScript

typescript

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.

Resources with FastMCP decorators in Python

python

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.

Reading resources from an MCP client in TypeScript

typescript

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.

Resource with binary content (images or files)

typescript

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 subscription for live updates

json

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.

Common Mistakes

Mistake:

Implementing side effects inside a resource handler (writing to a database, sending notifications, modifying files) instead of keeping resources read-only.

Fix:

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.

Mistake:

Using inconsistent or poorly structured URI schemes, making it hard for clients to discover and navigate resources programmatically.

Fix:

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.

Mistake:

Returning enormous resources (entire database tables, multi-megabyte log files) without pagination or size limits, causing memory issues and slow responses.

Fix:

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).

Mistake:

Not implementing the list callback for dynamic resource templates, so clients cannot discover which resource instances are available.

Fix:

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.

Mistake:

Sending resource update notifications too frequently (e.g., on every database write) which floods the client with notifications and degrades performance.

Fix:

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.

Best Practices

  • Choose resources over tools when the operation is purely data retrieval with no side effects. Resources have clearer semantics (read-only) and can be subscribed to for live updates, which tools cannot.
  • Use descriptive URI schemes that convey the resource type and hierarchy. Prefer config://database over data://1 -- the URI should tell you what you are reading before you read it.
  • Set accurate MIME types on resource contents. Use application/json for structured data, text/markdown for documentation, text/plain for logs, and image/png or image/jpeg for images. Clients use MIME types to render content correctly.
  • Implement the list callback for all dynamic resources. Even if the list is expensive to compute, cache the results. Discoverability is essential for both AI models and human users browsing available resources.
  • Keep individual resource responses under 1MB. If the underlying data is larger, truncate, summarize, or split it across multiple resources. Large responses slow down the protocol and may exceed client memory limits.
  • Use resource subscriptions for data that changes during a session (live metrics, log streams, configuration changes). For static data that rarely changes, subscriptions add unnecessary complexity.

Summary

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.

Practice MCP with hands-on challenges

Learn mcp resources hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master MCP with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.