Resources in WebMCP are data endpoints that AI agents can read. They expose dynamic or static content like configuration, files, or API data to AI models.
Resources are part of the Model Context Protocol (MCP) specification. WebMCP implements resources for browser environments, enabling web applications to expose data to AI agents.
Overview
Concept Description Purpose Expose readable data to AI agents URI Unique identifier (static or template with parameters) MIME Type Content type hint for parsing Output Array of content objects with text or binary data
When to Use Resources
Configuration Data Expose application settings and preferences
File Systems Provide access to virtual or real file content
Dynamic State Share current application state with AI
External Data Proxy API responses and external content
Static Resources
Register a resource with a fixed URI:
// Register a configuration resource
navigator . modelContext . registerResource ({
uri: 'config://app-settings' ,
name: 'App Settings' ,
description: 'Current application configuration' ,
mimeType: 'application/json' ,
async read () {
const settings = {
theme: 'dark' ,
language: 'en' ,
notifications: true ,
};
return {
contents: [{
uri: 'config://app-settings' ,
text: JSON . stringify ( settings , null , 2 ),
mimeType: 'application/json' ,
}],
};
},
});
Template Resources
Use URI templates with {param} placeholders for dynamic access:
// Register a file reader with URI template
navigator . modelContext . registerResource ({
uri: 'file://{path}' ,
name: 'File Reader' ,
description: 'Read files from the virtual filesystem' ,
mimeType: 'text/plain' ,
async read ( uri , params ) {
// params.path contains the extracted parameter
const path = params ?. path || 'unknown' ;
const content = await fetchFileContent ( path );
return {
contents: [{
uri: uri . href ,
text: content ,
mimeType: getMimeType ( path ),
}],
};
},
});
// AI can now read any file:
// readResource('file://readme.txt') -> path = "readme.txt"
// readResource('file://config.json') -> path = "config.json"
Multiple Parameters
Templates can have multiple parameters:
navigator . modelContext . registerResource ({
uri: 'api://users/{userId}/posts/{postId}' ,
name: 'User Post' ,
description: 'Fetch a specific post by a user' ,
mimeType: 'application/json' ,
async read ( uri , params ) {
const { userId , postId } = params || {};
const post = await fetch ( `/api/users/ ${ userId } /posts/ ${ postId } ` );
const data = await post . json ();
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( data , null , 2 ),
mimeType: 'application/json' ,
}],
};
},
});
How AI Agents Read Resources
When an AI agent wants to read a resource, it calls readResource() through the MCP protocol. This invokes your registered read() handler:
// AI agent calls readResource('config://app-settings')
// Your handler is invoked and returns the content
navigator . modelContext . registerResource ({
uri: 'config://app-settings' ,
name: 'App Settings' ,
async read () {
// This handler is called when AI reads the resource
console . log ( 'AI reading app settings' );
return {
contents: [{
uri: 'config://app-settings' ,
text: JSON . stringify ( settings ),
mimeType: 'application/json' ,
}],
};
},
});
The readResource() method is called by AI agents through the MCP protocol, not directly from your application code. Your read() handler is invoked automatically when an agent requests the resource.
Listing Resources
// List static resources
const resources = navigator . modelContext . listResources ();
resources . forEach ( r => {
console . log ( ` ${ r . uri } : ${ r . name } ` );
});
// List resource templates
const templates = navigator . modelContext . listResourceTemplates ();
templates . forEach ( t => {
console . log ( ` ${ t . uriTemplate } : ${ t . name } ` );
});
Multiple Contents
Resources can return multiple content items:
navigator . modelContext . registerResource ({
uri: 'bundle://app-data' ,
name: 'App Data Bundle' ,
description: 'Multiple related data items' ,
async read () {
return {
contents: [
{
uri: 'bundle://app-data#config' ,
text: JSON . stringify ( config ),
mimeType: 'application/json' ,
},
{
uri: 'bundle://app-data#user' ,
text: JSON . stringify ( user ),
mimeType: 'application/json' ,
},
{
uri: 'bundle://app-data#preferences' ,
text: JSON . stringify ( preferences ),
mimeType: 'application/json' ,
},
],
};
},
});
Binary Content
Resources can return binary content using base64 encoding:
navigator . modelContext . registerResource ({
uri: 'image://{name}' ,
name: 'Image Resource' ,
description: 'Read images as base64' ,
mimeType: 'image/png' ,
async read ( uri , params ) {
const name = params ?. name ;
const imageBlob = await fetchImage ( name );
const base64 = await blobToBase64 ( imageBlob );
return {
contents: [{
uri: uri . href ,
blob: base64 , // Base64-encoded binary data
mimeType: 'image/png' ,
}],
};
},
});
Dynamic vs Static Registration
Use registerResource() for resources that may change at runtime: // Register dynamically
const registration = navigator . modelContext . registerResource ({
uri: 'session://current-data' ,
// ...
});
// Unregister when no longer needed
registration . unregister ();
Use for:
Session-specific data
Component-scoped resources
Temporary data access
Use provideContext() for base resources: navigator . modelContext . provideContext ({
resources: [
{ uri: 'config://app' , /* ... */ },
{ uri: 'file://{path}' , /* ... */ },
],
});
Use for:
Core application resources
Always-available data
Base resource set
URI Scheme Conventions
Scheme Purpose Example config://Configuration data config://app-settingsfile://File system access file://{path}user://User-related data user://{id}/profileapi://External API data api://weather/{city}db://Database records db://products/{sku}session://Session state session://currentcache://Cached data cache://recent-queries
Best Practices
Use meaningful URI schemes
Choose URI schemes that indicate the data type: // Good - clear purpose
'config://theme-settings'
'user://current/preferences'
// Avoid - unclear
'data://123'
'resource://get'
Always specify MIME types
Help AI agents parse content correctly: mimeType : 'application/json' // JSON data
mimeType : 'text/plain' // Plain text
mimeType : 'text/markdown' // Markdown
mimeType : 'text/csv' // CSV data
Return helpful error information: async read ( uri , params ) {
const path = params ?. path ;
if ( ! fileExists ( path )) {
return {
contents: [{
uri: uri . href ,
text: `Error: File not found: ${ path } ` ,
mimeType: 'text/plain' ,
}],
};
}
// ... normal read
}
Return only relevant data. Let AI agents request specific resources rather than dumping everything.
Document available resources
Use clear descriptions so AI agents know what resources are available and what they contain.
Common Patterns
Configuration Resources
navigator . modelContext . registerResource ({
uri: 'config://feature-flags' ,
name: 'Feature Flags' ,
description: 'Current feature flag states' ,
mimeType: 'application/json' ,
async read () {
return {
contents: [{
uri: 'config://feature-flags' ,
text: JSON . stringify ({
darkMode: true ,
newEditor: false ,
betaFeatures: [ 'ai-assist' , 'live-collab' ],
}),
mimeType: 'application/json' ,
}],
};
},
});
Database Records
navigator . modelContext . registerResource ({
uri: 'db://products/{sku}' ,
name: 'Product Details' ,
description: 'Fetch product information by SKU' ,
mimeType: 'application/json' ,
async read ( uri , params ) {
const product = await db . products . findBySku ( params ?. sku );
return {
contents: [{
uri: uri . href ,
text: JSON . stringify ( product ),
mimeType: 'application/json' ,
}],
};
},
});
Real-Time State
navigator . modelContext . registerResource ({
uri: 'state://editor-content' ,
name: 'Editor Content' ,
description: 'Current content in the editor' ,
mimeType: 'text/plain' ,
async read () {
const content = editorRef . current ?. getValue () || '' ;
return {
contents: [{
uri: 'state://editor-content' ,
text: content ,
mimeType: 'text/plain' ,
}],
};
},
});