Desplegar un servidor MCP en AWS Lambda, Azure y Cloud Run, lado a lado
Un servidor MCP en TypeScript sin estado, tres hosts. Mira la configuración exacta de Lambda Web Adapter, el host.json de Azure Functions para servidores autoalojados y el comando de despliegue de Cloud Run, además de las opciones de autenticación, los tiempos de espera y las contrapartidas del arranque en frío que deciden qué plataforma encaja con tu proyecto.
Tu servidor MCP funciona bien en tu equipo portátil por stdio. Después, un compañero te pide una URL que pueda pegar en un cliente, y empieza el trabajo de verdad. Un servidor remoto necesita HTTPS, autenticación, un transporte que sobreviva a los balanceadores de carga y un host que no te cobre mientras nadie lo llama. Este artículo toma un servidor pequeño en TypeScript y lo pone en tres plataformas: AWS Lambda, Azure Functions y Google Cloud Run. Obtienes la configuración que importa en cada una, el control de acceso que mantiene fuera a los desconocidos y una comparación clara para elegir host en diez minutos en lugar de en una semana.
💡 Alcance: todos los fragmentos de abajo asumen el transporte Streamable HTTP. Stdio sirve para procesos hijos locales, así que un servidor que solo habla stdio necesita un frontend HTTP antes de que cualquiera de estos hosts pueda ejecutarlo.
Elige el transporte antes que la nube
Por qué lo sin estado gana en serverless
Las plataformas serverless arrancan y detienen instancias cuando quieren. La petición uno llega a la instancia A, la dos a la B y la tres provoca un arranque en frío en la C. Si tu servidor guarda una sesión en memoria, esa secuencia la rompe.
La solución es un servidor Streamable HTTP sin estado: un único endpoint /mcp que acepta un POST, responde y olvida. Las tres plataformas están construidas en torno a esa forma. La vista previa autoalojada de Azure solo acepta servidores sin estado sobre el transporte streamable-http. Cloud Run documenta SSE y Streamable HTTP como sus dos opciones remotas, con streaming de respuestas HTTP integrado. Lambda se comporta igual en cuanto pones un adaptador web delante.
Qué cambió la especificación de julio de 2026
La revisión 2026-07-28 de la especificación de MCP apuntó en la misma dirección:
Sin sesiones a nivel de protocolo. La cabecera Mcp-Session-Id desaparece de Streamable HTTP.
Sin handshake. Se eliminó el intercambio initialize, y cada petición lleva ahora su versión del protocolo y las capacidades del cliente en _meta.
Estado mediante handles. Un servidor que necesita memoria entre llamadas crea un handle explícito y lo pasa como un argumento normal de herramienta.
Sin reanudación de streams. Si un stream de respuesta se rompe, la petición en curso se pierde y el cliente debe enviarla de nuevo con un nuevo ID de petición.
HTTP+SSE queda obsoleto. El trabajo nuevo debe usar Streamable HTTP.
En la práctica puedes escribir el servidor como código plano de petición y respuesta y dejar que la plataforma ejecute tantas copias como quiera. Una advertencia: las versiones del SDK van por detrás de las revisiones de la especificación, así que fija la versión del SDK y prueba con los clientes que te importan antes de confiar en un despliegue.
Un servidor, tres destinos
El handler que comparten todos los hosts
El servidor de demostración expone dos herramientas que dan acceso a la API para desarrolladores de PicassoIA: una inicia un trabajo de imagen y la otra lo consulta. La API es de estilo Replicate y asíncrona, con una URL base de https://api.picassoia.com/v1, autenticación Bearer, POST /models/{owner}/{name}/predictions para iniciar un trabajo y GET /predictions/{id} para leerlo. Separar el trabajo en iniciar y consultar mantiene cada petición corta, lo que encaja con plataformas que cobran por milisegundo. El trabajo de abajo apunta a PicassoIA Image mediante su slug picassoia/picassoia-image.
Crear un servidor y un transporte nuevos por petición es el patrón sin estado de los ejemplos del SDK, y apenas cuesta nada, porque registrar dos herramientas es barato. Los nombres de los métodos cambian entre versiones del SDK, así que ajusta el fragmento a la versión que instales y revisa los campos de petición y respuesta en la documentación de la API de PicassoIA.
Los secretos quedan fuera de la imagen
No incluyas nada sensible en el contenedor. Lee PICASSOIA_API_TOKEN desde el almacén de secretos de la plataforma: AWS Secrets Manager o SSM Parameter Store en Lambda, un ajuste de la aplicación que apunte a una bóveda gestionada en Azure y Google Secret Manager en Cloud Run.
💡 Las cuentas de PicassoIA permiten 5 predicciones simultáneas, compartidas entre tokens y conexiones MCP. Limita el paralelismo de tu host con la concurrencia reservada de Lambda, --max-instances y --concurrency en Cloud Run, o con el número máximo de instancias de Azure, en lugar de descubrir el límite en producción.
Desplegar en AWS Lambda
Configuración de Lambda Web Adapter
La ruta menos invasiva ejecuta tu aplicación Express sin cambios a través de Lambda Web Adapter. Para una imagen de contenedor, basta con una línea adicional:
FROM public.ecr.aws/docker/library/node:22-slim
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter
ENV PORT=8080 AWS_LWA_INVOKE_MODE=response_stream AWS_LWA_READINESS_CHECK_PATH=/health
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY dist ./dist
CMD ["node", "dist/server.js"]
¿Prefieres paquetes zip? Adjunta la capa del adaptador, fija AWS_LAMBDA_EXEC_WRAPPER en /opt/bootstrap y apunta el handler a un script de arranque. El adaptador lee el puerto de AWS_LWA_PORT (si no existe usa PORT, por defecto 8080) y comprueba la ruta de disponibilidad antes de reenviar tráfico.
URL de función y streaming de respuestas
Coloca delante una Function URL y fija su modo de invocación en RESPONSE_STREAM, igual que la variable del adaptador de arriba. El modo almacenado por defecto retiene toda la respuesta hasta que la herramienta termina, lo que anula el streaming. Lambda te da hasta 15 minutos por invocación y hasta 10 GB de memoria, mucho más de lo que necesita una herramienta de iniciar y consultar.
Existen dos vías de acceso:
AWS_IAM Function URL. Quien llama firma las peticiones con SigV4. Va bien para tráfico entre servicios y resulta incómodo para clientes MCP de escritorio.
NONE más tu propia comprobación. Ejecuta OAuth dentro del servidor o pon un autorizador delante. Un autorizador de Cognito o de Lambda a través de API Gateway es la opción habitual, pero su tiempo de espera de integración por defecto ronda los 30 segundos, así que las llamadas largas favorecen la Function URL.
El Serverless Framework v4 puede configurar todo esto con unas pocas líneas de YAML:
mcp:
servers:
images:
server: index.ts
💡 Ese artículo señala dos trampas: el inicio de sesión interactivo con OAuth necesita un dominio personalizado en la raíz, en lugar de la URL execute-api por defecto, y Cognito no tiene registro dinámico de clientes.
Desplegar en Azure Functions
El host.json que importa
Azure ejecuta los servidores creados con el SDK como handlers personalizados: el host de Functions recibe la petición y la envía a tu proceso. La documentación de Microsoft sobre MCP autoalojado ofrece este archivo mínimo para un servidor en TypeScript, y el inicio rápido de Node lo muestra en un proyecto que funciona:
El perfil mcp-custom-handler activa el proxy HTTP, dirige todas las rutas ({*route}) a tu servidor y elimina el prefijo de ruta, de modo que /mcp llega sin tocar. Haz que el valor port coincida con el puerto en el que escucha tu servidor. Prueba en local con func start, ya que el depurador con F5 aún no está soportado, y luego publica con func azure functionapp publish <APP_NAME>.
Límites de la vista previa e inicio de sesión con Entra
Lee la letra pequeña antes de comprometerte: esta función está en vista previa pública. Solo admite servidores streamable-http sin estado, escritos con los SDK de Python, TypeScript, C# o Java, y la aplicación debe ejecutarse en el plan Flex Consumption. Si necesitas estado, Microsoft te remite a la extensión de MCP de Functions. Flex Consumption puede mantener instancias siempre listas para reducir los arranques en frío, a cambio de pagar por capacidad inactiva.
La autenticación es el punto fuerte de Azure. La autenticación de servidor integrada de la plataforma implementa por ti los requisitos de autorización de MCP: emite el desafío 401, publica el documento de metadatos del recurso protegido y envía a los clientes a Microsoft Entra ID para iniciar sesión. La versión ampliada host.json de la documentación fija defaultAuthorizationLevel en anonymous y deja el inicio de sesión en esa capa de la plataforma, así que actívalo antes de hacer pública la URL.
Desplegar en Cloud Run
Un solo comando desde el código fuente
Cloud Run es el que menos ceremonia exige. Con un Dockerfile o un proyecto de Node en la carpeta:
¿Ya tienes una imagen? gcloud run deploy --image IMAGE_URL --port PORT hace el trabajo. Cloud Run inyecta PORT, y el servidor debe escuchar en 0.0.0.0, algo que ya hace el handler compartido. La línea del adaptador en el Dockerfile de la sección de Lambda es solo un archivo inerte aquí, así que una misma imagen puede servir a ambas plataformas.
Privado por defecto
Una URL nueva de Cloud Run exige el rol de IAM Cloud Run Invoker (roles/run.invoker) en cada petición. Para un cliente local, la documentación de Google recomienda un proxy que inyecte tu identidad:
gcloud run services proxy mcp-images --region us-central1 --port=3000
Después apunta el cliente a http://localhost:3000/mcp. Quienes hacen llamadas automatizadas pueden enviar un token OIDC de ID como Authorization: Bearer <token>, con la audiencia (audience) fijada a la URL run.app del servicio. Los llamantes que se ejecutan en Cloud Run tienen más opciones, como un sidecar, la autenticación estándar entre servicios o Cloud Service Mesh. Un servidor público orientado al consumidor necesita --allow-unauthenticated más OAuth dentro de tu aplicación, y esa es una decisión que tomar a propósito, no por defecto.
Instancias calientes y tiempos de espera
Cloud Run escala a cero por defecto. Añade --min-instances 1 si los arranques en frío te afectan, y ten en cuenta el costo de la instancia inactiva. Las peticiones pueden durar hasta 60 minutos con --timeout (el valor por defecto son 5 minutos), el límite más alto de los tres, y el streaming de respuestas HTTP no necesita ningún interruptor extra.
Comparación lado a lado
Pregunta
AWS Lambda
Azure Functions
Cloud Run
Empaquetado
Imagen de contenedor con el Web Adapter, o zip más capa
Handler personalizado más host.json
Imagen de contenedor o despliegue desde el código fuente
Servidores con estado
Evitar
No en la vista previa autoalojada
Evitar
Petición más larga
15 minutos
Según el plan Flex Consumption
60 minutos
Opciones de inicio de sesión
Function URL con IAM, autorizador de Cognito o de Lambda
Autenticación integrada con Entra ID
Rol Invoker o token OIDC de ID
Instancias calientes
Concurrencia aprovisionada
Instancias siempre listas
--min-instances
Estado para MCP autoalojado
Funciona a través del adaptador
Vista previa pública
Ruta de alojamiento documentada
¿Qué host encaja con cada equipo?
Ya en AWS con tráfico irregular: Lambda. Pagas por petición y nada mientras está inactivo.
Equipo que trabaja con Microsoft y Entra ID: Azure Functions. La autenticación integrada te ahorra escribir una capa de OAuth, siempre que una función en vista previa sea aceptable.
Equipo pequeño con llamadas largas: Cloud Run. La menor ceremonia y el tiempo de espera más largo.
Si no sabes decidirte, crea primero una imagen de contenedor. Funciona en Cloud Run tal cual, funciona en Lambda a través del adaptador y el mismo código se ejecuta detrás del handler personalizado de Azure.
Prueba el endpoint antes que los clientes
Ejecuta MCP Inspector con npx @modelcontextprotocol/inspector, elige Streamable HTTP, pega tu URL /mcp y lista las herramientas. Luego haz la prueba que la gente se salta: llama a la URL sin credenciales.
curl -i -X POST "$URL/mcp" -H "Content-Type: application/json" -d '{}'
Un 401 o un 403 significa que la puerta de entrada resiste. Cualquier otra respuesta quiere decir que la petición ha superado tu autenticación, y la cuenta de PicassoIA que hay detrás paga por lo que haga después quien llama.
3 errores comunes
Enlazar a localhost.127.0.0.1 funciona en el equipo portátil y falla detrás de cada una de estas plataformas. Enlaza a 0.0.0.0.
Guardar el estado en memoria. Un contador o una caché que vive en el proceso desaparece en el siguiente arranque en frío. Usa handles explícitos o un almacén externo.
Almacenar en búfer el stream. El modo de invocación por defecto de Lambda es almacenado, y un proxy intermedio puede hacer lo mismo. Si los mensajes de progreso llegan todos de golpe, busca un búfer.
Redacta e ilustra con PicassoIA
La misma plataforma que le da a tu servidor algo que llamar también puede escribir el código que lo rodea y crear las imágenes para su documentación.
Usa Claude Sonnet 5 en PicassoIA
Un modelo de programación te lleva de los fragmentos de arriba a un servidor que encaja con tus propias herramientas. Claude Sonnet 5 gestiona tareas de programación de varios pasos y uso de herramientas, y lee imágenes, así que una captura de un despliegue fallido puede ir directamente en la petición.
Pega un prompt que nombre el transporte, las herramientas y el host, por ejemplo: "Escribe un servidor MCP en TypeScript con Streamable HTTP, sin estado, con dos herramientas, start_job y get_job, listo para Cloud Run."
Rellena una vez el prompt de sistema para que cada respuesta siga tus reglas: sin estado, enlazar a 0.0.0.0, leer PORT, sin sesiones en memoria.
Elige un nivel de esfuerzo que encaje con la tarea, según la tabla de abajo.
Deja max_tokens en el valor por defecto de 8192 para respuestas de un solo archivo, y pide un archivo cada vez si una respuesta se corta.
Adjunta una imagen cuando tengas una captura del log. El ajuste max_image_resolution viene por defecto en 0,5 megapíxeles y la reduce antes de enviarla.
Parámetro
Ajuste sugerido
Úsalo para
effort
low (por defecto)
Retoques de configuración y arreglos de una línea
effort
high o max
Flujos de autenticación y errores que afectan a varios archivos
max_tokens
8192 (por defecto)
Un archivo por respuesta
system_prompt
Las reglas de tu hosting
Resultados coherentes en todo un proyecto
image
Captura del error
Depurar los logs de despliegue
Para una segunda opinión sobre un error de autenticación complicado, ejecuta el mismo prompt con GPT 5.6 Sol y compara las dos respuestas.
Genera tus propias imágenes
Una vez que el servidor esté en marcha, necesitará una cabecera para el README, un fondo para el diagrama y una tarjeta para redes sociales. PicassoIA Image convierte un prompt sencillo en una imagen terminada en segundos, con siete relaciones de aspecto desde 1:1 hasta 16:9, una semilla bloqueable para resultados reproducibles, salida en JPG, PNG o WebP y hasta dos variaciones por ejecución. Se describe como ilimitado, sin tope por imagen, así que puedes iterar con libertad. Cuando una imagen fija merece movimiento, PicassoIA Video la anima en un clip corto.
Prueba este prompt: un loft tranquilo de oficina al atardecer, un equipo portátil abierto sobre un escritorio de roble, luz suave de ventana, fotografía de 35 mm, grano de película. Cambia un detalle, bloquea la semilla, genera de nuevo y compara las dos. Abre Picasso IA, ejecuta tu primer prompt y mira cómo será tu próxima imagen. Todos los modelos están en picassoia.com/en/all-models, así que hay mucho con lo que experimentar.