Fundamentos, arquitectura limpia y estrategia de testing

Introducción

Model Context Protocol (MCP) está emergiendo como el estándar de facto para conectar agentes de IA con capacidades reales de sistemas: APIs, datos, operaciones.

El problema es que está entrando en proyectos por la puerta equivocada.

Estoy viendo equipos que:

  • diseñan directamente “un MCP Server”
  • meten lógica dentro de tools
  • acoplan dominio a protocolo
  • y luego se preguntan por qué no pueden testear ni escalar

Este artículo va a ser claro:

MCP no es tu arquitectura. Es un adaptador.

Y si no lo tratas como tal, vas a construir algo frágil.

1. Qué es MCP (desde el punto de vista arquitectónico)

MCP define un contrato:

  • tools (acciones ejecutables)
  • resources (datos)
  • prompts (plantillas)
  • comunicación vía JSON-RPC

Especificación oficial:
https://modelcontextprotocol.io/specification/2025-03-26

En .NET, ya puedes montar servidores MCP sobre ASP.NET Core:

  • SDK C#
  • integración con HTTP / streaming
  • despliegue en Azure

Ejemplo oficial de Microsoft:
https://learn.microsoft.com/en-us/azure/container-apps/tutorial-mcp-server-dotnet

Lo importante

MCP:

  • expone capacidades
  • no define cómo construirlas

2. El anti-patrón que debes evitar

Este diseño aparece constantemente:

MCP Tool
 ├── lógica de negocio
 ├── validaciones
 ├── llamadas a Azure
 ├── composición de respuesta
 └── retorno al cliente

Problemas:

  • acoplamiento extremo
  • imposible testear en aislamiento
  • difícil evolucionar
  • no reutilizable
  • sin separación de responsabilidades

Esto no escala.

3. Patrón correcto: Clean Architecture + MCP como adapter

La única forma sostenible es separar:

[MCP Adapter]
      │
[Application Layer]
      │
[Domain]
      │
[Infrastructure]

Regla clave

Cada tool MCP debe ser un wrapper fino sobre un caso de uso.

Ejemplo conceptual en .NET:

public class GetCostTool
{
    private readonly IGetCostUseCase _useCase;

    public async Task<GetCostResult> Execute(GetCostInput input)
    {
        return await _useCase.Execute(input);
    }
}

Nada de lógica en el tool.

4. Por qué no debes diseñar “directo a MCP”

Hay razones técnicas, no opiniones.

4.1 El protocolo aún está evolucionando

El propio roadmap lo reconoce:

  • retos en transporte remoto
  • balanceo
  • operación stateless
  • sesiones

Información adicional: https://modelcontextprotocol.io/development/roadmap

4.2 MCP no cubre necesidades operativas

No resuelve:

  • health checks
  • observabilidad estándar
  • control de errores avanzado

De hecho, Microsoft recomienda endpoints adicionales:

/mcp
/health

4.3 Te ata a un canal de entrada

Si todo está dentro de MCP:

  • no puedes exponer REST fácilmente
  • no puedes reutilizar lógica
  • no puedes integrar otros sistemas

5. Testing: separar lo determinista de lo probabilístico

Aquí está la clave que suele confundirse.

5.1 Qué puedes testear de forma determinista

Dominio y aplicación

  • tests unitarios clásicos
  • sin MCP
  • sin LLM

100% determinista

Adaptador MCP (contrato)

Puedes validar:

  • nombre del tool
  • schema de entrada/salida
  • mapping a casos de uso
  • errores

Endpoint MCP

Puedes testear:

  • JSON-RPC correcto
  • estructura de respuesta
  • códigos HTTP
  • streaming

El protocolo está definido para permitir esto: https://modelcontextprotocol.io/specification/2025-03-26

5.2 Qué NO es determinista

La respuesta final de un agente:

Usuario → LLM → decide tool → ejecuta → responde

Aquí entran:

  • decisiones del modelo
  • contexto
  • temperatura

Esto no es testing clásico. Es:

  • evaluación
  • métricas
  • observabilidad

5.3 ¿Necesitas una API REST para testear?

No.

Puedes testear directamente MCP:

  • llamadas HTTP JSON-RPC
  • herramientas como MCP Inspector

https://modelcontextprotocol.io/docs/tools/inspector

REST es útil, pero no obligatorio.

6. Diseño de un MCP Server en .NET

Stack típico:

  • ASP.NET Core
  • ModelContextProtocol.AspNetCore
  • hosting en Azure

Opciones de despliegue:

  • Azure Container Apps
  • Azure App Service
  • Azure Kubernetes Service

Referencia oficial: https://learn.microsoft.com/en-us/azure/container-apps/tutorial-mcp-server-dotnet

Endpoints recomendados

/mcp      → protocolo MCP
/health   → health checks

Nunca uses /mcp como health endpoint.

Nota del autor: siento repetir esto, pero cuando lo ves, lo explicas 1 vez, 2 veces, …, pues insistes otra vez.

7. Problemas reales en producción

Cuando sales de local aparecen problemas serios.

7.1 Transporte

  • streaming HTTP
  • proxies que rompen conexiones
  • buffering inesperado

7.2 Escalado

  • sesiones implícitas
  • balanceadores no preparados
  • pérdida de contexto

7.3 Seguridad

  • autenticación
  • exposición de tools
  • ejecución indirecta

7.4 Observabilidad

  • correlación entre tool calls
  • trazabilidad de decisiones

8. Insight clave

Este es el punto que debes interiorizar:

MCP introduce ejecución remota de capacidades controladas por un modelo.

Eso cambia completamente:

  • el modelo de seguridad
  • el modelo de testing
  • el modelo de arquitectura

9. Patrón recomendado

Estructura de solución

/src
 ├── Domain
 ├── Application
 ├── Infrastructure
 ├── Adapters.Mcp
 └── Adapters.Rest (opcional)

Principios

  • MCP = adapter, no core
  • dominio independiente
  • casos de uso testeables
  • infraestructura desacoplada
  • tools sin lógica

Para terminar

MCP es una pieza clave en la arquitectura de sistemas con IA.

Pero no es el sistema.

Si diseñas directamente alrededor de MCP:

  • pierdes testabilidad
  • pierdes control
  • pierdes capacidad de evolución

Si lo usas correctamente:

  • ganas interoperabilidad con agentes
  • mantienes arquitectura sólida
  • puedes escalar en Azure

No construyas un MCP Server.
Construye capacidades bien diseñadas y expónlas vía MCP.

Nota del autor

Una observación recurrente en proyectos recientes es que muchas de las malas prácticas en torno a MCP aparecen en desarrollos realizados en Python.

Es importante matizar que esto no es un problema del lenguaje en sí —aunque, como cualquier stack, tiene sus limitaciones—, sino principalmente una cuestión de falta de disciplina arquitectónica.

En muchos casos se detecta:

  • ausencia de separación clara entre dominio, aplicación e infraestructura

  • lógica de negocio incrustada en handlers o tools

  • falta de contratos explícitos

  • baja testabilidad desde el diseño

  • falta de testing (derivado de lo anterior)

Python facilita iterar rápido, pero también facilita saltarse estructuras formales si no se imponen desde el inicio. En entornos MCP esto se agrava, porque el protocolo introduce una capa adicional de complejidad (ejecución indirecta, tools, agentes, etc.).

La conclusión no es “evitar Python”, sino:

sin un modelo de arquitectura limpia, cualquier lenguaje deriva rápidamente en sistemas difíciles de mantener, testear y escalar.

En contextos enterprise —especialmente cuando se integran agentes y MCP—, la disciplina arquitectónica deja de ser opcional.