Skip to main content

What are Resources?

Resources are contextual data sources in MCP that AI models can read to gather information. Unlike tools (which perform actions) and prompts (which generate text), resources provide static or dynamic data like configuration, user profiles, or application state. In xmcp, resources use file-system routing where the file path determines the resource URI.

File-System Routing

Resources are automatically registered based on their location in the src/resources/ directory:
src/resources/
├── (config)/
│   └── app.ts              → resource://app-config
└── (users)/
    └── [userId]/
        └── profile.ts      → resource://user-profile/{userId}

Routing Rules

Files without dynamic segments create static resource URIs:
  • src/resources/(config)/app.tsresource://app-config
  • src/resources/(data)/settings.tsresource://settings
Files with [param] segments create parameterized resources:
  • src/resources/(users)/[userId]/profile.tsresource://user-profile/{userId}
  • src/resources/(posts)/[postId]/comments.tsresource://comments/{postId}
Directories wrapped in parentheses (name) are omitted from the URI:
  • (config) groups configuration resources but doesn’t appear in the URI
  • (users) organizes user-related resources without affecting the URI

Resource Structure

Every resource consists of two main exports:

1. Metadata Export

The metadata export defines the resource’s identity:
import { type ResourceMetadata } from "xmcp";

export const metadata: ResourceMetadata = {
  name: "app-config",
  title: "Application Config",
  description: "Application configuration data",
};

ResourceMetadata Type

The ResourceMetadata interface from ~/workspace/source/packages/xmcp/src/types/resource.ts:3-15 includes:
name
string
required
Unique identifier for the resource (must match the URI pattern)
title
string
Human-readable title for the resource
description
string
Description of what the resource provides
mimeType
string
MIME type of the resource content (e.g., "application/json", "text/plain", "text/html+skybridge")
size
number
Size of the resource in bytes
_meta
object
Metadata for the resource. Supports nested OpenAI metadata and other vendor extensions:
_meta: {
  openai?: OpenAIMetadata;
  [key: string]: unknown;
}

2. Default Export (Handler)

The default export is the function that returns the resource data:
export default function handler() {
  return "App configuration here";
}

Real Examples

Static Resource: App Config

From ~/workspace/source/examples/http-transport/src/resources/(config)/app.ts:1-12:
import { type ResourceMetadata } from "xmcp";

export const metadata: ResourceMetadata = {
  name: "app-config",
  title: "Application Config",
  description: "Application configuration data",
};

export default function handler() {
  return "App configuration here";
}
File location: src/resources/(config)/app.ts
Resource URI: resource://app-config

Dynamic Resource: User Profile

From ~/workspace/source/examples/stdio-transport/src/resources/(users)/[userId]/profile.ts:1-17:
import { z } from "zod";
import { type ResourceMetadata, type InferSchema } from "xmcp";

export const schema = {
  userId: z.string().describe("The ID of the user"),
};

export const metadata: ResourceMetadata = {
  name: "user-profile",
  title: "User Profile",
  description: "User profile information",
};

export default function handler({ userId }: InferSchema<typeof schema>) {
  return `Profile data for user ${userId}`;
}
File location: src/resources/(users)/[userId]/profile.ts
Resource URI: resource://user-profile/{userId}
Dynamic resources use a schema export (just like tools and prompts) to define the parameters extracted from the URI.

HTML Resource: Widget

From ~/workspace/source/examples/open-ai/src/resources/(ui)/widget/pizza-map.ts:1-16:
import { type ResourceMetadata } from "xmcp";

export const metadata: ResourceMetadata = {
  name: "pizza-map",
  title: "Show Pizza Map",
  mimeType: "text/html+skybridge",
};

export default async function handler() {
  return `
    <div id="pizzaz-root"></div>
    <link rel="stylesheet" href="https://persistent.oaistatic.com/ecosystem-built-assets/pizzaz-0038.css">
    <script type="module" src="https://persistent.oaistatic.com/ecosystem-built-assets/pizzaz-0038.js"></script>
  `.trim();
}
Set mimeType to specify the content type. Use "text/html+skybridge" for OpenAI widgets.

Creating Resources with CLI

Use the xmcp CLI to quickly scaffold a new resource:
xmcp create resource my-resource
This creates a new resource file at src/resources/my-resource.ts with the basic structure:
import { type ResourceMetadata } from "xmcp";

export const metadata: ResourceMetadata = {
  name: "my-resource",
  title: "My Resource",
  description: "TODO: Add description",
};

export default function myResource() {
  // TODO: Implement your resource logic here
  return "Resource data for my-resource";
}

Resource Patterns

Configuration Resources

Store application settings and configuration:
// src/resources/(config)/app.ts
export const metadata: ResourceMetadata = {
  name: "app-config",
  title: "App Configuration",
  description: "Application-wide configuration",
};

export default function handler() {
  return JSON.stringify({
    version: "1.0.0",
    features: ["auth", "api", "ui"],
    environment: process.env.NODE_ENV,
  });
}

User Data Resources

Provide user-specific data:
// src/resources/(users)/[userId]/profile.ts
export const schema = {
  userId: z.string(),
};

export const metadata: ResourceMetadata = {
  name: "user-profile",
  title: "User Profile",
  description: "User profile data",
};

export default async function handler({ userId }: InferSchema<typeof schema>) {
  const user = await fetchUser(userId);
  return JSON.stringify(user);
}

Documentation Resources

Provide contextual documentation:
// src/resources/(docs)/api-reference.ts
export const metadata: ResourceMetadata = {
  name: "api-reference",
  title: "API Reference",
  description: "Complete API documentation",
  mimeType: "text/markdown",
};

export default function handler() {
  return `
# API Reference

## Authentication
All requests require an API key...
  `.trim();
}

Return Values

Resources can return:
  1. Strings - Plain text or JSON strings
  2. Objects - Automatically serialized to JSON
  3. Structured content - MCP content format:
    return {
      content: [{ type: "text", text: "Resource data" }],
    };
    

Best Practices

Use Descriptive Names

Choose clear names that describe the resource’s content: user-profile, app-config, api-docs.

Set MIME Types

Specify mimeType for non-text resources:
  • "application/json" for JSON data
  • "text/markdown" for documentation
  • "text/html+skybridge" for OpenAI widgets

Organize with Route Groups

Use parentheses to group related resources:
  • (config)/ for configuration
  • (users)/ for user data
  • (docs)/ for documentation

Handle Async Data

Resources can be async to fetch data from databases or APIs:
export default async function handler() {
  const data = await fetchData();
  return JSON.stringify(data);
}

Validate Dynamic Parameters

Use Zod schemas to validate URI parameters:
export const schema = {
  userId: z.string().uuid(),
};

Resources vs Tools vs Prompts

Resources

Provide dataRead-only contextual information that models can reference.Example: User profiles, configuration, documentation

Tools

Perform actionsExecutable functions that models can call to accomplish tasks.Example: Create user, send email, fetch data

Prompts

Generate textReusable templates that help models generate consistent responses.Example: Code review, documentation generation

Next Steps

Building Tools

Learn how to create executable tools

Building Widgets

Create React components for OpenAI-compatible UIs

Build docs developers (and LLMs) love