Apidog

Plataforma de desarrollo de API colaborativa todo en uno

Diseño de API

Documentación de API

Depuración de API

Simulación de API

Prueba automatizada de API

Cómo implementar servidores MCP en AWS Lambda

Daniel Costa

Daniel Costa

Updated on April 25, 2025

El Protocolo de Contexto del Modelo (MCP) está surgiendo rápidamente como una forma estándar de habilitar Modelos de Lenguaje Grande (LLMs) como Claude, ChatGPT y otros para interactuar con el mundo exterior. Al definir herramientas de manera estructurada, el MCP permite a los LLMs solicitar acciones, obtener datos en tiempo real o interactuar con APIs externas, yendo más allá de sus datos de entrenamiento estáticos.

No obstante, implementar estos "servidores" MCP (que proporcionan las herramientas) a menudo presenta un desafío, especialmente en entornos de nube modernos. Muchas implementaciones iniciales de MCP estaban diseñadas para ejecución local, comunicándose a través de la entrada/salida estándar (stdio), o utilizando protocolos como Eventos Enviados por el Servidor (SSE) para streaming. Aunque funcionales, estos enfoques a menudo dependen de conexiones persistentes y comportamientos con estado, lo que los convierte en ajustes incómodos para plataformas escalables, sin estado y orientadas a eventos como AWS Lambda.

AWS Lambda ofrece enormes beneficios: escalado automático, eficiencia en costos de pago por uso y cero sobrecarga de gestión de servidores. ¿Cómo podemos cerrar la brecha y ejecutar servidores MCP robustos y listos para producción en este entorno sin servidor?

Entra MCPEngine, una implementación de código abierto en Python de MCP diseñada específicamente para abordar estos desafíos. MCPEngine soporta HTTP transmitible junto con SSE, haciéndolo completamente compatible con el modelo de solicitud/respuesta de AWS Lambda. También agrupa características esenciales para implementaciones en producción, incluyendo soporte de autenticación incorporado y empaquetado simplificado.

Este artículo explora cómo aprovechar MCPEngine para construir y desplegar servidores MCP en AWS Lambda, cubriendo herramientas sin estado, gestión de estado y autenticación.

💡
¿Quieres acabar con las alucinaciones en Cursor?

Apidog-MCP-Server permite que Cursor lea directamente la documentación de la API, que podría ser documentación publicada en línea o incluso archivos OpenAPI locales.

Al hacer de tu diseño de API la fuente de verdad para la IA, el Servidor MCP de Apidog facilita tareas como la generación de código basada en esquemas, búsqueda inteligente a través de puntos finales y asegurando que las modificaciones de código se alineen perfectamente con el contrato de la API, optimizando así el flujo de trabajo de desarrollo.

Conceptos Clave: MCPEngine y Lambda

Antes de profundizar en la implementación, comprendamos los componentes clave de MCPEngine para la integración con Lambda:

  1. MCPEngine: La clase central que orquesta tus herramientas y maneja la comunicación MCP.
  2. @engine.tool() Decorador: Registra una función de Python como una herramienta MCP. El nombre de la función se convierte en el nombre de la herramienta, y su docstring sirve como la descripción proporcionada al LLM.
  3. engine.get_lambda_handler(): Este método genera una función manejadora compatible con AWS Lambda. Expones este manejador, y MCPEngine se encarga de traducir la carga útil del evento de Lambda en solicitudes MCP y de formatear las respuestas.

Construyendo una Herramienta Simple sin Estado

Comencemos con lo básico: una herramienta sin estado desplegada en Lambda. Este ejemplo proporciona una herramienta de saludo simple.

Requisitos Previos:

  • Python 3.8+
  • Una cuenta de AWS con permisos para gestionar Lambda, ECR e IAM.
  • Docker instalado localmente.
  • AWS CLI configurado.

1. Instalar MCPEngine:

pip install mcpengine[cli,lambda]

2. Crear la Aplicación (app.py):

# app.py
from mcpengine import MCPEngine, Context

# Inicializar el motor
engine = MCPEngine()

@engine.tool()
def saludo_personalizado(nombre: str) -> str:
    """
    Genera un saludo amigable para el nombre especificado.
    Usa esta herramienta cuando se te pida saludar a alguien.
    """
    # Lógica simple sin estado
    return f"¡Hola, {nombre}! Bienvenido al mundo sin servidor de MCP."

