Tutorials Logic, IN info@tutorialslogic.com

MCP Resources: URI Design, Templates, Pagination, and Resource Scale

MCP Resources

Resources are the read surface of MCP. They let a host fetch stable, readable context without overloading tools with content delivery responsibilities.

Good resource design starts with URI strategy, then scales into templates, pagination, metadata, and update notifications.

Mental Model

A resource should feel like a durable address for content. If a host wants to fetch it tomorrow or reference it in a tool result, the identifier should still make sense.

Resource Anatomy

A resource typically includes a URI, name, optional title and description, MIME type, and sometimes size metadata. That combination tells the host both what the thing is and how expensive it may be to read or include in context.

The server decides how the URI scheme maps to actual storage. The spec does not force business-domain URIs to look like file paths, which means you can design identifiers that reflect your domain rather than your backend internals.

  • Use domain-driven URI schemes such as policy://, repo://, report://, or ticket://
  • Include MIME type whenever the client benefits from rendering hints
  • Treat resource URIs as public contract surfaces inside the protocol

Resource Templates and Dynamic Addressing

Not every resource can be listed exhaustively up front. Dynamic domains such as user profiles, reports by date, or repository files often need URI templates. MCP resource templates let the server describe a family of resources without pre-registering every instance.

Templates should still be bounded. A server that exposes users://{userId}/profile should validate the parameter and enforce authorization before reading actual content.

  • Templates are ideal for dynamic but structurally predictable resources
  • Template parameters need the same validation discipline as tool parameters
  • Dynamic resources should not bypass audit and authorization logic

Resource Scale and Context Strategy

Resources frequently represent content that is too large to dump into every model request. Hosts need enough metadata to decide whether to fetch, summarize, chunk, or ignore the content.

That is why size and MIME type matter. A 2 KB markdown policy and a 40 MB binary artifact should not be treated the same way by a host, even if both are technically resources.

  • Use resources for context, not for uncontrolled data exfiltration
  • Paginate large lists of resources
  • Prefer resource links from tools when the host should fetch on demand

Design Resource URIs Like Product Contracts

A resource URI is more than a pointer. It is a stable contract that tells the host how to request context safely and tells the server what authorization, pagination, freshness, and formatting rules apply. Poor resource design often looks convenient at first, such as exposing arbitrary backend URLs or raw database identifiers, but it becomes hard to secure and hard for the model to understand.

Use predictable URI families that match user concepts. A policy server might expose `policy://handbook/{section}`, `policy://rule/{rule_id}`, and `policy://search/{query}`. A repository server might expose `repo://{workspace}/file/{path}` only inside approved roots. Each URI pattern should have clear ownership, allowed parameters, error behavior, and permission checks.

Versioning matters when resources become long-lived references in prompts, traces, or saved workflows. If content can change, include metadata such as last modified time, version, etag, or source revision. The model should know whether it is reading a current document, a historical snapshot, or a summarized representation.

  • Keep URI schemes domain-specific and readable.
  • Avoid exposing arbitrary filesystem paths or backend URLs directly.
  • Validate template parameters before reading data.
  • Attach metadata for freshness, size, owner, and source version.
  • Return clear no-access and not-found errors without leaking hidden resource names.

Pagination, Summaries, and Large Content

Resources often fail in production because they are too large. A host may discover a resource successfully, but reading the entire object can blow through context limits, increase cost, or expose unnecessary data. Treat large resources as collections, windows, or summaries rather than single giant blobs.

A good server offers progressive access. First return metadata and a compact preview. Then allow the host to request a page, range, section, or specific version. If the user asks a broad question, combine resources with a search tool that returns ranked resource references rather than embedding every possible document in one response.

  • Prefer metadata-first discovery for large collections.
  • Use pagination or byte/section ranges for long documents.
  • Provide summaries as separate resources when they are generated and versioned.
  • Keep raw content, summaries, and search results distinct.
  • Audit reads for sensitive resource families.

Resource Discovery Should Be Safe and Useful

Resource discovery is where the host learns what context exists. It should help the model find useful information without revealing sensitive names, internal topology, or unauthorized records. A resource that cannot be read by the current user often should not be discoverable by that user either.

Use resource templates for predictable families and concrete resources for important known objects. Templates make dynamic access possible, but every template parameter must be validated. If a template accepts a file path, normalize it and check containment. If it accepts an ID, check tenant and object-level permissions before returning content.

