Modern AI applications are only as powerful as the context they can access. While large language models (LLMs) excel at reasoning and generation, they often lack real-time awareness of your application’s data, whether it’s files, database schemas, APIs, or internal knowledge systems.
This is where the Model Context Protocol (MCP) comes in.
MCP introduces a standardized way for applications (servers) to expose structured, discoverable, and dynamic context to AI systems (clients). Instead of hardcoding integrations or manually stitching together data sources, MCP allows context to be treated as a first-class concept and accessible through well-defined interfaces.
At the heart of this protocol lies Resources. Resources represent any piece of data that can provide context to an AI model:
· A source code file
· A database schema
· A configuration file
· Or even dynamically generated application data
Each resource is uniquely identified using a URI and can be discovered, read, and even subscribed to for updates.
In this guide, we’ll understand what are MCP Resources from the ground up and cover how they work, how clients interact with them, and how you can leverage them to build smarter, context-aware AI applications.
Whether you're building developer tools, internal copilots, or enterprise AI systems, understanding MCP Resources is a key step toward designing scalable and flexible context sharing architectures.
1. User Interaction Model: Who Decides the Context?
One of the most important and often overlooked aspects of MCP Resources is this "MCP does not dictate how users interact with resources".
Instead, it follows an application-driven model, where the host application is fully responsible for deciding how resources are discovered, selected, and used as context for the AI.
This design choice is intentional. Different applications have very different needs, and a rigid interaction model would limit flexibility.
1.1 What Does Application-Driven Really Mean?
In traditional systems, the backend often controls what data gets passed around. But with MCP:
· The server exposes resources
· The client understands what’s available
· And the application decides how to use them
This separation gives you the freedom to design experiences that best fit your users. Let’s look at a few practical ways applications can expose and use MCP resources:
a. Explicit Selection (User in Control)
Applications can present resources in a UI like a file explorer:
· Tree view (folders, files)
· List view (flat resource list)
Users can manually select what context to include before interacting with the AI.
Example:
A developer tool where users pick one of the following files before asking "Explain how configuration is loaded in this project"
· main.java
· application.yml
This approach gives maximum control and transparency.
b. Search and Filter (Guided Discovery)
When the number of resources grows, manual browsing becomes inefficient. Applications can provide:
· Search bars
· Filters (by type, name, tags, annotations)
Example:
Typing "schema" filters resources to:
· user_schema.sql
· order_schema.json
This improves discoverability and usability at scale.
c. Automatic Context Inclusion (System in Control)
In some cases, users shouldn't have to think about context at all. Applications can automatically select relevant resources based on:
· Heuristics (e.g., recently used files)
· User actions (e.g., current screen)
· AI-driven selection
Example:
If a user is viewing a database table, the app automatically includes:
· Table schema
· Related queries
This enables seamless, low-friction experiences.
d. No One-Size-Fits-All
MCP is flexible by design, it defines what resources are, not how they should be used.
You are free to:
· Build a fully manual system (like a file picker)
· A fully automated system (like a smart assistant)
· Or a hybrid of both
This flexibility unlocks powerful design possibilities:
· Developer tools: precise, manual selection
· Enterprise copilots: automated, context-aware assistance
· Search systems: dynamic filtering and ranking
By decoupling resource exposure from user interaction, MCP allows you to innovate on the experience layer without changing the protocol itself.
2. Capabilities: What Can Your MCP Server Actually Do?
Before a client starts interacting with resources, it needs to understand one thing, what features does the server support?. This is where capabilities come into play.
In MCP, capabilities are declared during the initial handshake between the client and the server. They act as a contract which clearly defines what the server can and cannot do.
f a server supports resources, it must declare the resources capability.
At its simplest, it looks like this:
{
"result": {
"capabilities": {
"resources": {
"listChanged": true,
"subscribe": false
},
"tools": {
"listChanged": true
},
"prompts": {
"listChanged": true
}
},
"serverInfo": {
"name": "employee-mcp-server",
"version": "0.0.1"
},
"protocolVersion": "2024-11-05"
},
"id": 1,
"jsonrpc": "2.0"
}
This tells the client that MCP Server support resources, tools and prompts, resource subscription is not supported.
3. Resource Capabilities
The resources capability can include two optional features:
· subscribe: Indicates whether clients can subscribe to individual resources. Enables real time updates when a resource changes
· listChanged: Indicates whether the server will notify when the list of resources changes. Useful when resources are dynamic (e.g., new files, tables, APIs)
MCP is flexible, servers can support only Subscriptions, only List Changes, both Features (Full Support), or neither as well.
Only Subscriptions
{
"capabilities": {
"resources": {
"subscribe": true
}
}
}
Only List Changes
{
"capabilities": {
"resources": {
"listChanged": true
}
}
}
Both Features (Full Support)
{
"capabilities": {
"resources": {
"subscribe": true,
"listChanged": true
}
}
}
Neither Feature
{
"capabilities": {
"resources": {}
}
}
4. Listing Resources: Discovering What’s Available
Before a client can use any resource, it first needs to answer a simple question "What resources does this server offer?". In MCP, this discovery step is handled through the resources/list API.
The resources/list request allows clients to fetch all available resources exposed by the server. At its simplest, the request looks like below.
{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/list"
}
What Does the Server Return?
The server responds with a structured list of resources.
{
"result": {
"resources": [
{
"name": "Company Organization Chart",
"description": "Complete organizational structure showing departments, teams, and reporting lines",
"mimeType": "application/json",
"uri": "resource://employee/org-chart"
}
]
}
}
Each resource acts like a metadata descriptor, not the actual data. Each resource in the list provides key information:
· uri: Unique identifier (used later to read the resource)
· name: Short, user-friendly label
· description: What the resource contains
· mimeType: Type of content (JSON, Markdown, etc.)
Sample CURL
curl --location 'http://localhost:8080/mcp' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/list"
}'
5. Reading Resources: Accessing the Actual Data
After discovering available resources using resources/list, the next logical step is "Give me the actual content behind this resource". This is exactly what the resources/read API is designed for.
To read a resource, the client simply provides its URI.
Sample Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/read",
"params": {
"uri": "resource://employee/org-chart"
}
}
Sample Response
{
"result": {
"contents": [
{
"uri": "resource://employee/org-chart",
"mimeType": "text/plain",
"text": "....."
}
]
}
}
In the response:
· contents is an Array, even though we requested a single resource, the response returns a list of content blocks. It is because, a resource can be composed of multiple parts and useful for chunked data, multi-file responses, or mixed formats.
· Each content block can contain either:
o text: for readable formats (JSON, Markdown, code, etc.)
o blob: for binary data (images, PDFs, etc., base64 encoded)
Text Content
{
"uri": "file:///example.txt",
"mimeType": "text/plain",
"text": "Resource content"
}
Binary Content
{
"uri": "file:///example.png",
"mimeType": "image/png",
"blob": "base64-encoded-data"
}
· mimeType tells the client how to interpret the content.
6. Resource Templates: Dynamic Access to Data
So far, we’ve seen how MCP allows clients to:
· Discover resources (resources/list)
· Read their contents (resources/read)
But what if the data isn’t static? What if you want to access data dynamically, like:
· A specific employee by ID
· A department by its identifier
· A file by its path
This is where Resource Templates come in. Resource templates allow servers to define parameterized resources using URI templates. Instead of exposing thousands of static resources like:
· resource://employee/101
· resource://employee/102
· resource://employee/103
You expose a single template: resource://employee/{employeeId}
The client fills in the {employeeId} dynamically.
6.1 Discovering Templates
Clients can fetch available templates using below request payload.
{
"jsonrpc": "2.0",
"id": 3,
"method": "resources/templates/list"
}
Sample Response: Available Templates
{
"result": {
"resourceTemplates": [
{
"uriTemplate": "resource://employee/{employeeId}",
"name": "Employee Profile",
"description": "Access employee details by employeeId",
"mimeType": "application/json",
"title": "Employee Profile",
"icons": [
{
"sizes": [
"48x48"
],
"src": "https://example.com/user-icon.png",
"mimeType": "image/png"
}
]
}
]
},
"id": 3,
"jsonrpc": "2.0"
}
Here, each template includes
· uriTemplate: Parameterized URI
· name: Friendly name
· description: What it represents
· mimeType: Expected response format
· title / icons: For UI enhancements
Sample CURL
curl --location 'http://localhost:8080/mcp' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 3,
"method": "resources/templates/list"
}'
7. List Changed Notification: Keeping Clients in Sync
So far, we’ve seen how clients can:
· Discover resources using resources/list
· Read them using resources/read
But there’s an important question, what happens when the available resources change?
For example:
· A new file is added
· A dataset is removed
· A new API backed resource becomes available
Without a mechanism to detect this, clients would be stuck with stale knowledge.
7.1 The Solution: list_changed Notifications
If a server supports the listChanged capability, it can proactively notify clients whenever the resource list changes.
Here’s what the notification looks like:
{
"jsonrpc": "2.0",
"method": "notifications/resources/list_changed"
}
No parameters. No payload. Just a signal: "Hey, something changed—refresh your resource list". When a client receives this notification, the expected behaviour is simple:
· Receive notification
· Call resources/list again
· Update local state / UI
This keeps the system event-driven instead of polling-based.
8. Subscriptions: Real-Time Updates for Individual Resources
So far, we’ve covered:
· resources/list: discover available resources
· list_changed: detect when the list changes
But what if the resource itself changes? What if you want to track updates to a specific resource in real time?
That’s where Subscriptions come in.
What Are Subscriptions?
Subscriptions allow clients to listen for changes to a specific resource. Instead of repeatedly calling resources/read, the client can say "Notify me whenever this resource changes".
Subscribe Request
To start listening, the client sends a resources/subscribe request:
{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/subscribe",
"params": {
"uri": "file:///project/src/main.rs"
}
}
This tells the server that I’m interested in updates to this specific resource.
Update Notification
When the resource changes, the server sends a notification.{
"jsonrpc": "2.0",
"method": "notifications/resources/updated",
"params": {
"uri": "file:///project/src/main.rs"
}
}
Why Not Send the Updated Data Directly?
You might ask "Why not include the updated content in the notification?", this is intentional:
· Keeps notifications lightweight
· Avoids sending large payloads unnecessarily
· Gives clients control over when and how to fetch updates
In summary, MCP Resources redefine how AI systems access and interact with context. Instead of tightly coupled integrations or hardcoded data pipelines, MCP introduces a standardized, flexible, and scalable approach to exposing and consuming context.
Let’s quickly recap the journey:
· Resources (resources/list): Help clients discover what data is available
· Reading (resources/read): Enables access to the actual content behind a resource
· Resource Templates: Introduce dynamic, parameterized access to large datasets
· Capabilities: Define what the server can support, enabling adaptive clients
· List Changed Notifications: Keep clients in sync when available resources evolve
· Subscriptions: Provide real-time updates for specific resources
Together, these concepts form a powerful pattern Discover → Select → Read → React → Repeat
This transforms MCP into more than just a protocol, it becomes a context delivery layer for AI systems.
Previous Next Home
No comments:
Post a Comment