# Obtener la función manejadora de Lambda
handler = engine.get_lambda_handler()

Este código define una única herramienta, saludo_personalizado, que toma un nombre y devuelve una cadena. La variable handler es la que invocará AWS Lambda.

Flujo de Trabajo de Implementación: Código a la Nube

Implementar una aplicación de MCPEngine en Lambda implica contenerizarla con Docker, subirla a Amazon Elastic Container Registry (ECR) y configurar la función Lambda.

1. Dockerizar la Aplicación (Dockerfile):

# Usar la imagen base oficial de AWS Lambda para Python
FROM public.ecr.aws/lambda/python:3.12

# Establecer el directorio de trabajo en el contenedor
WORKDIR ${LAMBDA_TASK_ROOT}

# Copiar los requisitos primero para aprovechar la caché de Docker
COPY requirements.txt .
# Instalar dependencias (suponiendo que mcpengine está listado en requirements.txt)
# O instalar directamente: RUN pip install --no-cache-dir mcpengine[cli,lambda]
RUN pip install --no-cache-dir -r requirements.txt

# Copiar el resto del código de la aplicación
COPY app.py .

# Establecer el comando para ejecutar la función manejadora (app.handler significa handler en app.py)
CMD ["app.handler"]

(Asegúrate de tener un archivo requirements.txt que liste mcpengine[cli,lambda] o modifica el comando RUN según corresponda).

2. Construir y Subir la Imagen Docker a ECR:

Primero, crea un repositorio ECR (reemplaza <region> y <repo-name>):

aws ecr create-repository --repository-name <repo-name> --region <region>

Toma nota de tu ID de Cuenta de AWS y el URI del repositorio de la salida (<account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>).

Ahora, construye, etiqueta y sube la imagen:

# Autenticar Docker con ECR
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com

# Construir la imagen (usa --platform para construcciones cruzadas si es necesario)
docker build --platform=linux/amd64 -t <repo-name>:latest .

# Etiquetar la imagen para ECR
docker tag <repo-name>:latest <account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest

# Subir la imagen a ECR
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest

3. Crear y Configurar la Función Lambda:

Primero necesitarás un rol de ejecución IAM para Lambda. Si no tienes uno, crea uno básico:

# (Simplificado - ajusta la política de confianza y los permisos según sea necesario)
aws iam create-role --role-name lambda-mcp-role --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "lambda.amazonaws.com"},"Action": "sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name lambda-mcp-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Ahora, crea la función Lambda usando la imagen ECR (reemplaza los marcadores de posición):

aws lambda create-function \
  --function-name mcp-greeter-function \
  --package-type Image \
  --code ImageUri=<account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest \
  --role arn:aws:iam::<account-id>:role/lambda-mcp-role \
  --timeout 30 \
  --memory-size 512 \
  --region <region>

4. Exponer a través de la URL de la Función:

Para hacer que Lambda sea invocable a través de HTTP sin API Gateway, crea una URL de función:

aws lambda create-function-url-config \
  --function-name mcp-greeter-function \
  --auth-type NONE \
  --region <region>

# Agregar permiso para acceso público (ajusta si se necesita autenticación)
aws lambda add-permission \
  --function-name mcp-greeter-function \
  --statement-id FunctionURLAllowPublicAccess \
  --action lambda:InvokeFunctionUrl \
  --principal '*' \
  --function-url-auth-type NONE \
  --region <region>

Toma nota de la URL de función devuelta por el comando create-function-url-config. ¡Tu servidor MCP sin estado ya está en línea!

Gestionando el Estado con el Contexto lifespan

Lambda es sin estado, pero muchas herramientas necesitan acceso a bases de datos, grupos de conexiones u otros recursos inicializados al inicio. MCPEngine aborda esto con el argumento lifespan, que acepta un administrador de contexto asíncrono.

La función lifespan ejecuta su código de configuración (antes de yield) cuando el contenedor Lambda se inicia y su código de limpieza (después de yield) cuando el contenedor se apaga. El valor devuelto se hace disponible en tus funciones de herramienta a través del objeto ctx (Contexto).

Construyamos un registrador de eventos simple que almacene eventos en una base de datos RDS Postgres.

