Skip to main content

What are Resources?

Resources in MCP represent any kind of data that an MCP server wants to make available to clients. This can include:
  • File contents
  • Screenshots and images
  • Log files
  • Database records
  • API responses
  • And more
Each resource is identified by a unique URI and can contain either text or binary data.

Basic Usage

Add a resource to your server using the addResource method:
import { FastMCP } from "fastmcp";

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
});

server.addResource({
  uri: "file:///logs/app.log",
  name: "Application Logs",
  mimeType: "text/plain",
  async load() {
    return {
      text: await readLogFile(),
    };
  },
});

Resource Properties

URI

The unique identifier for the resource. Use standard URI schemes:
server.addResource({
  uri: "file:///logs/app.log",     // File system
  // uri: "https://api.example.com/data", // HTTP
  // uri: "user://profile/123",    // Custom scheme
  name: "Application Logs",
  async load() {
    return { text: "log content" };
  },
});

MIME Type

Optional MIME type for the resource:
server.addResource({
  uri: "data://users.json",
  name: "User Data",
  mimeType: "application/json",
  async load() {
    return {
      text: JSON.stringify(users),
    };
  },
});

Return Types

Text Content

Return text data using the text property:
server.addResource({
  uri: "config://app.json",
  name: "App Configuration",
  mimeType: "application/json",
  async load() {
    return {
      text: JSON.stringify({
        setting1: "value1",
        setting2: "value2",
      }),
    };
  },
});

Binary Content

Return binary data using the blob property (base64-encoded):
server.addResource({
  uri: "file:///images/logo.png",
  name: "Company Logo",
  mimeType: "image/png",
  async load() {
    const buffer = await readFile("./logo.png");
    return {
      blob: buffer.toString("base64"),
    };
  },
});

Multiple Resources

The load function can return an array of resources:
server.addResource({
  uri: "dir:///logs",
  name: "All Logs",
  description: "Directory of log files",
  async load() {
    return [
      {
        uri: "file:///logs/app.log",
        text: "App log content",
      },
      {
        uri: "file:///logs/error.log",
        text: "Error log content",
      },
    ];
  },
});
Returning multiple resources is useful for directory listings or collections of related data.

Resource Templates

Resource templates allow you to define dynamic resources with URI parameters:
server.addResourceTemplate({
  uriTemplate: "file:///logs/{name}.log",
  name: "Application Logs",
  mimeType: "text/plain",
  arguments: [
    {
      name: "name",
      description: "Name of the log file",
      required: true,
    },
  ],
  async load({ name }) {
    return {
      text: `Log content for ${name}`,
    };
  },
});

URI Templates

Use URI templates with placeholders:
server.addResourceTemplate({
  uriTemplate: "user://{userId}/profile",
  name: "User Profile",
  arguments: [
    {
      name: "userId",
      description: "The user's ID",
      required: true,
    },
  ],
  async load({ userId }) {
    const user = await fetchUser(userId);
    return {
      text: JSON.stringify(user),
    };
  },
});

Multiple Arguments

Templates can have multiple arguments:
server.addResourceTemplate({
  uriTemplate: "docs://project/{section}/{page}",
  name: "Project Documentation",
  mimeType: "text/markdown",
  arguments: [
    {
      name: "section",
      description: "Documentation section",
      required: true,
    },
    {
      name: "page",
      description: "Page name",
      required: true,
    },
  ],
  async load({ section, page }) {
    return {
      text: `# ${section}/${page}\n\nContent here...`,
    };
  },
});

Optional Arguments

Mark arguments as optional:
server.addResourceTemplate({
  uriTemplate: "api://users/{userId}",
  name: "User Data",
  arguments: [
    {
      name: "userId",
      description: "User ID to fetch",
      required: false, // Optional argument
    },
  ],
  async load({ userId }) {
    if (userId) {
      return { text: JSON.stringify(await fetchUser(userId)) };
    }
    return { text: JSON.stringify(await fetchAllUsers()) };
  },
});

Argument Auto-completion

Provide auto-completion for resource template arguments:
server.addResourceTemplate({
  uriTemplate: "file:///logs/{name}.log",
  name: "Application Logs",
  arguments: [
    {
      name: "name",
      description: "Name of the log",
      required: true,
      complete: async (value) => {
        // Filter log files based on input
        const logs = await listLogFiles();
        const filtered = logs.filter(log => 
          log.startsWith(value)
        );
        
        return {
          values: filtered,
        };
      },
    },
  ],
  async load({ name }) {
    return {
      text: `Log content for ${name}`,
    };
  },
});

Completion Response

