What are MCP Resources?
Understand application-controlled data and URI schemes in MCP.
What You'll Build
In this lesson, you'll build resources.py — a reusable resource
registry module, plus file_example.py to demonstrate it. By the end, your server will:
- Scan a directory for files with allowed extensions
- Handle
resources/listto advertise available files - Handle
resources/readto return file contents - Return text content for readable files, base64 for binary
- Include security checks to prevent directory traversal
Each step builds on the previous one, and you'll test your server with curl commands.
Resources: Application-Controlled Data
Resources are one of the three core primitives in MCP, alongside Tools and Prompts. While tools let the AI do things, resources let the AI read things — they expose data that can be attached to the conversation context.
Unlike tools (which the AI decides when to call), resources are application-controlled. The client application decides which resources to attach to the conversation — for example, when a user opens a file in an IDE, the IDE might automatically attach that file as a resource.
How Resources Fit In
┌─────────────────────────────────────────────────────────────┐
│ MCP Server │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ TOOLS │ │ RESOURCES │ │ PROMPTS │ │
│ │ │ │ │ │ │ │
│ │ Model calls │ │ App attaches │ │ User selects │ │
│ │ when needed │ │ to context │ │ from menu │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ │
│ Model-controlled App-controlled User-controlled │
└─────────────────────────────────────────────────────────────┘
URI Schemes
Every resource in MCP is identified by a URI (Uniform Resource Identifier). The URI scheme tells clients what kind of resource it is:
| Scheme | Example | Use Case |
|---|---|---|
file:// |
file:///home/user/config.json |
Local files |
http:// |
http://api.example.com/data |
Web resources |
db:// |
db://mydb/users/123 |
Database records |
custom:// |
slack://channel/general |
Custom integrations |
A URI is an identifier — it names a resource. A URL is a locator — it tells you how to access it. In MCP, we use URIs because the server handles the actual data retrieval; the client just needs to identify what it wants.
Resource Flow
Here's how resources work in practice:
Client Server
│ │
│─── resources/list ────────────────>│ "What resources do you have?"
│<────────────────── list of URIs ───│
│ │
│─── resources/read ────────────────>│ "Give me file:///config.json"
│ { uri: "file:///config.json" } │
│<────────────────── file contents ──│
│ │
Example: Listing Resources
A client asks the server what resources are available:
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/list",
"params": {}
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resources": [
{
"uri": "file:///project/config.json",
"name": "config.json",
"description": "Application configuration",
"mimeType": "application/json"
},
{
"uri": "file:///project/README.md",
"name": "README.md",
"description": "Project documentation",
"mimeType": "text/markdown"
}
]
}
}
When to Use Resources
Resources are ideal when you want to:
- Expose file contents to the AI (configuration, code, documentation)
- Provide database records or API responses as context
- Let the application control what data the AI sees
- Share read-only data (resources are meant to be read, not modified)
Use a resource when you want to provide data for context. Use a tool when you want the AI to be able to take an action or when it needs to decide dynamically what data to fetch.