1. Modificar app.py:

# app.py (Ejemplo con Estado)
import os
import psycopg2
from contextlib import asynccontextmanager
from mcpengine import MCPEngine, Context

# Suponiendo que los detalles de la conexión a la base de datos están en variables de entorno
DB_HOST = os.environ.get("DB_HOST")
DB_USER = os.environ.get("DB_USER")
DB_PASS = os.environ.get("DB_PASS")
DB_NAME = os.environ.get("DB_NAME")

@asynccontextmanager
async def db_connection_manager():
    """Maneja el grupo de conexiones a la base de datos."""
    conn = None
    try:
        print("Estableciendo conexión a la base de datos...")
        conn = psycopg2.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASS,
            dbname=DB_NAME
        )
        # Crear tabla si no existe (ejemplo simple)
        with conn.cursor() as cur:
             cur.execute("""
                CREATE TABLE IF NOT EXISTS events (
                    id SERIAL PRIMARY KEY,
                    event_name TEXT NOT NULL,
                    timestamp TIMESTAMP DEFAULT now()
                );
             """)
        conn.commit()
        print("Conexión a la base de datos lista.")
        yield {"db_conn": conn} # Hacer la conexión disponible a través de ctx.db_conn
    finally:
        if conn:
            print("Cerrando conexión a la base de datos.")
            conn.close()

# Inicializar el motor con el administrador de lifespan
engine = MCPEngine(lifespan=db_connection_manager)

@engine.tool()
def registrar_evento(nombre_evento: str, ctx: Context) -> str:
    """Registra un evento con el nombre dado en la base de datos."""
    try:
        with ctx.db_conn.cursor() as cur:
            cur.execute("INSERT INTO events (event_name) VALUES (%s)", (nombre_evento,))
        ctx.db_conn.commit()
        return f"Evento '{nombre_evento}' registrado con éxito."
    except Exception as e:
        # Manejo básico de errores
        ctx.db_conn.rollback()
        return f"Error registrando el evento: {e}"

@engine.tool()
def obtener_ultimos_eventos(limite: int = 5, ctx: Context) -> list[str]:
    """Recupera los últimos eventos registrados de la base de datos."""
    try:
        with ctx.db_conn.cursor() as cur:
            cur.execute("SELECT event_name, timestamp FROM events ORDER BY timestamp DESC LIMIT %s", (limite,))
            eventos = [f"[{row[1].strftime('%Y-%m-%d %H:%M:%S')}] {row[0]}" for row in cur.fetchall()]
            return eventos
    except Exception as e:
        return [f"Error recuperando eventos: {e}"]


# Obtener la función manejadora de Lambda
handler = engine.get_lambda_handler()

2. Consideraciones de Implementación:

  • Base de Datos: Necesitas una instancia RDS accesible (u otra base de datos).
  • Red: Configura la configuración de VPC de la función Lambda para permitir el acceso a la instancia RDS (Grupos de Seguridad, Subredes).
  • Variables de Entorno: Pasa DB_HOST, DB_USER, DB_PASS, DB_NAME como variables de entorno a la función Lambda.
  • IAM: El rol de ejecución de Lambda puede necesitar permisos adicionales si accede a otros servicios de AWS (por ejemplo, Secrets Manager para credenciales de la base de datos).

Actualiza el Dockerfile si es necesario (por ejemplo, para instalar psycopg2-binary), reconstruye/sube la imagen y actualiza el código y la configuración de la función Lambda (variables de entorno, configuración de VPC).

Asegurando Herramientas con Autenticación

Las herramientas de producción necesitan autenticación. MCPEngine se integra con proveedores de OpenID Connect (OIDC) como Google, AWS Cognito, Auth0, etc.

1. Configurar Proveedor OIDC:
Configura un ID de cliente OAuth con tu proveedor elegido (por ejemplo, Google Cloud Console). Necesitarás el ID de cliente y potencialmente el secreto del cliente (dependiendo del flujo).

2. Actualizar app.py para Autenticación:

# app.py (Ejemplo Autenticado - Fragmentos)
import os
# ... otras importaciones ...
from mcpengine import MCPEngine, Context, GoogleIdpConfig # O otra IdpConfig

# ... db_connection_manager ...

