Los tres pilares que hacen que la integración de la herramienta LLM sea sorprendentemente sencilla.

Hace seis meses, pasé dos semanas creando un agente de atención al cliente «inteligente». Podía responder preguntas, consultar el estado de los pedidos e incluso procesar reembolsos. Estaba orgulloso de él.

El código de integración fue una pesadilla. Llamadas API personalizadas por todas partes. Análisis JSON que fallaba si faltaba un campo. Una función de 400 líneas solo para gestionar el enrutamiento de herramientas. Pero funcionaba, en su mayor parte.

Luego se lo mostré al equipo.

«¿Cuál es el estado del pedido 12345?», preguntó alguien.

El agente respondió con confianza con los detalles del pedido. Genial. Entonces alguien hizo una pregunta de seguimiento y el agente intentó volver a llamar a la función de búsqueda de pedidos. Excepto que esta vez, mi frágil lógica de análisis se atascó en un caso extremo. Todo se colgó. Delante de todo el mundo.

Pasé esa noche depurando y me di cuenta de que el problema no era mi lógica. Era la arquitectura. Había construido un castillo de naipes al intentar conectar un LLM a herramientas externas utilizando código personalizado. Cada nueva herramienta significaba más análisis personalizado. Cada caso extremo significaba más sentencias if.

Ese fracaso me llevó al MCP.

El Protocolo de Contexto de Modelo (MCP) es un estándar abierto que hace bien una cosa: proporciona a los LLM una forma limpia y coherente de descubrir y utilizar herramientas externas. Se acabó el análisis personalizado. Se acabaron las integraciones frágiles. Solo un protocolo que funciona.

Una vez que entendí MCP, reconstruí ese mismo agente en una tarde. Y no se ha bloqueado desde entonces.

Odio cuando la gente complica demasiado estas cosas. La mayoría de las explicaciones sobre MCP comienzan con especificaciones JSON-RPC y discusiones sobre la capa de transporte. Eso es al revés.

No es necesario comprender el funcionamiento interno del protocolo para utilizarlo, al igual que no es necesario comprender HTTP para crear una aplicación web.

Esto es lo que realmente necesitas: tres conceptos y unos 15 minutos.

Los tres pilares

MCP tiene tres pilares. Eso es todo.

Servidor: lo que expone tus herramientas. Es un script de Python que dice «estas son las funciones que puede llamar un LLM». Lo ejecutas y espera las solicitudes.

Herramienta: una función que quieres que utilice el LLM. Puede ser cualquier cosa: obtener el tiempo, consultar una base de datos, enviar un correo electrónico. La escribes como una función Python normal, añades un decorador y MCP se encarga del resto.

Cliente: lo que se conecta a tu servidor y llama a las herramientas. En producción, suele ser tu aplicación LLM. Para las pruebas, FastMCP te ofrece un cliente que funciona desde el primer momento.

El servidor expone las herramientas. El cliente llama a las herramientas. Ese es todo el modelo mental.

Todo lo demás (transportes, JSON-RPC, negociación de capacidades) son detalles de implementación. No es necesario pensar en ello hasta que se escala a producción.

Vamos a crear uno.

Paso 1: Instalar FastMCP

FastMCP es el marco de trabajo de Python que simplifica MCP. Una instalación, sin configuración.

pip install fastmcp

Eso es todo. No se necesitan entornos virtuales para este tutorial (aunque es recomendable tener uno en producción).

Paso 2: Crear el servidor

Crear un archivo llamado my_server.py:

from fastmcp import FastMCP

# Initialize the server with a name
mcp = FastMCP("my-first-server")

# Define a tool using the @mcp.tool decorator
@mcp.tool
def get_weather(city: str) -> dict:
    """Get the current weather for a city."""
    # In production, you'd call a real weather API
    # For now, we'll return mock data
    weather_data = {
        "new york": {"temp": 72, "condition": "sunny"},
        "london": {"temp": 59, "condition": "cloudy"},
        "tokyo": {"temp": 68, "condition": "rainy"},
    }
    
    city_lower = city.lower()
    if city_lower in weather_data:
        return {"city": city, **weather_data[city_lower]}
    else:
        return {"city": city, "temp": 70, "condition": "unknown"}

# Run the server
if __name__ == "__main__":
    mcp.run(transport="stdio")

Analicemos lo que está sucediendo:

  • FastMCP(«my-first-server») crea tu servidor con un nombre
  • @mcp.tool es el decorador que convierte cualquier función en una herramienta MCP
  • La cadena de documentación se convierte en la descripción de la herramienta (los LLM la utilizan para saber cuándo invocarla)
  • Las sugerencias de tipo (city: str, -> dict) indican a MCP las entradas y salidas esperadas
  • transport=«stdio» significa que el servidor se comunica a través de la entrada/salida estándar (perfecto para pruebas locales)

Eso es todo tu servidor. 15 líneas de código real.

Paso 3: Crea un cliente para probarlo

