Recursos MCP
Los recursos son datos y contenido que los servidores MCP exponen a los LLMs. Aprende cómo crear, exponer y gestionar recursos efectivamente.
¿Qué son los Recursos?
Los recursos son datos de solo lectura que un servidor MCP expone al LLM. Permiten que el asistente acceda a información sin poder modificarla directamente. Los recursos se identifican mediante URIs y pueden ser listados, leídos y buscados.
Listar
El cliente puede solicitar una lista de todos los recursos disponibles.
Leer
El cliente puede leer el contenido completo de un recurso específico.
Buscar
El cliente puede buscar recursos que coincidan con una consulta.
Cómo Exponer Recursos desde Servidores
Para exponer recursos en tu servidor MCP, necesitas implementar tres métodos principales: list_resources, read_resource, y opcionalmente search_resources.
Ejemplo: Python SDK
from mcp.server import Server
from mcp.types import Resource, TextContent
import mcp.types as types
import os
app = Server("file-server")
@app.list_resources()
async def list_resources() -> list[Resource]:
"""Lista todos los archivos en un directorio"""
resources = []
base_path = "/path/to/documents"
for root, dirs, files in os.walk(base_path):
for file in files:
file_path = os.path.join(root, file)
uri = f"file://{file_path}"
resources.append(Resource(
uri=uri,
name=file,
description=f"Archivo: {file}",
mimeType="text/plain"
))
return resources
@app.read_resource()
async def read_resource(uri: str) -> str:
"""Lee el contenido de un archivo"""
# Validar URI
if not uri.startswith("file://"):
raise ValueError("URI inválida")
file_path = uri.replace("file://", "")
# Validar que el archivo existe y está permitido
if not os.path.exists(file_path):
raise FileNotFoundError(f"Archivo no encontrado: {file_path}")
# Leer y retornar contenido
with open(file_path, "r", encoding="utf-8") as f:
return f.read()
# Ejecutar servidor
if __name__ == "__main__":
from mcp.server.stdio import stdio_server
stdio_server(app)Ejemplo: TypeScript SDK
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { readFileSync, readdirSync } from "fs";
import { join } from "path";
const server = new Server(
{
name: "file-server",
version: "0.1.0",
},
{
capabilities: {
resources: {},
},
}
);
const BASE_PATH = "/path/to/documents";
server.setRequestHandler(
ListResourcesRequestSchema,
async () => {
const resources = [];
function walkDir(dir: string): void {
const files = readdirSync(dir, { withFileTypes: true });
for (const file of files) {
const fullPath = join(dir, file.name);
if (file.isDirectory()) {
walkDir(fullPath);
} else {
resources.push({
uri: `file://${fullPath}`,
name: file.name,
description: `Archivo: ${file.name}`,
mimeType: "text/plain",
});
}
}
}
walkDir(BASE_PATH);
return { resources };
}
);
server.setRequestHandler(
ReadResourceRequestSchema,
async (request) => {
const uri = request.params.uri;
if (!uri.startsWith("file://")) {
throw new Error("URI inválida");
}
const filePath = uri.replace("file://", "");
const content = readFileSync(filePath, "utf-8");
return {
contents: [
{
uri,
mimeType: "text/plain",
text: content,
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main();Tipos de Recursos
Los recursos pueden representar diferentes tipos de contenido. Cada tipo tiene sus propias características y casos de uso.
Archivos de Texto
Documentos, código fuente, markdown, JSON, etc. El LLM puede leer y analizar el contenido.
Ejemplo:
Archivos de código fuente, documentación, notas personales.
Caso de uso:
Análisis de código, generación de documentación, resúmenes.
Entradas de Base de Datos
Filas, documentos o registros de bases de datos que el servidor puede consultar y exponer.
Ejemplo:
Registros de usuarios, productos, transacciones, logs.
Caso de uso:
Consultas de datos, análisis de información, generación de reportes.
Contenido Web
Páginas web, artículos, posts de blogs que el servidor puede obtener y exponer.
Ejemplo:
Artículos de Wikipedia, posts de blogs, contenido de APIs.
Caso de uso:
Investigación, resúmenes de contenido, análisis de tendencias.
Medios
Imágenes, videos, audio que pueden ser descritos o procesados por el LLM.
Ejemplo:
Imágenes, capturas de pantalla, archivos multimedia.
Caso de uso:
Descripción de imágenes, análisis de contenido visual.
Recursos Estructurados
Datos en formato estructurado como JSON, XML, YAML que el LLM puede procesar.
Ejemplo:
Configuraciones, datos de API, metadatos.
Caso de uso:
Análisis de configuración, transformación de datos, validación.
Contenido Dinámico
Recursos generados en tiempo real basados en consultas o parámetros.
Ejemplo:
Resultados de búsqueda, estadísticas en tiempo real, métricas.
Caso de uso:
Dashboards, monitoreo, análisis en tiempo real.
Ejemplos Prácticos
Aquí tienes ejemplos completos de servidores que exponen diferentes tipos de recursos.
Ejemplo 1: Recursos desde Base de Datos
Expón registros de una base de datos SQLite como recursos MCP
from mcp.server import Server
from mcp.types import Resource
import sqlite3
import json
app = Server("database-server")
DB_PATH = "example.db"
@app.list_resources()
async def list_resources() -> list[Resource]:
"""Lista todos los usuarios como recursos"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("SELECT id, name, email FROM users")
users = cursor.fetchall()
resources = []
for user_id, name, email in users:
resources.append(Resource(
uri=f"db://users/{user_id}",
name=f"Usuario: {name}",
description=f"Información del usuario {name} ({email})",
mimeType="application/json"
))
conn.close()
return resources
@app.read_resource()
async def read_resource(uri: str) -> str:
"""Lee los datos de un usuario específico"""
if not uri.startswith("db://users/"):
raise ValueError("URI inválida")
user_id = uri.split("/")[-1]
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute(
"SELECT id, name, email, created_at FROM users WHERE id = ?",
(user_id,)
)
user = cursor.fetchone()
conn.close()
if not user:
raise ValueError(f"Usuario {user_id} no encontrado")
user_data = {
"id": user[0],
"name": user[1],
"email": user[2],
"created_at": user[3]
}
return json.dumps(user_data, indent=2)Ejemplo 2: Recursos desde API Externa
Expón contenido de una API REST como recursos MCP
from mcp.server import Server
from mcp.types import Resource
import httpx
import json
app = Server("api-server")
API_BASE = "https://api.example.com"
@app.list_resources()
async def list_resources() -> list[Resource]:
"""Lista artículos de una API como recursos"""
async with httpx.AsyncClient() as client:
response = await client.get(f"{API_BASE}/articles")
articles = response.json()
resources = []
for article in articles:
resources.append(Resource(
uri=f"api://articles/{article['id']}",
name=article["title"],
description=f"Artículo: {article['title']}",
mimeType="application/json"
))
return resources
@app.read_resource()
async def read_resource(uri: str) -> str:
"""Lee el contenido completo de un artículo"""
if not uri.startswith("api://articles/"):
raise ValueError("URI inválida")
article_id = uri.split("/")[-1]
async with httpx.AsyncClient() as client:
response = await client.get(
f"{API_BASE}/articles/{article_id}"
)
article = response.json()
return json.dumps(article, indent=2)Ejemplo 3: Búsqueda de Recursos
Implementa búsqueda para encontrar recursos por contenido
from mcp.server import Server
from mcp.types import Resource
import os
import re
app = Server("searchable-file-server")
@app.list_resources()
async def list_resources() -> list[Resource]:
"""Lista todos los archivos"""
resources = []
for root, dirs, files in os.walk("/docs"):
for file in files:
if file.endswith(".md"):
file_path = os.path.join(root, file)
resources.append(Resource(
uri=f"file://{file_path}",
name=file,
description=f"Documento Markdown: {file}",
mimeType="text/markdown"
))
return resources
@app.search_resources()
async def search_resources(query: str) -> list[Resource]:
"""Busca archivos que contengan el texto de búsqueda"""
matching_resources = []
for root, dirs, files in os.walk("/docs"):
for file in files:
if file.endswith(".md"):
file_path = os.path.join(root, file)
# Buscar en el contenido del archivo
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
if query.lower() in content.lower():
matching_resources.append(Resource(
uri=f"file://{file_path}",
name=file,
description=f"Documento que contiene: {query}",
mimeType="text/markdown"
))
except Exception:
continue
return matching_resourcesMejores Prácticas
Sigue estas recomendaciones para crear servidores de recursos eficientes y seguros.
Nombres Descriptivos
Usa URIs claras y descriptivas para tus recursos. Ejemplo: 'file:///docs/api.md' en lugar de 'file:///a/b/c.md'.
Metadatos Ricos
Proporciona descripciones detalladas y metadatos útiles (mimeType, tamaño, fecha de modificación) para cada recurso.
Paginación
Para listas grandes, implementa paginación usando el parámetro 'limit' y 'cursor' para mejorar el rendimiento.
Validación
Valida siempre los parámetros de las solicitudes de recursos antes de acceder a los datos subyacentes.
Caché Inteligente
Considera implementar caché para recursos que no cambian frecuentemente para mejorar la velocidad de respuesta.
Seguridad
Nunca expongas recursos sensibles sin validación. Verifica permisos y sanitiza el contenido antes de exponerlo.
¿Listo para crear tus propios recursos?
Explora más sobre herramientas, prompts y la arquitectura completa de MCP.