# What are MCP resources?
Source: https://speakeasy.com/mcp/core-concepts/resources

MCP resources are read-only, addressable content entities exposed by the server. They allow MCP clients to retrieve structured, contextual data (such as logs, configuration data, or external documents) that can be passed to models for reasoning. Because resources are strictly observational and not actionable, they must be deterministic, idempotent, and free of side effects.

Resources can expose:

- Log files
- JSON config data
- Real-time market stats
- File contents
- Structured blobs (for example, PDFs or images)

Resources are accessed via URI schemes like `note://`, `config://`, or `stock://`, and read using the `resources/read` method.

## Resource lifecycles and request and response formats

Each resource lifecycle follows this pattern:

1.  The server registers a static resource or URI template (for example, `stock://{symbol}/earnings`).
2.  The client calls `resources/list` to discover available resources or templates.
3.  The client sends a `resources/read` request with a specific resource URI.
4.  The server loads the content for that resource.
5.  The server returns the content as either `text` or `blob`.

Here is an example of a resource read request:

```json
{
  "method": "resources/read",
  "params": {
    "uri": "stock://AAPL/earnings"
  },
  "id": 8
}
```

The server responds with a text resource:

```json
{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "contents": [
      {
        "uri": "stock://AAPL/earnings",
        "mimeType": "application/json",
        "text": "{ \"fiscalDateEnding\": \"2023-12-31\", \"reportedEPS\": \"3.17\" }"
      }
    ]
  }
}
```

Resources can also return `blob` values for binary content like base64-encoded PDFs or images.

## Declaring a resource in Python

Resources are regular functions marked with decorators that define their role:

- `@list_resources()` exposes static resources. You return a list of `types.Resource` items for static URIs.
- `@list_resource_templates()` exposes dynamic resources. You return a list of `types.ResourceTemplate` items for dynamic, parameterized URIs.
- `@read_resource()` implements logic for resolving a resource by URI.

Here's an example using the Alpha Vantage API to return earnings data. Since we want to support arbitrary stock symbols, we'll use a resource template to let clients specify the symbol.

```python
import asyncio
from mcp.server import Server
from mcp import types
from mcp.server import stdio
from mcp.server import InitializationOptions, NotificationOptions
from pydantic import AnyUrl
import requests

app = Server("stock-earnings-server")

@app.list_resource_templates()
async def list_earnings_resources_handler(symbol: str) -> list[types.ResourceTemplate]:
    return [
        types.ResourceTemplate(
            uriTemplate="stock://{symbol}/earnings",
            name="Stock Earnings",
            description="Quarterly and annual earnings for a given stock symbol",
            mimeType="application/json"
        )
    ]

@app.read_resource()
async def read_earnings_resource_handler(uri: AnyUrl) -> str:
    parsed = str(uri)

    if not parsed.startswith("stock://") or not parsed.endswith("/earnings"):
        raise ValueError("Unsupported resource URI")

    symbol = parsed.split("://")[1].split("/")[0].upper()

    url = (
        "https://www.alphavantage.co/query"
        "?function=EARNINGS"
        f"&symbol={symbol}"
        "&apikey=demo"
    )

    response = requests.get(url)
    return response.text
```

In the example above, we:

- **Declare a resource template `stock://{symbol}/earnings`** that clients can use to request dynamic resources for any stock symbol.
- **Use `@list_resource_templates` to expose the template** to clients, which tells the agent how to construct valid URIs.
- **Use `@read_resource` to handle execution**, where we fetch real-time earnings from the Alpha Vantage API for the provided stock symbol.
- **Return a valid JSON text response**, which is wrapped by MCP in a `TextContent` resource and can be used as context for prompts or decisions.

If the resource was static – for example, if the client only tracks Apple stock – we'd use the `@list_resources` decorator and return a list of `types.Resource` items instead:

```python
...
@app.list_resources()
async def list_apple_earnings_resource() -> list[types.Resource]:
    return [
        types.Resource(
            uri="stock://AAPL/earnings",
            name="Apple Earnings",
            description="Quarterly and annual earnings for Apple Inc.",
            mimeType="application/json"
        )
    ]
```

## Best practices and pitfalls to avoid

Here are some best practices for implementing MCP resources:

- Use clear, descriptive URI schemes like `note://`, `stock://`, or `config://`.
- Keep resource data consistent and read-only.
- Validate inputs or file paths to prevent injections or errors.
- Set correct MIME types so clients can parse content properly.
- Support dynamic resources with URI templates.

Here are some pitfalls to avoid:

- Treating resources as action triggers (use [tools](/mcp/tools) instead).
- Returning extremely large payloads (use pagination instead).
- Exposing sensitive data (unless scoped by [roots](/mcp/roots) or authenticated context).
- Relying on global state (unless explicitly isolated per session).