Crea un archivo llamado test_client.py:

import asyncio
from fastmcp import Client

async def main():
    # Point the client at your server file
    client = Client("my_server.py")
    
    # Connect to the server
    async with client:
        # List available tools
        tools = await client.list_tools()
        print("Available tools:")
        for tool in tools:
            print(f"  - {tool.name}: {tool.description}")
        
        print("\n" + "="*50 + "\n")
        
        # Call the weather tool
        result = await client.call_tool(
            "get_weather", 
            {"city": "Tokyo"}
        )
        print(f"Weather result: {result}")

if __name__ == "__main__":
    asyncio.run(main())

Puntos clave:

  • Client(«my_server.py»)indica al cliente a qué servidor conectarse
  • async with client:gestiona automáticamente el ciclo de vida de la conexión
  • list_tools()descubre qué herramientas están disponibles (esta es la detección dinámica de MCP)
  • call_tool(«get_weather», {“city”: «Tokyo»})invoca la herramienta con parámetros

Paso 4: Ejecutarlo

Abre tu terminal y ejecuta:

python test_client.py

Deberías ver:

Available tools:
  - get_weather: Get the current weather for a city.
==================================================
Weather result: {'city': 'Tokyo', 'temp': 68, 'condition': 'rainy'}

Eso es todo. Acabas de crear un servidor MCP y lo has invocado desde un cliente.

Paso 5: Añadir más herramientas

La potencia de MCP reside en lo fácil que es añadir capacidades. Añadamos dos herramientas más a nuestro servidor:

from fastmcp import FastMCP
from datetime import datetime

mcp = FastMCP("my-first-server")

@mcp.tool
def get_weather(city: str) -> dict:
    """Get the current weather for a city."""
    weather_data = {
        "new york": {"temp": 72, "condition": "sunny"},
        "london": {"temp": 59, "condition": "cloudy"},
        "tokyo": {"temp": 68, "condition": "rainy"},
    }
    city_lower = city.lower()
    if city_lower in weather_data:
        return {"city": city, **weather_data[city_lower]}
    return {"city": city, "temp": 70, "condition": "unknown"}

@mcp.tool
def get_time(timezone: str = "UTC") -> str:
    """Get the current time in a specified timezone."""
    # Simplified - in production use pytz or zoneinfo
    return f"Current time ({timezone}): {datetime.now().strftime('%H:%M:%S')}"

@mcp.tool
def calculate(expression: str) -> dict:
    """Safely evaluate a mathematical expression."""
    try:
        # Only allow safe math operations
        allowed_chars = set("0123456789+-*/.() ")
        if not all(c in allowed_chars for c in expression):
            return {"error": "Invalid characters in expression"}
        
        result = eval(expression)  # Safe because we validated input
        return {"expression": expression, "result": result}
    except Exception as e:
        return {"error": str(e)}
if __name__ == "__main__":
    mcp.run(transport="stdio")

Vuelva a ejecutar su cliente de prueba: detectará automáticamente las tres herramientas:

Available tools:
  - get_weather: Get the current weather for a city.
  - get_time: Get the current time in a specified timezone.
  - calculate: Safely evaluate a mathematical expression.

Sin cambios en la configuración. Sin lógica de enrutamiento. Usted añadió herramientas y MCP las puso a su disposición.

Próximo paso: conexión a un LLM

El cliente que hemos creado es para realizar pruebas. En producción, su marco LLM se conecta como cliente. Así es como se ve conceptualmente:

Cómo MCP conecta su LLM con herramientas externas: el marco llama al cliente, que descubre e invoca herramientas desde su servidor.

El código del servidor que escribiste no cambia. Esa es la ventaja de MCP: creas herramientas una vez y cualquier cliente compatible con MCP puede utilizarlas.

Para implementaciones de producción, también cambiarías del transporte stdio a http:

if __name__ == "__main__":
    mcp.run(transport="http", host="0.0.0.0", port=8000)

Esto expone su servidor MCP como un punto final HTTP al que pueden conectarse los clientes remotos.

La verdadera lección

¿Recuerda ese agente de atención al cliente que se colgó durante mi demostración?

El problema no era que no pudiera escribir código que funcionara. Podía hacerlo. El problema era que estaba resolviendo el problema equivocado. Estaba creando integraciones personalizadas cuando lo que necesitaba era un protocolo estándar.

MCP no es magia. Es fontanería. Buena fontanería.

Se encarga de las tareas aburridas (detección, enrutamiento, serialización) para que puedas centrarte en lo que importa: las herramientas en sí. Tu función meteorológica, tu consulta de base de datos, tu remitente de correo electrónico.

¿La verdadera revelación?

Las buenas abstracciones hacen desaparecer los problemas difíciles.

Ahora tienes un servidor MCP que funciona. En 15 minutos. El siguiente paso es conectarlo a tu LLM real y crear algo útil.

Yo empezaría con cualquier herramienta que desearas que tu agente tuviera la semana pasada.