Lección 5 15 min

Crea tu propio servidor MCP con Python

Construye un servidor MCP con Python FastMCP — @mcp.tool() y @mcp.resource(), manejo de errores, conexión a Claude Desktop y testing local.

🔄 En la lección 4 aprendiste las tres primitivas: Tools, Resources y Prompts. Ahora vas a implementarlas tú mismo.

¿Cuándo necesitas un servidor propio?

Hay 8,600+ servidores MCP disponibles. Pero necesitas uno propio cuando:

  • Tu sistema interno (wiki corporativa, ticketing, BD privada) no tiene servidor público
  • Tienes lógica de negocio específica que no cubre ningún servidor existente
  • Quieres combinar varias APIs en una sola interfaz para la IA

Con FastMCP (SDK de Python), empiezas con 30 líneas de código.

Configurar FastMCP

# Instalar FastMCP
pip install fastmcp

# Verificar
python -c "import mcp; print('OK')"

Tu primer servidor: lista de tareas

Empecemos con algo que funcione inmediatamente.

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("todo-server")

# Lista de tareas en memoria
tareas = []

@mcp.tool()
def agregar_tarea(texto: str) -> str:
    """Agregar una nueva tarea a la lista"""
    tareas.append({"texto": texto, "completada": False})
    return f"Tarea agregada: {texto}"

@mcp.tool()
def listar_tareas() -> str:
    """Mostrar todas las tareas"""
    if not tareas:
        return "No hay tareas"
    return "\n".join(
        f"{'✅' if t['completada'] else '⬜'} {t['texto']}"
        for t in tareas
    )

@mcp.tool()
def completar_tarea(indice: int) -> str:
    """Marcar una tarea como completada"""
    if 0 <= indice < len(tareas):
        tareas[indice]["completada"] = True
        return f"Completada: {tareas[indice]['texto']}"
    return "Índice no válido"

if __name__ == "__main__":
    mcp.run()

Eso es todo. Tres herramientas (agregar, listar, completar) en un servidor MCP funcional.

Quick Check: ¿Por qué agregar_tarea se define como @mcp.tool() y no como @mcp.resource()? (Porque agregar una tarea modifica el estado — tiene efectos secundarios. Los resources son solo lectura.)

Agregar un Resource

Añadamos datos de solo lectura al servidor:

@mcp.resource("todo://resumen")
def resumen_tareas() -> str:
    """Resumen de la lista de tareas"""
    total = len(tareas)
    completadas = sum(1 for t in tareas if t["completada"])
    return f"Total: {total} | Completadas: {completadas} | Pendientes: {total - completadas}"

Los resources se identifican por URI. todo://resumen expone estadísticas que la IA puede consultar como contexto.

Ejemplo práctico: servidor de clima

Un servidor que se conecta a una API externa:

import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

@mcp.tool()
def obtener_clima(ciudad: str) -> str:
    """Obtener el clima actual de una ciudad"""
    url = f"https://api.ejemplo.com/clima?ciudad={ciudad}"
    respuesta = httpx.get(url)
    datos = respuesta.json()
    return f"{ciudad}: {datos['temp']}°C, {datos['condicion']}"

@mcp.tool()
def pronostico(ciudad: str, dias: int = 3) -> str:
    """Obtener el pronóstico de los próximos días"""
    url = f"https://api.ejemplo.com/pronostico?ciudad={ciudad}&dias={dias}"
    respuesta = httpx.get(url)
    return respuesta.json()

FastMCP lee los type hints (ciudad: str, dias: int = 3) y los docstrings para generar automáticamente el schema del tool.

Manejo de errores

En producción, el manejo de errores no es opcional:

@mcp.tool()
def consultar_bd(sql: str) -> str:
    """Ejecutar una consulta SQL (solo lectura)"""
    palabras_prohibidas = ["DROP", "DELETE", "INSERT", "UPDATE"]
    if any(p in sql.upper() for p in palabras_prohibidas):
        return "Error: solo se permiten consultas SELECT"
    try:
        resultado = db.execute(sql)
        return str(resultado.fetchall())
    except Exception as e:
        return f"Error en la consulta: {str(e)}"

Filtramos keywords peligrosos en el SQL para forzar el modo solo lectura, incluso si la IA intenta escribir.

Conectar a Claude Desktop

Añade tu servidor al archivo de configuración:

{
  "mcpServers": {
    "tareas": {
      "command": "python",
      "args": ["/Users/tu-usuario/proyectos/todo_server.py"],
      "env": {}
    }
  }
}

Si empaquetaste el servidor, puedes usar uvx:

{
  "mcpServers": {
    "tareas": {
      "command": "uvx",
      "args": ["todo-server"],
      "env": {}
    }
  }
}

Reinicia Claude Desktop y verifica que aparezcan las herramientas.

Testing local

Antes de conectar a Claude Desktop, prueba tu servidor en local:

# Abre el MCP Inspector en tu navegador
fastmcp dev todo_server.py

El Inspector te permite llamar a cada herramienta manualmente, ver los parámetros y resultados. Muy útil para depurar antes de integrar.

Quick Check: ¿Para qué sirve el comando fastmcp dev? (Abre el MCP Inspector en tu navegador, donde puedes probar cada herramienta de tu servidor sin necesidad de Claude Desktop. Es la herramienta de desarrollo principal.)

Puntos clave

  • FastMCP (Python) permite crear servidores MCP en ~30 líneas de código
  • @mcp.tool() para acciones con efectos secundarios, @mcp.resource() para datos de solo lectura
  • Type hints y docstrings generan automáticamente el schema de herramientas
  • Manejo de errores es obligatorio — filtra SQL peligroso, captura excepciones
  • claude_desktop_config.json para conectar, fastmcp dev para probar

Siguiente lección

Ya sabes crear un servidor. En la lección 6 aprenderás a combinar múltiples servidores con Claude Code — incluyendo Tool Search para optimizar tokens y el patrón de loop agéntico en la práctica.

Comprobación de Conocimientos

1. ¿Cuántas líneas de código necesitas para un servidor MCP mínimo con FastMCP?

2. ¿Cuál es la diferencia entre @mcp.tool() y @mcp.resource()?

3. ¿Cómo se prueba un servidor MCP antes de conectarlo a Claude Desktop?

Responde todas las preguntas para comprobar

Primero completa el quiz de arriba

Skills Relacionados