El Problema que Resuelve MCP

Todo asistente de IA — Claude, Copilot, Cursor, el que sea — acaba chocando con la misma pared: el modelo sabe mucho, pero no puede hacer nada fuera de su ventana de contexto. No puede leer tu base de datos, consultar tu API interna ni saber qué hora es, a menos que alguien construya explícitamente ese puente.

Antes de MCP, cada equipo que construía una herramienta basada en IA tenía que cablear sus propias integraciones desde cero. ¿Quieres que Claude busque en tu espacio de trabajo de Notion? Escribe una integración personalizada. ¿Quieres que consulte tu base de datos Postgres? Escribe otra. Cada integración era puntual, frágil y no reutilizable.

Model Context Protocol (MCP) es la respuesta de Anthropic: un estándar abierto que define una interfaz única y universal entre los hosts de IA y el mundo exterior. Construye un servidor MCP una vez y cualquier cliente compatible — Claude Desktop, Claude Code, Cursor, tu propia aplicación — podrá usarlo de inmediato.

Piénsalo como HTTP para herramientas de IA. HTTP no inventó la web, pero le dio a cada navegador y servidor un lenguaje común. MCP hace lo mismo para la IA y los servicios con los que necesita interactuar.


Arquitectura en Tres Partes

MCP tiene tres roles:

Host — La aplicación con la que interactúa el usuario. Claude Desktop, Claude Code, Cursor o tu propia aplicación personalizada. El host contiene un cliente MCP que gestiona las conexiones.

Cliente — Vive dentro del host. Mantiene una conexión 1:1 con un servidor MCP, gestiona la negociación del protocolo y enruta las peticiones del modelo al servidor y las respuestas de vuelta.

Servidor — Un proceso externo (local o remoto) que expone capacidades al modelo. Esta es la parte que tú construyes. Un servidor puede ser tan simple como un único archivo Python.

┌──────────────────────────────────┐
             Host                 
  ┌──────────┐   ┌─────────────┐  
     LLM    │◄──│ MCP Client    
  └──────────┘   └──────┬──────┘  
└─────────────────────────┼────────┘
                            MCP Protocol
              ┌───────────▼──────────┐
                    MCP Server      
                (your code / tool)  
              └──────────────────────┘

Qué Pueden Exponer los Servidores

Un servidor MCP puede exponer tres tipos de capacidades:

Herramientas

Funciones que el modelo puede invocar — el tipo más común y útil. El modelo decide cuándo llamar a una herramienta según la conversación, recibe el resultado y lo incorpora a su respuesta.

@mcp.tool()
def query_database(sql: str) -> str:
    """Ejecuta una consulta SQL de solo lectura y devuelve los resultados en JSON"""
    ...

Recursos

Datos estructurados que el modelo puede leer — archivos, contenidos de bases de datos, respuestas de APIs. Los recursos se identifican mediante URIs y se leen bajo demanda.

@mcp.resource("logs://app/recent")
def get_recent_logs() -> str:
    """Devuelve las últimas 100 líneas del registro de la aplicación"""
    ...

Prompts

Plantillas de prompts reutilizables que el host puede mostrar a los usuarios. Útiles para estandarizar flujos de trabajo comunes.

@mcp.prompt()
def code_review(code: str) -> str:
    return f"Revisa este código en busca de errores, problemas de seguridad y estilo:\n\n{code}"

Transporte: Cómo se Comunican Clientes y Servidores

MCP admite dos mecanismos de transporte:

stdio — El servidor se ejecuta como subproceso del host. La comunicación ocurre a través de stdin/stdout. Es el enfoque estándar para herramientas locales — sin configuración de red, simple y rápido.

HTTP + SSE — El servidor se ejecuta como servicio HTTP. El cliente envía peticiones por HTTP y recibe respuestas en streaming mediante Server-Sent Events. Se usa para servidores remotos o cuando se necesita un servicio de red persistente.

Para el desarrollo local y herramientas personales, stdio es casi siempre la opción correcta.


Construyendo tu Primer Servidor MCP en Python

Instala el SDK:

pip install mcp

A continuación, un servidor MCP completo y funcional que expone algunas herramientas útiles — consultar la hora actual, leer un archivo y hacer una búsqueda web básica a través de la API de DuckDuckGo:

# server.py
from datetime import datetime
from pathlib import Path
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("dev-tools")


@mcp.tool()
def current_datetime() -> str:
    """Devuelve la fecha y hora actuales."""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


@mcp.tool()
def read_file(path: str) -> str:
    """
    Lee un archivo del sistema de archivos y devuelve su contenido.

    Args:
        path: Ruta absoluta o relativa al archivo.
    """
    try:
        return Path(path).read_text(encoding="utf-8")
    except FileNotFoundError:
        return f"Error: archivo no encontrado: {path}"
    except Exception as e:
        return f"Error al leer el archivo: {e}"


@mcp.tool()
def web_search(query: str, max_results: int = 5) -> str:
    """
    Busca en la web usando DuckDuckGo y devuelve un resumen de los resultados.

    Args:
        query: La consulta de búsqueda.
        max_results: Número de resultados a devolver (por defecto 5).
    """
    url = "https://api.duckduckgo.com/"
    params = {"q": query, "format": "json", "no_redirect": 1}
    response = httpx.get(url, params=params, timeout=10)
    data = response.json()

    results = []
    for topic in data.get("RelatedTopics", [])[:max_results]:
        if "Text" in topic and "FirstURL" in topic:
            results.append(f"- {topic['Text']}\n  {topic['FirstURL']}")

    if not results:
        return f"No se encontraron resultados para: {query}"
    return "\n".join(results)


