MCP Framework
Resources

Resources Overview

Managing external data sources and APIs with MCP Framework resources

Resources

What are Resources?

Resources are data sources that AI models can read or subscribe to. Think of them as a way to provide context, data, or state to your AI interactions!

Understanding Resources

Resources can be:

  • Files
  • API endpoints
  • Database queries
  • Real-time data streams
  • Configuration data

Here's a simple example:

import { MCPResource } from "mcp-framework";

class ConfigResource extends MCPResource {
  uri = "resource://config";
  name = "Configuration";
  description = "System configuration settings";
  mimeType = "application/json";

  async read() {
    return [
      {
        uri: this.uri,
        mimeType: this.mimeType,
        text: JSON.stringify({
          version: "1.0.0",
          environment: "production",
          features: ["analytics", "reporting"],
        }),
      },
    ];
  }
}

Creating Resources

Using the CLI

mcp add resource my-resource

This creates a new resource in src/resources/MyResource.ts.

Resource Structure

Every resource has:

  1. Metadata
uri = "resource://my-data";
name = "My Data Resource";
description = "Provides access to my data";
mimeType = "application/json";
  1. Read Method
async read(): Promise<ResourceContent[]> {
  // Fetch or generate your data
  return [{
    uri: this.uri,
    mimeType: this.mimeType,
    text: JSON.stringify(data)
  }];
}

Resource Types

Static Resources

class DocumentationResource extends MCPResource {
  uri = "resource://docs";
  name = "Documentation";
  mimeType = "text/markdown";

  async read() {
    return [
      {
        uri: this.uri,
        mimeType: this.mimeType,
        text: "# API Documentation\n\nWelcome to our API...",
      },
    ];
  }
}

Dynamic Resources

class MarketDataResource extends MCPResource {
  uri = "resource://market-data";
  name = "Market Data";
  mimeType = "application/json";

  async read() {
    const data = await this.fetch("https://api.market.com/latest");
    return [
      {
        uri: this.uri,
        mimeType: this.mimeType,
        text: JSON.stringify(data),
      },
    ];
  }
}

Real-time Resources

Real-time Updates

Use subscription methods to handle real-time data streams!

class StockTickerResource extends MCPResource {
  uri = "resource://stock-ticker";
  name = "Stock Ticker";
  mimeType = "application/json";
  private ws: WebSocket | null = null;

  async subscribe() {
    this.ws = new WebSocket("wss://stocks.example.com");
    this.ws.on("message", this.handleUpdate);
  }

  async unsubscribe() {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }

  async read() {
    const latestData = await this.getLatestStockData();
    return [
      {
        uri: this.uri,
        mimeType: this.mimeType,
        text: JSON.stringify(latestData),
      },
    ];
  }
}

Title, Icons, Size, and Annotations

Resources now support additional metadata fields per MCP spec 2025-11-25, enabling richer display in client UIs and providing behavioral hints to clients.

import { MCPResource } from "mcp-framework";

class ProjectResource extends MCPResource {
  uri = "resource://project/readme";
  name = "README";
  title = "Project Documentation";  // Human-readable display name
  description = "Project README file";
  mimeType = "text/markdown";
  size = 4096;  // Optional: size in bytes

  // Optional: icons for client UI
  icons = [{ src: "https://example.com/doc-icon.png", mimeType: "image/png" }];

  // Optional: annotations for client behavior hints
  resourceAnnotations = {
    audience: ["user", "assistant"],  // Who this is for
    priority: 0.8,                    // 0.0 (optional) to 1.0 (critical)
    lastModified: "2025-01-12T15:00:58Z",
  };

  async read() {
    return [{
      uri: this.uri,
      mimeType: this.mimeType,
      text: "# My Project\n\nProject documentation...",
    }];
  }
}

Field Reference

  • title — Optional human-readable name for display in client UIs. Falls back to name if not set.
  • icons — Optional array of icon objects with src (URL or data URI), optional mimeType, and optional sizes.
  • size — Optional size in bytes. This is a hint; actual content returned from read() may differ.
  • resourceAnnotations — Optional metadata hints for client behavior:
    • audience — Array of "user" and/or "assistant" indicating who the resource is intended for.
    • priority — Number from 0.0 to 1.0 indicating importance (1.0 = most important, 0.0 = least important).
    • lastModified — ISO 8601 timestamp of the last modification.

