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_resources

Mejores 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.