# MCP en Producción: Lo que 12 Integraciones Me Enseñaron sobre el Model Context Protocol (y No es lo que Anthropic Te Cuenta)
Tu Server MCP Se Va a Romper a las 3 de la Mañana y No es Culpa del Protocolo
Empecé usando MCP como me dijeron. Como un USB-C para IA.
Conectas un server a un cliente, el modelo descubre las tools, y todo funciona. En local.
*El problema es que en producción no hay "en local". *
Doce integraciones después — desde gestorías fiscales hasta buscas de emergencias — he visto el mismo patrón repetirse. Gente que monta un MCP server en cinco minutos, lo prueba con Claude Desktop, y despliega a producción pensando que ya está.
Luego llega la primera race condition. El primer prompt injection. El primer timeout que arrastra toda la sesión.
*MCP no es un estándar de conexión. Es una arquitectura distribuida que estás tratando como una librería. *
Y el 90% lo descubre cuando el server ya está sirviendo peticiones reales.
He visto startups que levantan seis cifras en inversión caer en la misma trampa. El problema es sistémico: los tutoriales, la documentación oficial y los examples de GitHub muestran un mundo donde el server MCP es un proceso aislado que solo tú usas. Nadie te enseña a gestionar diez, veinte o cien usuarios concurrentes llamando a las mismas tools desde contextos de sesión diferentes.
El contraste es brutal. Mientras Anthropic vende MCP como un protocolo de conexión limpio y estándar — algo parecido a lo que las empresas de contenido promocionan como soluciones plug-and-play — la realidad operativa se parece más a orquestar una infraestructura distribuida sin el soporte de herramientas maduras. Es como si te dieran las especificaciones de USB-C pero sin los chips de negociación, sin los controladores, y esperando que tú implementes el handshake a mano.
---
El Error Que Repite el 90%: Tratar MCP Como un Plugin
❌ Crees que MCP es: Un enchufe donde conectas un server HTTP y el modelo lo llama mágicamente.
✅ La realidad: MCP es un protocolo de comunicación bidireccional sobre JSON-RPC. Tu server tiene que gestionar concurrencia, timeouts, autenticación, rate limiting, y seguridad — todo al mismo tiempo.
El error más común que veo: montar un server Express básico, exponiendo un endpoint, y asumir que el LLM va a llamarlo bien.
Lo que pasa en realidad:
- El modelo lanza 3 tool calls concurrentes
- Tu server procesa la primera, las otras dos hacen timeout
- El modelo reintenta y duplica datos
- El contexto se llena de errores
- El usuario recibe un "lo siento, no pude completar la acción"
*No es un bug de MCP. Es un bug de cómo lo desplegaste. *
Este patrón se repite en cada integración nueva que veo. La gente confunde la simplicidad del desarrollo local con la complejidad del despliegue en producción. Es el mismo error conceptual que cometen los desarrolladores novatos cuando despliegan su primera API REST sin gestionar concurrencia, pero amplificado porque aquí el cliente no es otro servidor predecible: es un LLM que puede lanzar peticiones en cualquier orden, en cualquier momento, y reintentar cuando recibe errores.
Para entender por qué esto escala mal, basta observar cómo funcionan los sistemas distribuidos reales. Un clúster de servidores no asume que las peticiones llegan ordenadas ni que los clientes se comportan bien. Implementa colas, timeouts, circuit breakers y mecanismos de idempotencia. Tu server MCP necesita el mismo nivel de madurez.
En KairosWorks — una plataforma B2B para despostado de carne en España y Portugal — tengo un server MCP que gestiona consultas de certificaciones HACCP y Halal. Si falla una tool call a las 3 de la mañana, no es el modelo quien lo arregla. Es el operario del matadero quien llama a RRHH.
Por eso cada server MCP que despliego pasa por un checklist que no está en la documentación de Anthropic.
Y ojo, no hablo de teoría. En una de esas integraciones, el equipo había desplegado un server que funcionaba perfectamente en pruebas con un solo usuario simulado. En producción, con tres usuarios reales lanzando consultas simultáneas, el server empezó a mezclar respuestas: el certificado Halal de un cliente aparecía en la consulta de otro. No era un bug de seguridad clásico — era que el estado global compartido no distinguía entre sesiones. El modelo, al recibir datos mezclados, empezó a alucinar respuestas incorrectas. El operario perdió dos horas intentando entender por qué el sistema le decía que un lote de cerdo tenía certificación Halal.
---
Los 3 Problemas Reales Que Nadie Te Cuenta en el README
1. Concurrencia: Tu Server Express No Está Preparado
La mayoría de los MCP servers de ejemplo usan HTTP simple o stdio. Funcionan para un solo cliente.
Pero cuando tu aplicación tiene 10 usuarios lanzando queries al mismo tiempo, cada uno con su propio contexto de sesión, tu server empieza a mezclar peticiones.
*El stateless que funciona para REST se rompe con MCP porque el modelo mantiene estado en la ventana de contexto. *
Solución: cada tool call necesita un identificador de sesión único. No vale un global state. Necesitas aislamiento por request.
Si no usas AsyncLocalStorage o un patrón similar, tu server MCP tiene una race condition esperando a ocurrir.
El problema de concurrencia no se limita al estado en memoria. También afecta a recursos compartidos como conexiones de base de datos, archivos temporales o colas de mensajes. Si dos tool calls concurrentes intentan escribir en el mismo registro sin coordinación, el resultado es impredecible. Necesitas bloqueos optimistas, transacciones o algún mecanismo de aislamiento a nivel de base de datos.
Hay quien defiende que usar colas de mensajes como RabbitMQ o Redis resuelve el problema. Y es cierto que ayudan, pero añaden latencia. En MCP, donde cada tool call ya compite por tiempo de ventana de contexto, una latencia extra de 200ms puede ser la diferencia entre una sesión fluida y un timeout que descarrila el razonamiento del modelo.
La alternativa que he visto funcionar mejor en producción es combinar AsyncLocalStorage con un pool de conexiones a base de datos que respete el contexto de sesión. Cada tool call obtiene su propia conexión o su propio slot en el pool, y las escrituras se serializan por sesión. No es perfecto, pero para la mayoría de los casos de uso — consultas de datos, búsquedas, validaciones — es suficiente.
2. Seguridad: El Vector de Ataque Que Nadie Quiere Mirar
MCP expone tools directamente al modelo. Si alguien logra un prompt injection, no está hackeando tu chat — está llamando a funciones reales de tu sistema.
En una de mis integraciones para gestorías, un server MCP daba acceso a consultas de Hacienda. Un prompt injection bien construido podría haber filtrado datos de terceros.
*El modelo no distingue entre "el usuario pidió esto" y "el atacante inyectó esto". *
La solución no es "confiar en que el modelo sea seguro". Es capa de autorización en cada tool:
Cada tool call necesita saber quién la inició. Sino, cualquier inyección en el prompt escala a brecha de seguridad.
El vector de ataque es más amplio de lo que parece. No hablo solo de un atacante externo intentando colar instrucciones en el prompt. También hay que considerar el caso del usuario legítimo que, sin mala intención, escribe una query que el modelo interpreta como una instrucción de acceso a datos que no debería. O el caso del usuario malicioso que explota la ambigüedad del lenguaje natural para engañar al modelo.
Por eso la autorización por tool no es suficiente. Necesitas también rate limiting por usuario y por sesión. Si un mismo sessionId hace 100 tool calls en un minuto, algo huele mal. Probablemente es un ataque automatizado o un bucle de reintentos que se ha descontrolado.
¿Y el logging? Cada tool call debería dejar traza. Quién llamó, qué parámetros usó, qué devolvió, cuánto tardó. Sin eso, cuando ocurra una brecha, no tendrás manera de hacer forensics. En producción, el logging es tu única fuente de verdad para reconstruir incidentes de seguridad.
3. La Ventana de Contexto: El Impuesto Silencioso
Cada tool call en MCP ocupa espacio en la ventana de contexto. El resultado de cada llamada también.
En una sesión larga con 20 tool calls, cada una devolviendo un JSON de 2kB, has consumido 40kB solo en respuestas. Sin contar el histórico de la conversación.
*El modelo se queda sin contexto para razonar porque tus tools devuelven demasiado. *
La solución: paginación y resúmenes en las respuestas de las tools. No devuelvas el dataset entero. Devuelve lo mínimo que el modelo necesita para decidir el siguiente paso.
La ventana de contexto es un recurso finito y caro. Piensa en ella como la memoria RAM de tu sesión. Cada tool call consume una parte, y cuando se llena, el modelo empieza a olvidar el principio de la conversación, las instrucciones del sistema, o peor: empieza a alucinar respuestas porque no tiene suficiente información contextual para razonar.
He visto equipos quejarse de que "el modelo no sigue las instrucciones del sistema" cuando el problema real es que sus tools han llenado la ventana de contexto con datos innecesarios. El modelo no desobedece: simplemente ha perdido las instrucciones originales porque el token budget se ha consumido en respuestas de tools mal diseñadas.
Una métrica útil: mide el tamaño medio de las respuestas de tus tools. Si supera los 5kB, pregúntate si realmente necesitas devolver todos esos datos. Los LLM no necesitan el dataset completo para tomar decisiones. Necesitan información suficiente para determinar cuál es el siguiente paso lógico. El resto sobra.
Otra técnica que funciona bien en producción es cachear respuestas de tools que no cambian frecuentemente. Si la tool getCompanyInfo devuelve siempre los mismos datos para un cliente dado, no tiene sentido que cada llamada consuma contexto con la misma información. Un hash de los parámetros de entrada como clave de caché permite devolver respuestas sin repetir datos, y el modelo puede referirse a resultados previos si necesita consultarlos de nuevo.
---
El Marco de las 3 Preguntas: Mi Framework para No Fracasar con MCP en Producción
Antes de desplegar cualquier server MCP, me hago tres preguntas. Si fallo en una, no despliego.
Pregunta 1: ¿Quién llama a esta tool?
Necesito saber la identidad del usuario final en cada tool call. No vale "el modelo". Vale el usuario real.
Si el server no puede identificar al autor de cada llamada, cualquier prompt injection escala a acceso no autorizado.
Implementación mínima: pasar un sessionId o userId en el contexto de cada llamada. Validar permisos antes de ejecutar.
Pero la identidad no es suficiente. También necesitas el contexto del usuario: su rol, su organización, los permisos específicos sobre los datos que consulta. Una tool que devuelve certificaciones HACCP puede estar disponible para todos los operarios de una planta, pero solo el responsable de calidad debería poder modificar el estado de una certificación.
La arquitectura recomendada es tener un middleware de autenticación que se ejecute antes de cada tool call. Ese middleware resuelve el sessionId contra tu sistema de identidad — sea OAuth, SAML, o una base de datos de usuarios — y adjunta la identidad al contexto de la sesión. Cada tool, entonces, consulta ese contexto para decidir si el usuario tiene permiso para ejecutar la operación solicitada.
Pregunta 2: ¿Qué pasa si esta tool falla?
MCP no tiene reintentos nativos. Si tu tool falla por timeout, el modelo recibe un error y decide qué hacer.
La mayoría de las veces decide "reintentar" — y duplica datos o causa efectos secundarios.
Solución: idempotencia. Cada tool debería poder ejecutarse dos veces sin causar daño. Usa identificadores únicos en las operaciones de escritura.
La idempotencia no es un lujo: es un requisito de producción. Los LLM no manejan errores como lo haría un cliente REST bien educado. Si reciben un timeout, pueden reintentar inmediatamente, o hacerlo después de una pausa, o incluso lanzar tres reintentos en paralelo. No hay garantía de orden ni de cantidad.
Por eso cada tool de escritura debería aceptar un idempotencyKey generado por el cliente (o por un middleware en el server). Ese identificador único permite que la segunda, tercera o décima llamada con los mismos parámetros devuelvan el mismo resultado sin efectos secundarios.
¿Y las tools de solo lectura? Técnicamente son idempotentes por naturaleza, pero cuidado: si tu tool de lectura ejecuta un cómputo pesado o consulta una fuente de datos que cambia rápidamente, puedes tener problemas de consistencia si el modelo hace dos llamadas seguidas y espera el mismo resultado. Aquí la solución es cachear por idempotencyKey durante unos segundos — suficiente para cubrir reintentos inmediatos.
Pregunta 3: ¿Cuánto contexto consume esta respuesta?
Mide el tamaño de cada respuesta de tool. Si una tool devuelve más de 5kB, probablemente estás llenando la ventana de contexto.
Solución: respuestas estructuradas con resúmenes. El modelo necesita datos para decidir, no el dataset completo.
Implementa un sistema de medición. Un simple middleware que registre el tamaño de cada respuesta en bytes y lo compare con un umbral. Cuando una tool supera el límite, el sistema debería lanzar una alerta. No solo para que el desarrollador optimice la tool, sino también para identificar patrones: quizás una tool que antes devolvía 2kB ahora devuelve 20kB porque alguien añadió un campo nuevo sin pensar en el impacto.
El objetivo no es eliminar por completo las respuestas grandes — a veces son necesarias — sino tener visibilidad y tomar decisiones informadas. Si una tool devuelve 50kB pero solo se llama una vez por sesión, quizás es aceptable. Si se llama 20 veces, estás regalando 1MB de contexto que podrías estar usando para el razonamiento del modelo.
---
MCP No Va a Ser Plug-and-Play. Y Está Bien.
La comunidad trata MCP como si fuera el USB-C de la IA. Lo conectas y funciona.
*Pero USB-C funciona porque hay chips, handshakes y protocolos de negociación que ocurren sin que los veas. *
MCP es lo mismo. Necesita infraestructura alrededor. Gestión de sesiones. Autorización. Idempotencia. Rate limiting.
No es culpa del protocolo. Es que el 90% de los tutoriales muestran servers de demostración con una tool que suma dos números. En producción, sumar dos números es el 1% del problema.
El 99% restante es gestionar el estado compartido, la concurrencia, la seguridad y el contexto de cien usuarios diferentes llamando al mismo server.
*El protocolo funciona. La arquitectura alrededor es la que falla. *
Y esa arquitectura no te la va a dar Anthropic. Te la tienes que construir tú — con AsyncLocalStorage, idempotencia en cada tool, y una obsesión enfermiza por cuánto contexto consume cada respuesta.
La buena noticia es que no necesitas inventar nada nuevo. Los patrones que he descrito existen desde hace décadas en sistemas distribuidos, en APIs REST de alto rendimiento, en arquitecturas de microservicios. MCP no introduce problemas nuevos: solo los disfraza bajo una capa de aparente simplicidad. Cuando entiendes eso, todo encaja.
El checklist para producción es simple, aunque no fácil:
- Aislamiento de sesiones:
AsyncLocalStorageo equivalente para que ningún usuario vea datos de otro - Autorización por tool: nunca confíes en que "el modelo ya filtró" — verifica en cada llamada
- Idempotencia en escritura: cada tool de modificación debe tolerar reintentos sin duplicar efectos
- Rate limiting: protege tu server de bucles accidentales o ataques de denegación de servicio
- Logging de cada tool call: quién, qué, cuándo, cuánto tardó, qué devolvió
- Medición de tamaño de respuesta: alertas automáticas cuando una tool supera los 5kB
- Paginación en respuestas grandes: nunca devuelvas datasets completos, solo resúmenes navegables
Cada uno de estos puntos lo he aprendido por las malas. En producción. A las 3 de la mañana.
Resumen:
- MCP no es plug-and-play — es una arquitectura distribuida que requiere gestión de estado, sesiones y concurrencia desde el día uno
- El prompt injection escala a brecha de seguridad si cada tool call no verifica quién la inició
- La ventana de contexto es tu recurso más escaso — respuestas estructuradas y resúmenes, no datasets enteros
- Idempotencia en cada tool de escritura — el modelo va a reintentar cuando falle
- Usa
AsyncLocalStoragepara aislar sesiones, o tus users compartirán estado sin saberlo - Implementa logging y rate limiting desde el día uno, no cuando ya haya ocurrido el primer incidente
El próximo artículo será sobre cómo monté un orquestador multi-servidor MCP para que cada tool sepa qué servers están vivos sin hacer broadcast. Ahí os espero.
Artículos relacionados
- MCP Avanzado: Cómo Construir Servidores que Sobreviven Producción Real
- MCP (Model Context Protocol): La Guía Definitiva para Entender el Protocolo que Invierte la Integración con IA
- MCP (Model Context Protocol): La Guía Definitiva para Entender el Protocolo que Invierte la Integración con IA
- MCP (Model Context Protocol): El 90% lo Trata Como un "USB-C para IA" — y el Talón de Aquiles es la Ventana de Contexto
- MCP (Model Context Protocol): El 90% lo Trata Como un Estándar de API — y el Talón de Aquiles es la Seguridad
---
¿Quieres recibir contenido como este cada semana? Suscríbete a mi newsletter