# Configurar IDP - usando Google como ejemplo
# Suponiendo que GOOGLE_CLIENT_ID está configurado como una variable de entorno
google_config = GoogleIdpConfig(
   client_id=os.environ.get("GOOGLE_CLIENT_ID")
   # el emisor a menudo se puede inferir, o establecer explícitamente
)

# Inicializar el motor con lifespan y configuración de IDP
engine = MCPEngine(
    lifespan=db_connection_manager,
    idp_config=google_config
)

# Asegurar la herramienta registrar_evento
@engine.auth() # Agregar este decorador
@engine.tool()
def registrar_evento(nombre_evento: str, ctx: Context) -> str:
    """Registra un evento con el nombre dado en la base de datos. Requiere autenticación."""
    # Acceder a la información del usuario autenticado si es necesario: user_email = ctx.user.email
    user_email = ctx.user.email if ctx.user else "desconocido"
    print(f"Usuario autenticado: {user_email}")
    try:
        # ... (la lógica de la base de datos permanece igual) ...
         return f"Evento '{nombre_evento}' registrado con éxito por {user_email}."
    except Exception as e:
        # ... manejo de errores ...
        return f"Error registrando el evento para {user_email}: {e}"

# obtener_ultimos_eventos puede permanecer no autenticada o también ser asegurada
@engine.tool()
def obtener_ultimos_eventos(limite: int = 5, ctx: Context) -> list[str]:
   # ... (la lógica permanece igual) ...


# Obtener la función manejadora de Lambda
handler = engine.get_lambda_handler()

Cambios Clave:

  • Importado GoogleIdpConfig (o el adecuado para tu proveedor).
  • Instanciado MCPEngine con el argumento idp_config.
  • Agregado el decorador @engine.auth() por encima de @engine.tool() para la(s) función(es) que requieren autenticación. MCPEngine rechazará automáticamente las solicitudes sin un token JWT válido verificado contra las claves públicas del IDP.
  • La información del usuario autenticado (de los reclamos JWT) está disponible a través de ctx.user.

3. Implementación:

  • Pasa las variables de entorno necesarias para la autenticación (por ejemplo, GOOGLE_CLIENT_ID) a tu función Lambda.
  • Reconstruye/sube la imagen y actualiza la función Lambda.

Conectando un Cliente LLM

Una vez que tu servidor MCP esté desplegado en Lambda con una URL de Función, puedes conectar clientes compatibles. Usar mcpengine proxy es una forma conveniente de conectar clientes como Claude:

mcpengine proxy <tu-nombre-de-servicio-elegido> <tu-url-de-función-lambda> --mode http --claude

Si usas autenticación:

mcpengine proxy <tu-nombre-de-servicio-elegido> <tu-url-de-función-lambda> \
  --mode http \
  --claude \
  --client-id <tu-id-de-cliente-google> \
  --client-secret <tu-secreto-de-cliente-google> # Necesario para el flujo de adquisición de token

Este comando ejecuta un proxy local al que se conecta Claude. El proxy luego reenvía solicitudes a través de HTTP a tu URL de Función Lambda, manejando el flujo de autenticación si está configurado. El LLM ahora puede descubrir e invocar tus herramientas sin servidor.

Conclusión

Desplegar servidores MCP en AWS Lambda desbloquea una escalabilidad increíble y eficiencia operativa para extender las capacidades de LLM. Las implementaciones tradicionales de MCP a menudo luchan en entornos sin estado, pero MCPEngine proporciona una solución robusta y de código abierto. Al soportar HTTP transmitible, ofrecer gestión de contexto a través de lifespan e integrarse sin problemas con OIDC para autenticación, MCPEngine hace que el MCP sin servidor no solo sea posible, sino práctico para casos de uso en producción. Ya sea construyendo herramientas simples sin estado o aplicaciones complejas, con estado y autenticadas, MCPEngine combinado con AWS Lambda ofrece una plataforma poderosa para la próxima generación de interacciones impulsadas por IA.

💡
¿Quieres una gran herramienta de Pruebas de API que genere hermosa Documentación de API?

¿Quieres una plataforma integrada, Todo-en-Uno para que tu Equipo de Desarrolladores trabaje junto con máxima productividad?

¡Apidog satisface todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
button