Resources should describe content type and expected size. The host can make better decisions when it knows whether a resource is text, JSON, binary metadata, a document section, or a large collection. Size and freshness metadata also help the host avoid stuffing inappropriate content into the model context.

Discovery should be fast. If listing resources requires expensive backend scans, introduce indexes, pagination, filters, or search tools. Slow discovery makes every host feel unreliable even when individual reads work.

  • Filter discovery by permission and tenant.
  • Validate template parameters before lookup.
  • Expose content type, freshness, and size metadata.
  • Use pagination or search for large collections.
  • Avoid leaking hidden resource names in errors.

Resource Reads Need Provenance and Bounds

Reading a resource should return bounded, meaningful context. If the resource is too large, return a section, range, summary, or page instead of dumping everything. The model needs enough evidence to answer, not every byte the backend can provide.

Provenance is the difference between useful context and anonymous text. Include the source URI, version, document title, section, timestamp, and any relevant access context. Later, when an answer cites a fact, the system can trace it back to the exact resource read.

Resources can contain adversarial instructions. A policy document, issue comment, README, or webpage may tell the model to ignore rules. The host and prompt should label resource content as evidence, not instructions. Server-side permissions and host-side policy remain in charge.

For production systems, audit sensitive reads. Even read-only context can be confidential. Track who read which resource, through which host, for which run, and whether the content was used in a final answer.

  • Return excerpts, pages, or sections for large resources.
  • Attach source URI and version metadata.
  • Label resource content as untrusted evidence.
  • Audit reads for sensitive resource families.
  • Test oversized and malformed backend responses.

Resource Review Exercise

Pick one resource family and review its full lifecycle: discovery, read, pagination, authorization, caching, citation, and deletion. This exercise catches the difference between exposing data and exposing safe context. The model needs useful evidence, not unrestricted backend access.

Check what a user can learn from discovery alone. Resource names, IDs, titles, and counts can reveal sensitive information even before content is read. If discovery is sensitive, filter it with the same seriousness as read access.

Then check what happens when content is large, stale, missing, or unauthorized. The server should return bounded results and clear status without leaking hidden objects or internal implementation details.

  • Review discovery leakage separately from read leakage.
  • Preserve provenance for citations.
  • Use pagination and summaries for large objects.
  • Test unauthorized, stale, and missing resources.

Static Resource Registration

Static Resource Registration
server.registerResource(
  'password-policy',
  'policy://security/password-rotation',
  {
    title: 'Password Rotation Policy',
    description: 'Official security policy for password rotation.',
    mimeType: 'text/markdown',
  },
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: await loadPolicyMarkdown('password-rotation'),
      },
    ],
  })
);
  • The URI is business-domain specific, not tied to a filesystem path.
  • The resource remains read-only even if the backing store is complex.

Resource Template for Dynamic Profiles

Resource Template for Dynamic Profiles
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';

server.registerResource(
  'user-profile',
  new ResourceTemplate('users://{userId}/profile', { list: undefined }),
  {
    title: 'User Profile',
    mimeType: 'application/json',
  },
  async (uri, { userId }) => ({
    contents: [
      {
        uri: uri.href,
        text: JSON.stringify(await loadApprovedUserProfile(userId)),
      },
    ],
  })
);
  • Template parameters should still be validated and authorized in the loader path.
  • The returned content is text JSON, which many hosts can display or summarize easily.
Key Takeaways
  • Design URIs as stable protocol identifiers.
  • Expose MIME type and size metadata where useful.
  • Use templates for dynamic families, not ad hoc string parsing everywhere.
  • Make large content fetchable on demand instead of always embedding it.
Common Mistakes to Avoid
Using resources for operations that really should be tools.
Encoding backend implementation details directly into public resource URIs.
Returning huge content bodies without giving hosts any metadata to make good decisions.

Practice Tasks

  • Design a URI scheme for policy documents, deployment runbooks, and incident timelines.
  • Add one dynamic resource template and describe the auth rule attached to it.
  • Write down when a tool should return a resource link instead of embedding the full content.

Frequently Asked Questions

Yes. Resources can return binary blobs, typically base64-encoded, with the correct MIME type.

Not necessarily. Dynamic template-backed resources may be discoverable through templates rather than a full concrete list.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.