What are Resources?
In MCP, resources are data sources that servers expose to clients. Unlike tools (which perform actions), resources provide read-only access to information.
Resources are ideal for:
Providing contextual data to AI models
Exposing database records
Sharing configuration data
Serving files or documents
Delivering API responses
Resource Types
MCP supports two types of resources:
1. Static Resources
Static resources have fixed URIs and are listed in resources/list:
// Static resource: got://quotes/random
server . resource (
"random-quotes" ,
"got://quotes/random" ,
async ( uri ) => { ... }
);
2. Dynamic Resources (Templates)
Dynamic resources use URI templates with variables, listed in resources/templates/list:
// Dynamic resource: person://properties/{name}
server . resource (
"person-properties" ,
new ResourceTemplate ( "person://properties/{name}" , { list: undefined }),
async ( uri , { name }) => { ... }
);
Defining Static Resources
Basic Static Resource
From source/servers/basic/src/server.ts:142-164:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
server . resource (
"random-quotes" , // Resource name
"got://quotes/random" , // Static URI
async ( uri ) => { // Handler function
try {
// Fetch 5 random quotes
const quotes = await fetchRandomQuotes ( 5 );
const formattedQuotes = quotes . map ( formatQuote );
return {
contents: [{
uri: uri . href ,
text: formattedQuotes . join ( " \n --- \n " ),
mimeType: "text/plain"
}]
};
} catch ( error ) {
throw new Error ( `Error fetching quotes: ${ ( error as Error ). message } ` );
}
}
);
Resources return an array of content objects:
return {
contents: [
{
uri: uri . href , // The resource URI
text: "Resource data" , // The actual content (or blob for binary)
mimeType: "text/plain" // Content type
}
]
};
Defining Dynamic Resources
Resource Templates
From source/servers/basic/src/server.ts:166-211:
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js" ;
// Define data structure
type PersonData = {
name : string ;
age : number ;
height : number ;
}
const personData : Record < string , PersonData > = {
alexys: {
name: "alexys" ,
age: 23 ,
height: 1.7
},
mariana: {
name: "mariana" ,
age: 23 ,
height: 1.7
}
};
server . resource (
"person-properties" ,
new ResourceTemplate ( "person://properties/{name}" , { list: undefined }),
async ( uri , { name }) => {
try {
// Validate parameter
if ( ! name || Array . isArray ( name )) {
throw new Error ( "name must be a single string" );
}
// Fetch data
const person = personData [ name ];
if ( ! person ) {
throw new Error ( `Person with name ${ name } not found` );
}
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( person ),
mimeType: "application/json"
}]
};
} catch ( error ) {
throw new Error ( `Error fetching person data: ${ ( error as Error ). message } ` );
}
}
);
URI Template Syntax
URI templates use curly braces for variables:
"person://properties/{name}" // Single variable
"files://documents/{category}/{id}" // Multiple variables
"api://users/{userId}/posts/{postId}" // Nested structure
Reading Resources from Clients
Reading Static Resources
From source/clients/basic-ts/src/index.ts:53-59 and source/clients/basic-py/main.py:43-46:
const resource = await client . readResource ({
uri: "got://quotes/random"
});
console . log ( "Resource fetched:" );
console . log ( JSON . stringify ( resource , null , 2 ));
Reading Dynamic Resources
From source/clients/basic-ts/src/index.ts:61-67 and source/clients/basic-py/main.py:48-51:
const templateResource = await client . readResource ({
uri: "person://properties/alexys"
});
console . log ( "Template resource fetched:" );
console . log ( JSON . stringify ( templateResource , null , 2 ));
Discovering Available Resources
// List static resources
const resources = await client . listResources ();
console . log ( resources );
// Output: { resources: [{ uri: "got://quotes/random", name: "random-quotes", ... }] }
// List resource templates
const templates = await client . listResourceTemplates ();
console . log ( templates );
// Output: { resourceTemplates: [{ uriTemplate: "person://properties/{name}", ... }] }
Content Types
Text Content
Most common for JSON, plain text, or formatted data:
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( data ),
mimeType: "application/json"
}]
};
Binary Content
For images, PDFs, or other binary data:
return {
contents: [{
uri: uri . href ,
blob: base64EncodedData ,
mimeType: "image/png"
}]
};
Common MIME Types
text/plain - Plain text
application/json - JSON data
text/html - HTML content
text/markdown - Markdown
image/png, image/jpeg - Images
application/pdf - PDF documents
You’re providing read-only data
The data is contextual information for AI models
You want to expose files or documents
The operation has no side effects
Examples:
Configuration files
Database records (read-only)
API responses (read-only)
Documentation
Resource Best Practices
Use Clear URI Schemes Choose URI schemes that clearly indicate the resource type: // Good
"got://quotes/random"
"person://properties/{name}"
"db://users/{id}"
// Less clear
"resource1"
"data/{id}"
Validate Template Parameters Always validate dynamic resource parameters: if ( ! name || Array . isArray ( name )) {
throw new Error ( "Invalid parameter" );
}
Handle Missing Data Return helpful errors when data isn’t found: if ( ! person ) {
throw new Error ( `Person ${ name } not found` );
}
Use Appropriate MIME Types Set the correct MIME type for your content: mimeType : "application/json" // For JSON
mimeType : "text/plain" // For text
Advanced Patterns
Paginated Resources
server . resource (
"users-paginated" ,
new ResourceTemplate ( "db://users?page={page}&limit={limit}" ),
async ( uri , { page , limit }) => {
const pageNum = parseInt ( page as string ) || 1 ;
const limitNum = parseInt ( limit as string ) || 10 ;
const users = await getUsersPaginated ( pageNum , limitNum );
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ({
data: users ,
page: pageNum ,
limit: limitNum ,
total: await getTotalUsers ()
}),
mimeType: "application/json"
}]
};
}
);
Filtered Resources
server . resource (
"quotes-by-character" ,
new ResourceTemplate ( "got://quotes/character/{character}" ),
async ( uri , { character }) => {
const quotes = await fetchQuotesByCharacter ( character as string );
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( quotes ),
mimeType: "application/json"
}]
};
}
);
Cached Resources
const cache = new Map < string , { data : any , timestamp : number }>();
const CACHE_TTL = 60000 ; // 1 minute
server . resource (
"cached-data" ,
"api://cached/data" ,
async ( uri ) => {
const cached = cache . get ( uri . href );
if ( cached && Date . now () - cached . timestamp < CACHE_TTL ) {
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( cached . data ),
mimeType: "application/json"
}]
};
}
const data = await fetchExpensiveData ();
cache . set ( uri . href , { data , timestamp: Date . now () });
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( data ),
mimeType: "application/json"
}]
};
}
);
Resource Change Notifications
If your resources can change, enable the listChanged capability:
const server = new McpServer ({
name: "My Server" ,
version: "1.0.0" ,
capabilities: {
resources: {
listChanged: true // Server will notify clients when resources change
}
}
});
This allows clients to be notified when the list of available resources changes, enabling dynamic resource discovery.
Next Steps
Prompts Learn how to create reusable prompt templates
Tools Understand how to implement executable tools
Servers Build complete MCP servers
Clients Create clients that consume resources