Documentation Index
Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
Use this file to discover all available pages before exploring further.
Resources expose readable data to an AI model’s context. Unlike tools that execute actions with side effects, resources are designed for read-only data retrieval—configuration files, user profiles, documents, API responses, or any content the model needs to reference.
Why Resources?
In the Model Context Protocol, resources serve a fundamentally different purpose than tools:
| Aspect | Resource | Tool |
|---|
| Purpose | Provide data to read | Execute actions |
| Direction | Model pulls data on demand | Model triggers execution |
| Idempotent | Always (read-only) | Not necessarily |
| Use case | Context loading, data retrieval | Side effects, mutations |
Resources are ideal for:
- Configuration — expose app settings, feature flags, environment info
- User data — profiles, preferences, permissions
- Documents — files, templates, knowledge base articles
- API responses — cached or live data from external services
- System state — logs, metrics, status information
Static Resources
Static resources have a fixed URI and return content at that specific address. Use them for singleton data like configuration or global state.
Class Style
import { Resource } from '@frontmcp/sdk';
@Resource({
name: 'app-config',
uri: 'config://app',
mimeType: 'application/json',
description: 'Application configuration and settings',
})
class AppConfig {
execute(uri: string, params: Record<string, string>) {
return {
version: '2.1.0',
environment: process.env.NODE_ENV,
features: {
darkMode: true,
analytics: false,
},
};
}
}
Function Style
For simpler resources, use the functional builder:
import { resource } from '@frontmcp/sdk';
const AppConfig = resource({
name: 'app-config',
uri: 'config://app',
mimeType: 'application/json',
})(() => ({
version: '2.1.0',
environment: process.env.NODE_ENV,
}));
Resource Templates
Resource templates use dynamic URIs with parameters following RFC 6570 Level 1 syntax. Parameters are extracted automatically and passed to execute().
Class Style
import { ResourceTemplate } from '@frontmcp/sdk';
@ResourceTemplate({
name: 'user-profile',
uriTemplate: 'users://{userId}/profile',
mimeType: 'application/json',
description: 'Fetch user profile by ID',
})
class UserProfile {
execute(uri: string, params: Record<string, string>) {
const { userId } = params;
// Fetch user from database, API, etc.
return {
id: userId,
name: 'Jane Doe',
email: 'jane@example.com',
role: 'admin',
};
}
}
URI Template Patterns
// Single parameter
'users://{userId}' // matches users://123
// Multiple parameters
'repos://{owner}/{repo}' // matches repos://acme/widget
// With path segments
'files://{path}/content' // matches files://docs/content
// Complex paths
'api://{version}/users/{id}' // matches api://v2/users/456
Parameters are extracted into a Record<string, string> and passed as the second argument to execute().
Registering Resources
Add resources to your app via the resources array:
import { App } from '@frontmcp/sdk';
@App({
id: 'my-app',
name: 'My Application',
resources: [AppConfig, UserProfile],
})
class MyApp {}
Resources can also be generated dynamically by adapters (e.g., OpenAPI adapter) or plugins.
Return Values
Resources support multiple return formats. The SDK automatically converts your return value to the MCP ReadResourceResult format.
Simple Returns
// Object -> auto-serialized to JSON
execute() {
return { key: 'value', count: 42 };
}
// String -> text content
execute() {
return 'Plain text content';
}
// Buffer -> binary blob (base64 encoded)
execute() {
return Buffer.from(imageData);
}
For complete control over the response, return the full ReadResourceResult structure:
execute(uri: string) {
return {
contents: [
{
uri,
mimeType: 'text/plain',
text: 'First content block',
},
{
uri: `${uri}#image`,
mimeType: 'image/png',
blob: 'base64EncodedImageData...',
},
],
};
}
Multiple Content Items
Return an array to include multiple content blocks:
execute(uri: string) {
return [
{ uri, mimeType: 'text/markdown', text: '# Document Title' },
{ uri: `${uri}#metadata`, mimeType: 'application/json', text: '{"author":"Jane"}' },
];
}
Static Resource (@Resource)
@Resource({
name: string, // Required: unique identifier
uri: string, // Required: static URI (e.g., 'config://app')
title?: string, // Optional: human-readable display name
description?: string, // Optional: hint for the LLM
mimeType?: string, // Optional: content type (e.g., 'application/json')
icons?: Icon[], // Optional: UI icons
})
Resource Template (@ResourceTemplate)
@ResourceTemplate({
name: string, // Required: unique identifier
uriTemplate: string, // Required: RFC 6570 URI template (e.g., 'users://{id}')
title?: string, // Optional: human-readable display name
description?: string, // Optional: hint for the LLM
mimeType?: string, // Optional: content type for all matching URIs
icons?: Icon[], // Optional: UI icons
})
Field descriptions:
| Field | Description |
|---|
name | Programmatic identifier used internally and in MCP responses |
uri / uriTemplate | The address clients use to request this resource |
title | Human-friendly name for UI display |
description | Helps the model understand when to use this resource |
mimeType | Content type hint; auto-detected for JSON/text if omitted |
icons | Array of icons for visual representation in clients |
Resource Context
Class-based resources have access to a rich execution context via this:
@Resource({ name: 'data', uri: 'app://data' })
class DataResource {
execute(uri: string, params: Record<string, string>) {
// Request information
this.uri; // The actual URI being read
this.params; // Extracted template parameters (empty for static)
this.metadata; // Resource metadata (name, uri, description, etc.)
// Authentication
this.authInfo; // Auth context from MCP session
// Dependency injection
this.get(DbService); // Resolve a provider
this.tryGet(Cache); // Resolve or return undefined
// Scope access
this.scope; // Access the current scope
// Utilities
this.fetch(url); // Built-in fetch for HTTP requests
// Flow control
this.respond(value); // End execution with a response
}
}
Using Providers
Inject services via the get() method:
@Resource({
name: 'users-list',
uri: 'data://users',
})
class UsersList {
async execute() {
const db = this.get(DatabaseProvider);
const users = await db.query('SELECT * FROM users LIMIT 100');
return users;
}
}
Real-World Examples
Configuration Resource
@Resource({
name: 'feature-flags',
uri: 'config://features',
mimeType: 'application/json',
description: 'Current feature flag configuration',
})
class FeatureFlags {
execute() {
return {
newDashboard: process.env.FEATURE_NEW_DASHBOARD === 'true',
betaFeatures: process.env.FEATURE_BETA === 'true',
maxUploadSize: parseInt(process.env.MAX_UPLOAD_SIZE || '10485760'),
};
}
}
Database-Backed Template
@ResourceTemplate({
name: 'order-details',
uriTemplate: 'orders://{orderId}',
mimeType: 'application/json',
description: 'Retrieve order details by order ID',
})
class OrderDetails {
async execute(uri: string, params: Record<string, string>) {
const db = this.get(DatabaseProvider);
const order = await db.orders.findById(params.orderId);
if (!order) {
throw new Error(`Order ${params.orderId} not found`);
}
return {
id: order.id,
status: order.status,
items: order.items,
total: order.total,
createdAt: order.createdAt,
};
}
}
File System Resource
@ResourceTemplate({
name: 'document',
uriTemplate: 'docs://{path}',
description: 'Read documentation files',
})
class DocumentResource {
async execute(uri: string, params: Record<string, string>) {
const fs = await import('fs/promises');
const path = await import('path');
const docPath = path.join(process.cwd(), 'docs', params.path);
const content = await fs.readFile(docPath, 'utf-8');
const ext = path.extname(params.path);
return {
contents: [{
uri,
mimeType: ext === '.md' ? 'text/markdown' : 'text/plain',
text: content,
}],
};
}
}
API Proxy Resource
@ResourceTemplate({
name: 'github-repo',
uriTemplate: 'github://{owner}/{repo}',
mimeType: 'application/json',
description: 'Fetch GitHub repository information',
})
class GitHubRepo {
async execute(uri: string, params: Record<string, string>) {
const { owner, repo } = params;
const response = await this.fetch(
`https://api.github.com/repos/${owner}/${repo}`,
{
headers: {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'FrontMCP-Resource',
},
}
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status}`);
}
return response.json();
}
}
MCP Protocol Integration
Resources integrate with the MCP protocol via three flows:
| Flow | Description |
|---|
resources/list | Returns all static resources |
resources/templates/list | Returns all resource templates |
resources/read | Reads content from a specific URI |
When a client requests resources/read with a URI:
- The SDK matches the URI against registered resources and templates
- For templates, parameters are extracted from the URI
- The
execute() method is called with the URI and parameters
- The return value is converted to MCP
ReadResourceResult format
Capabilities
FrontMCP automatically advertises resource capabilities during MCP initialization:
{
"capabilities": {
"resources": {
"subscribe": true,
"listChanged": true
}
}
}
| Capability | Description |
|---|
subscribe | When true, clients can subscribe to individual resource changes via resources/subscribe |
listChanged | When true, the server will send notifications/resources/list_changed when resources are added or removed |
The SDK sets these capabilities based on your registered resources:
listChanged: true when you have any resources registered
subscribe: true when subscription support is enabled
Change Notifications
When resources change dynamically (e.g., via adapters or plugins), FrontMCP automatically sends notifications/resources/list_changed to connected clients. Clients that support this notification will refresh their resource list.
For subscribed resources, the server sends notifications/resources/updated when the content changes.
To learn how clients discover your resources via MCP flows like resources/list and resources/templates/list, see Discovery APIs.
Best Practices
Do:
- Use descriptive
name and description fields to help models understand resource purpose
- Return structured data (objects) when possible for better model comprehension
- Use templates for parameterized data instead of creating many static resources
- Keep resources focused—one resource per data concern
- Handle errors gracefully and return meaningful error messages
Don’t:
- Use resources for operations with side effects (use tools instead)
- Return extremely large datasets—paginate or summarize when needed
- Expose sensitive data without proper authentication checks
- Hardcode values that should come from configuration