MCP resources are intended for structured, factual, and often cached information. They are perfect for background context, factual grounding, or real-world reference material.

## Resource annotations

Resources can include optional annotations that provide additional metadata to guide clients on how to use them:

```python
from mcp.server import Server
from mcp import types

app = Server("annotated-resources-server")

@app.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri="docs://company/earnings-2024.pdf",
            name="2024 Earnings Report",
            mimeType="application/pdf",
            annotations={
                # Specify intended audience (user, assistant, or both)
                "audience": ["user", "assistant"],
                # Importance ranking from 0 (least) to 1 (most)
                "priority": 0.8
            }
        )
    ]
```

Annotations help clients decide:

- **Which audience** should see the resource (the `user`, the `assistant`, or both).
- **How important** the resource is (on a scale of `0.0` to `1.0`).

Clients can use annotations to sort, filter, or highlight resources appropriately, but annotations are hints, not guarantees of behavior.

## Pagination support

When dealing with large sets of resources, MCP supports pagination using cursors:

```python
from mcp.server import Server
from mcp import types

app = Server("paginated-resources-server")

@app.list_resources()
async def list_resources(cursor: str = None) -> tuple[list[types.Resource], str]:
    # Fetch resources from your backend, database, etc.
    all_resources = fetch_all_resources()

    # Implementation of pagination
    page_size = 10
    start_index = 0

    if cursor:
        # Parse cursor to get starting position
        start_index = int(cursor)

    # Get the current page of resources
    current_page = all_resources[start_index:start_index + page_size]

    # Calculate next cursor if there are more items
    next_cursor = None
    if start_index + page_size < len(all_resources):
        next_cursor = str(start_index + page_size)

    return current_page, next_cursor
```

When implementing pagination:

- Remember the `cursor` parameter can be any string, but typically encodes a position.
- Return a tuple with both resources and the next cursor.
- Return `None` for the cursor when there are no more pages.
- Keep cursor values opaque to clients. They shouldn't assume structure.
- Handle invalid cursors gracefully.

Pagination helps manage memory usage for large resource collections and improves client responsiveness.

## Resources vs tools

MCP resources are **read-only** and addressable via URIs like `note://xyz` or `stock://AAPL/earnings`. They are designed to preload context into the agent's working memory or support summarization and analysis workflows.

MCP tools are **actionable** and invoked by the client with parameters to perform an action like writing a file, placing an order, or creating a task.

To avoid decision paralysis, define resources according to **what the client should know** and tools according to **what the client can do**.

## Practical implementation example

In a WhatsApp MCP server, you could expose all image attachments that the server downloads as resources. This way, the MCP client can access the images without having to call a tool every time. The MCP client can simply request the resource by its URL, and the MCP server will return the image data.

Here's how to implement a resource using the TypeScript SDK:

```typescript
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { MediaRegistry } from "whatsapp";

mcpServer.resource(
  "media",
  new ResourceTemplate("media://{messageId}", {
    list: () => {
      const allMedia = mediaRegistry.getAllMediaItems();
      const resources = [];
      for (const { mediaItems } of allMedia) {
        for (const mediaItem of mediaItems) {
          resources.push({
            uri: `media://${mediaItem.messageId}`,
            name: mediaItem.name,
            description: mediaItem.description,
            mimeType:
              mediaItem.mimetype !== "unknown" ? mediaItem.mimetype : undefined,
          });
        }
      }
      return { resources };
    },
  }),
  async (uri: URL, { messageId }) => {
    const messageIdRaw = Array.isArray(messageId) ? messageId[0] : messageId;
    const messageIdString = decodeURIComponent(messageIdRaw);
    const mediaData =
      await messageService.downloadMessageMedia(messageIdString);

    return {
      contents: [
        {
          uri: uri.href,
          blob: mediaData.data,
          mimeType: mediaData.mimetype,
        },
      ],
    };
  },
);
```

This code defines a resource called `media` that can be accessed by the MCP client. The resource has a `list` method that returns a list of all media items. The MCP client can then request a specific media item by its URI, and the MCP server will return the media data.

### Notifying clients about resource changes

The MCP server can send a `notifications/resources/list_changed` message to notify the MCP client that the list of resources has changed:

```typescript
import { MediaItem, MediaRegistry } from "whatsapp";

// When the MCP server detects that a new media item has been added
mediaRegistry.onMediaListChanged((mediaItems: MediaItem[]) => {
  mcpServer.sendResourceListChanged();
});
```

You can also send a notification when a specific resource has changed:

```typescript
mediaRegistry.onMediaItemChanged((mediaItem: MediaItem) => {
  void mcpServer.server.sendResourceUpdated({
    uri: resourceUri,
  });
});
```

How the LLM client handles resources is up to the client implementation. The MCP server just needs to expose the resources and notify the client when they change.