The resources/list Method
Implement the method that tells clients what resources are available.
Purpose of resources/list
The resources/list method allows clients to discover what resources
your server provides. Clients call this to get a list of all available resources
and their metadata.
Remember from the MCP handshake: your server must advertise that it supports
resources by including "resources": {} in its capabilities.
Only then will clients call resources/list.
Request Format
The client sends a simple request with no required parameters:
{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/list",
"params": {}
}
Response Format
The server responds with an array of resource objects:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resources": [
{
"uri": "file:///project/config.json",
"name": "config.json",
"description": "Application configuration",
"mimeType": "application/json"
},
{
"uri": "file:///project/src/main.py",
"name": "main.py",
"description": "Main application entry point",
"mimeType": "text/x-python"
},
{
"uri": "db://app/users/current",
"name": "Current User",
"description": "Currently logged in user profile",
"mimeType": "application/json"
}
]
}
}
Implementing in Python
Here's how to implement the resources/list handler:
"""
resource_server.py - MCP server with resources/list support
"""
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
# Define available resources
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"
}
]
def handle_resources_list(params: dict) -> dict:
"""Handle resources/list requests."""
return {
"resources": RESOURCES
}
class MCPHandler(BaseHTTPRequestHandler):
"""Handle MCP requests."""
def send_json(self, data: dict, status: int = 200):
"""Send a JSON response."""
body = json.dumps(data, indent=2).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_POST(self):
"""Handle POST requests."""
# Read and parse request
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length)
request = json.loads(body.decode("utf-8"))
method = request.get("method")
params = request.get("params", {})
request_id = request.get("id")
# Route to appropriate handler
if method == "resources/list":
result = handle_resources_list(params)
self.send_json({
"jsonrpc": "2.0",
"id": request_id,
"result": result
})
else:
self.send_json({
"jsonrpc": "2.0",
"id": request_id,
"error": {
"code": -32601,
"message": f"Method not found: {method}"
}
})
Pagination Support
For servers with many resources, MCP supports pagination using cursors. This prevents overwhelming clients with huge lists.
// Request with cursor
{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/list",
"params": {
"cursor": "eyJvZmZzZXQiOiAxMH0="
}
}
// Response with next cursor
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"resources": [
// ... resources 11-20 ...
],
"nextCursor": "eyJvZmZzZXQiOiAyMH0="
}
}
Implementing Pagination
"""
pagination.py - Resources with pagination support
"""
import base64
import json
PAGE_SIZE = 10
def encode_cursor(offset: int) -> str:
"""Encode offset as a cursor string."""
return base64.b64encode(
json.dumps({"offset": offset}).encode()
).decode()
def decode_cursor(cursor: str) -> int:
"""Decode cursor string to offset."""
try:
data = json.loads(base64.b64decode(cursor))
return data.get("offset", 0)
except Exception:
return 0
def handle_resources_list(params: dict) -> dict:
"""Handle resources/list with pagination."""
# Get cursor from params
cursor = params.get("cursor")
offset = decode_cursor(cursor) if cursor else 0
# Get page of resources
page = RESOURCES[offset:offset + PAGE_SIZE]
# Build response
result = {"resources": page}
# Add next cursor if there are more resources
if offset + PAGE_SIZE < len(RESOURCES):
result["nextCursor"] = encode_cursor(offset + PAGE_SIZE)
return result
Dynamic Resources
Resources don't have to be static. You can generate them dynamically based on the current state of your application:
"""
dynamic_resources.py - Dynamically generated resources
"""
import os
from pathlib import Path
def get_file_resources(directory: str) -> list[dict]:
"""Generate resources from files in a directory."""
resources = []
base_path = Path(directory)
for file_path in base_path.rglob("*"):
if file_path.is_file():
# Determine MIME type from extension
mime_types = {
".py": "text/x-python",
".js": "text/javascript",
".json": "application/json",
".md": "text/markdown",
".txt": "text/plain",
}
ext = file_path.suffix.lower()
mime_type = mime_types.get(ext, "application/octet-stream")
resources.append({
"uri": f"file://{file_path.absolute()}",
"name": file_path.name,
"description": f"File in {file_path.parent.name}/",
"mimeType": mime_type
})
return resources
def handle_resources_list(params: dict) -> dict:
"""Handle resources/list with dynamic file discovery."""
project_dir = os.environ.get("PROJECT_DIR", ".")
resources = get_file_resources(project_dir)
return {"resources": resources}
If generating resources is expensive (e.g., scanning large directories or querying databases), consider caching the results and refreshing periodically or when notified of changes.
Testing resources/list
Test your implementation with curl:
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/list",
"params": {}
}'
Expected 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"
}
]
}
}