Core Concepts

Resources

Expose data sources as MCP resources with static or dynamic URIs.

What are Resources?

Resources provide access to data via URIs. They can represent files, database records, API endpoints, or any other data source that can be accessed through a URI.

Static Resources

Static resources have a fixed URI that doesn't change.

Simple File Resources

The easiest way to expose a local file is using the file property. This automatically handles the URI generation, MIME type detection, and file reading.

server/mcp/resources/readme.ts
export default defineMcpResource({
  name: 'readme',
  description: 'Project README file',
  file: 'README.md', // Relative to project root
})

This generates:

  • URI: file:///path/to/project/README.md
  • Handler: Automatically reads the file content
  • MIME Type: Automatically detected (e.g., text/markdown)

Custom Static Resources

For more control, you can define the uri and handler manually:

server/mcp/resources/custom-readme.ts
import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'

export default defineMcpResource({
  name: 'custom-readme',
  title: 'README',
  description: 'Project README file',
  uri: 'file:///README.md',
  metadata: {
    mimeType: 'text/markdown',
  },
  handler: async (uri: URL) => {
    const filePath = fileURLToPath(uri)
    const content = await readFile(filePath, 'utf-8')
    return {
      contents: [{
        uri: uri.toString(),
        mimeType: 'text/markdown',
        text: content,
      }],
    }
  },
})

Auto-Generated Name and Title

You can omit name and title - they will be automatically generated from the filename:

server/mcp/resources/project-readme.ts
export default defineMcpResource({
  // name and title are auto-generated from filename:
  // name: 'project-readme'
  // title: 'Project Readme'
  file: 'README.md'
})

The filename project-readme.ts automatically becomes:

  • name: project-readme (kebab-case)
  • title: Project Readme (title case)

You can still provide name or title explicitly to override the auto-generated values.

Resource Structure

A resource definition consists of:

export default defineMcpResource({
  name: 'resource-name',
  file: 'path/to/file.txt', // Local file path
  metadata: { ... }
})

Dynamic Resources with Templates

Use ResourceTemplate to create dynamic resources that accept variables:

server/mcp/resources/file.ts
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js'

export default defineMcpResource({
  name: 'file',
  title: 'File Resource',
  uri: new ResourceTemplate('file:///project/{+path}', {
    list: async () => {
      // Return list of available resources
      return {
        resources: [
          { uri: 'file:///project/README.md', name: 'README.md' },
          { uri: 'file:///project/src/index.ts', name: 'src/index.ts' },
        ],
      }
    },
  }),
  handler: async (uri: URL, variables: Variables) => {
    const path = variables.path as string
    const filePath = join(process.cwd(), path)
    const content = await readFile(filePath, 'utf-8')

    return {
      contents: [{
        uri: uri.toString(),
        mimeType: 'text/plain',
        text: content,
      }],
    }
  },
})

ResourceTemplate

ResourceTemplate allows you to create resources with variable parts in the URI:

new ResourceTemplate('file:///project/{+path}', {
  list: async () => {
    // Optional: Return list of available resources
    return {
      resources: [
        { uri: 'file:///project/file1.txt', name: 'File 1' },
        { uri: 'file:///project/file2.txt', name: 'File 2' },
      ],
    }
  },
})

Template Variables

Variables in the URI are defined with {variableName}:

// Single variable
new ResourceTemplate('file:///project/{path}', { ... })

// Variable allowing slashes (reserved expansion)
new ResourceTemplate('file:///project/{+path}', { ... })

// Multiple variables
new ResourceTemplate('api://users/{userId}/posts/{postId}', { ... })

Handler Function

The handler receives the resolved URI and optional variables:

// Static resource handler
handler: async (uri: URL) => {
  return {
    contents: [{
      uri: uri.toString(),
      mimeType: 'text/plain',
      text: 'Content',
    }],
  }
}

// Dynamic resource handler
handler: async (uri: URL, variables: Variables) => {
  const path = variables.path as string
  // Use variables to resolve the resource
  return {
    contents: [{
      uri: uri.toString(),
      mimeType: 'text/plain',
      text: 'Content',
    }],
  }
}

Resource Metadata

Add metadata to help clients understand the resource:

server/mcp/resources/readme.ts
export default defineMcpResource({
  name: 'readme',
  description: 'Project README file',
  file: 'README.md',
})

Content Types

Resources can return different MIME types:

return {
  contents: [{
    uri: uri.toString(),
    mimeType: 'text/markdown',
    text: '# Markdown content',
  }],
}

Error Handling

Handle errors gracefully in your handlers:

server/mcp/resources/custom-readme.ts
import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'

export default defineMcpResource({
  name: 'readme',
  uri: 'file:///README.md',
  handler: async (uri: URL) => {
    try {
      const filePath = fileURLToPath(uri)
      const content = await readFile(filePath, 'utf-8')

      return {
        contents: [{
          uri: uri.toString(),
          mimeType: 'text/markdown',
          text: content,
        }],
      }
    }
    catch (error) {
      return {
        contents: [{
          uri: uri.toString(),
          mimeType: 'text/plain',
          text: `Error: ${error instanceof Error ? error.message : String(error)}`,
        }],
        isError: true,
      }
    }
  },
})

File Organization

Organize your resources in the server/mcp/resources/ directory:

server/
└── mcp/
    └── resources/
        ├── readme.ts
        └── file.ts

Each file should export a default resource definition.

URI Schemes

You can use any URI scheme that makes sense for your use case:

  • file:// - File system resources
  • api:// - API endpoints
  • http:// / https:// - Web resources
  • custom:// - Custom schemes

Next Steps

  • Tools - Create tools to perform actions
  • Prompts - Create reusable prompts
  • Handlers - Create custom MCP endpoints
  • Examples - More resource examples