Annotations Are Hints

Annotations are purely informational. Clients may use them for display ordering, filtering, or UI hints, but they do not enforce any behavior.

Resource Templates with Metadata

When using resource templates, title and icons defined on the class also apply to the template definition:

class FileResource extends MCPResource {
  uri = "resource://files/{path}";
  name = "Project Files";
  title = "Project File Browser";
  icons = [{ src: "https://example.com/file-icon.png", mimeType: "image/png" }];

  protected template = {
    uriTemplate: "resource://files/{path}",
    description: "Access project files",
  };
  // title and icons on the class also apply to the template definition

  async read() {
    // ...
  }
}

Resource Discovery & Listing

Automatic Listing

You don't need to implement any listing logic yourself. The framework automatically handles the MCP resources/list protocol method for all discovered resources.

How It Works

  1. Place your resource classes in src/resources/ (nested subdirectories are supported)
  2. Export each class as the default export
  3. The framework discovers all resources at startup and registers them

When an MCP client calls resources/list, the framework returns the resourceDefinition of every registered resource — including uri, name, description, mimeType, and any optional fields like title, icons, size, or annotations.

Example

Given these resource files:

src/resources/ConfigResource.ts
src/resources/api/MarketDataResource.ts

An MCP client calling resources/list receives both automatically:

{
  "resources": [
    {
      "uri": "resource://config",
      "name": "Configuration",
      "description": "System configuration settings",
      "mimeType": "application/json"
    },
    {
      "uri": "resource://market-data",
      "name": "Market Data",
      "description": "Live market data",
      "mimeType": "application/json"
    }
  ]
}

Listing Resource Templates

If your resource defines a template property, it will also appear in resources/templates/list:

class ItemResource extends MCPResource {
  uri = "resource://items/{id}";
  name = "Items";
  description = "Access items by ID";
  mimeType = "application/json";

  protected template = {
    uriTemplate: "resource://items/{id}",
    description: "Retrieve a specific item by its ID",
  };

  async read() {
    return [{ uri: this.uri, mimeType: this.mimeType, text: JSON.stringify({ id: "1" }) }];
  }
}

Programmatic Registration

You can also register resources programmatically using addResource() before calling start():

import { MCPServer } from "mcp-framework";

const server = new MCPServer({ name: "my-server", version: "1.0.0" });

server.addResource(ConfigResource);
server.addResource(MarketDataResource);

await server.start();

Programmatic and auto-discovered resources are merged. If both define the same URI, the programmatic registration takes precedence.

Best Practices

  1. URI Naming
uri = "resource://domain/type/identifier";
// Example: "resource://finance/stocks/AAPL"
  1. Error Handling
async read() {
  try {
    const data = await this.fetchData();
    return [{
      uri: this.uri,
      mimeType: this.mimeType,
      text: JSON.stringify(data)
    }];
  } catch (error) {
    throw new Error(`Failed to read resource: ${error.message}`);
  }
}
  1. Caching
class CachedResource extends MCPResource {
  private cache: any = null;
  private lastFetch: number = 0;
  private TTL = 60000; // 1 minute

  async read() {
    if (this.cache && Date.now() - this.lastFetch < this.TTL) {
      return this.cache;
    }

    const data = await this.fetchFreshData();
    this.cache = data;
    this.lastFetch = Date.now();
    return data;
  }
}

Advanced Usage

Combining with Tools

class DataResource extends MCPResource {
  uri = "resource://data";
  name = "Data Store";

  async read() {
    return [
      {
        uri: this.uri,
        mimeType: "application/json",
        text: JSON.stringify(await this.getData()),
      },
    ];
  }
}

class DataProcessor extends MCPTool {
  async execute(input) {
    const resource = new DataResource();
    const [data] = await resource.read();
    return this.processData(JSON.parse(data.text));
  }
}

Next Steps