@mcp.resource("env://system")
def system_info() -> str:
    """Devuelve información básica del sistema."""
    import platform
    return (
        f"OS: {platform.system()} {platform.release()}\n"
        f"Python: {platform.python_version()}\n"
        f"Máquina: {platform.machine()}"
    )


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

Ejecútalo directamente para verificar que arranca sin errores:

python server.py

Conectar con Claude Desktop

Claude Desktop lee la configuración del servidor MCP desde un archivo JSON:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Añade tu servidor:

{
  "mcpServers": {
    "dev-tools": {
      "command": "python",
      "args": ["/ruta/absoluta/a/server.py"]
    }
  }
}

Reinicia Claude Desktop. Aparecerá un pequeño icono de enchufe en la interfaz de chat que confirma que el servidor está conectado. Ahora puedes pedirle a Claude que use cualquiera de tus herramientas directamente en la conversación:

"¿Qué hora es?" "Lee el archivo en /home/usuario/notas.txt y resúmelo." "Busca en la web las últimas noticias sobre Rust 2025."

Claude decide de forma autónoma cuándo invocar cada herramienta. No necesitas pedirlo explícitamente — utilizará la herramienta adecuada cuando el contexto lo requiera.


Conectar con Claude Code

En Claude Code (este CLI), los servidores MCP se configuran por proyecto o de forma global. Añade un servidor a la configuración del proyecto:

claude mcp add dev-tools python /ruta/absoluta/a/server.py

O edita .claude/settings.json directamente:

{
  "mcpServers": {
    "dev-tools": {
      "command": "python",
      "args": ["/ruta/absoluta/a/server.py"]
    }
  }
}

Un Ejemplo Más Realista: Herramientas para Bases de Datos

Aquí hay un ejemplo con más parecido a producción — un servidor MCP que expone acceso de solo lectura a una base de datos SQLite:

# db_server.py
import sqlite3
import json
from mcp.server.fastmcp import FastMCP

DB_PATH = "app.db"
mcp = FastMCP("database")


@mcp.tool()
def list_tables() -> str:
    """Lista todas las tablas de la base de datos."""
    with sqlite3.connect(DB_PATH) as conn:
        tables = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
        ).fetchall()
    return json.dumps([t[0] for t in tables])


@mcp.tool()
def describe_table(table: str) -> str:
    """
    Devuelve el esquema de una tabla.

    Args:
        table: Nombre de la tabla.
    """
    with sqlite3.connect(DB_PATH) as conn:
        cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
    return json.dumps([
        {"name": c[1], "type": c[2], "nullable": not c[3], "pk": bool(c[5])}
        for c in cols
    ], indent=2)


@mcp.tool()
def query(sql: str) -> str:
    """
    Ejecuta una consulta SQL SELECT de solo lectura.

    Args:
        sql: Una sentencia SELECT. INSERT/UPDATE/DELETE son rechazadas.
    """
    sql_stripped = sql.strip().upper()
    if not sql_stripped.startswith("SELECT"):
        return "Error: solo se permiten consultas SELECT."

    with sqlite3.connect(DB_PATH) as conn:
        conn.row_factory = sqlite3.Row
        rows = conn.execute(sql).fetchall()

    return json.dumps([dict(r) for r in rows], indent=2, default=str)


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

Con esto conectado a Claude, puedes mantener una conversación en lenguaje natural sobre tu base de datos:

"¿Qué tablas tenemos?" "Muéstrame los últimos 10 pedidos con un valor superior a 500 €." "¿Cuántos usuarios se registraron cada mes este año?"

Claude traduce tu intención a SQL, llama a la herramienta query y presenta los resultados — sin que tengas que escribir la consulta tú mismo.


Consideraciones de Seguridad

Los servidores MCP se ejecutan con los mismos permisos que el proceso que los inicia. Algunas reglas básicas:

  • Expón solo lo necesario. No crees una herramienta genérica run_shell_command a menos que tengas una razón muy sólida.
  • Valida las entradas. Trata los argumentos de las herramientas como cualquier otra entrada externa — provienen de un LLM que puede ser dirigido por usuarios.
  • Aplica solo lectura donde corresponda. El ejemplo de base de datos anterior rechaza todo lo que no sea SELECT.
  • Ten cuidado con el acceso a archivos. Si construyes una herramienta read_file, considera restringirla a un directorio específico en lugar de aceptar rutas arbitrarias.

La Perspectiva Global

MCP es todavía joven, pero la curva de adopción es pronunciada. En pocos meses desde su lanzamiento, aparecieron cientos de servidores — para GitHub, Slack, Postgres, Kubernetes, navegadores web, bases de datos vectoriales y más. Como el protocolo es abierto, cualquier herramienta que construyas funciona en todos los hosts compatibles — hoy Claude Desktop y Claude Code, pronto muchos otros.

El patrón que habilita es poderoso: en lugar de hacer fine-tuning de un modelo para que conozca tus sistemas específicos, le das al modelo la capacidad de consultar en tiempo de ejecución. El modelo permanece general; las herramientas lo hacen específico para tu contexto. Esa separación es más limpia, más mantenible y más fácil de auditar que intentar integrar conocimiento de dominio en los pesos.


Recursos