The complete function returns a completion object:
type Completion = {
  values: string[];        // Array of completion values (max 100)
  total?: number;          // Total number of available completions
  hasMore?: boolean;       // Whether more completions exist
};
Example with metadata:
complete: async (value) => {
  const allOptions = await getAllOptions();
  const filtered = allOptions.filter(opt => 
    opt.startsWith(value)
  );
  
  return {
    values: filtered.slice(0, 100),
    total: filtered.length,
    hasMore: filtered.length > 100,
  };
}

Embedded Resources

The embedded() method simplifies including resources in tool responses:

Basic Example

server.addResource({
  uri: "system://status",
  name: "System Status",
  mimeType: "text/plain",
  async load() {
    return {
      text: "System operational",
    };
  },
});

server.addTool({
  name: "get_system_status",
  description: "Get current system status",
  execute: async () => {
    return {
      content: [
        {
          type: "resource",
          resource: await server.embedded("system://status"),
        },
      ],
    };
  },
});

With Resource Templates

server.addResourceTemplate({
  uriTemplate: "docs://project/{section}",
  name: "Project Documentation",
  mimeType: "text/markdown",
  arguments: [
    {
      name: "section",
      required: true,
    },
  ],
  async load(args) {
    const docs = {
      "getting-started": "# Getting Started\n\nWelcome!",
      "api-reference": "# API Reference\n\nAuth required.",
    };
    return {
      text: docs[args.section] || "Not found",
    };
  },
});

server.addTool({
  name: "get_documentation",
  description: "Retrieve project documentation",
  parameters: z.object({
    section: z.enum(["getting-started", "api-reference"]),
  }),
  execute: async (args) => {
    return {
      content: [
        {
          type: "resource",
          resource: await server.embedded(`docs://project/${args.section}`),
        },
      ],
    };
  },
});

Authentication Context

Resources can access authentication context:
server.addResource({
  uri: "session://current-user",
  name: "Current User Information",
  mimeType: "application/json",
  load: async (auth) => {
    if (!auth) {
      return {
        text: JSON.stringify({ authenticated: false }),
      };
    }
    
    return {
      text: JSON.stringify({
        authenticated: true,
        user: {
          id: auth.userId,
          username: auth.username,
          role: auth.role,
        },
      }),
    };
  },
});

Complete Example

Here’s a complete example combining multiple features:
import { FastMCP } from "fastmcp";
import { z } from "zod";

const server = new FastMCP({
  name: "Documentation Server",
  version: "1.0.0",
});

// Static resource
server.addResource({
  uri: "docs://readme",
  name: "README",
  mimeType: "text/markdown",
  description: "Project README file",
  async load() {
    return {
      text: "# My Project\n\nWelcome to the project!",
    };
  },
});

// Resource template with auto-completion
server.addResourceTemplate({
  uriTemplate: "docs://api/{endpoint}",
  name: "API Documentation",
  mimeType: "text/markdown",
  arguments: [
    {
      name: "endpoint",
      description: "API endpoint to document",
      required: true,
      complete: async (value) => {
        const endpoints = ["users", "posts", "comments"];
        return {
          values: endpoints.filter(e => e.startsWith(value)),
        };
      },
    },
  ],
  async load({ endpoint }) {
    const docs = {
      users: "# Users API\n\nManage users...",
      posts: "# Posts API\n\nManage posts...",
      comments: "# Comments API\n\nManage comments...",
    };
    
    return {
      text: docs[endpoint] || "Endpoint not found",
    };
  },
});

// Tool using embedded resources
server.addTool({
  name: "get_docs",
  description: "Get API documentation",
  parameters: z.object({
    endpoint: z.string(),
  }),
  execute: async (args) => {
    return {
      content: [
        {
          type: "resource",
          resource: await server.embedded(`docs://api/${args.endpoint}`),
        },
      ],
    };
  },
});

API Reference

Resource Type

type Resource<T> = {
  uri: string;
  name: string;
  description?: string;
  mimeType?: string;
  load: (auth?: T) => Promise<ResourceResult | ResourceResult[]>;
};

ResourceTemplate Type

type ResourceTemplate<T, Arguments> = {
  uriTemplate: string;
  name: string;
  description?: string;
  mimeType?: string;
  arguments: ResourceTemplateArgument[];
  load: (args: Args, auth?: T) => Promise<ResourceResult | ResourceResult[]>;
  complete?: (name: string, value: string, auth?: T) => Promise<Completion>;
};

ResourceResult Type

type ResourceResult =
  | { text: string; mimeType?: string; uri?: string }
  | { blob: string; mimeType?: string; uri?: string };

Build docs developers (and LLMs) love