mcp-distributed-sse-poc
by hectorcast44
README.md
# PoC: Protocolo MCP Distribuido sobre HTTP/SSE (TypeScript)
Esta Prueba de Concepto (PoC) implementa la especificación del **Model Context Protocol (MCP)** en una arquitectura distribuida de red de 3 capas en Node.js y TypeScript, sin dependencias de LLMs externos. Demuestra el handshake, la negociación de capacidades, el descubrimiento dinámico de herramientas y la ejecución remota de herramientas mediante **Server-Sent Events (SSE)** y **HTTP POST**.
---
## 🏗 Arquitectura de las 3 Capas
```
┌─────────────────────────────────────────────────────────────────────────┐
│ CAPA 3: MCP HOST │
│ (src/host.ts) │
│ - Orquesta la aplicación cliente. │
│ - Controla el flujo secuencial: descubrimiento y llamadas a tools. │
└────────────────────────────────────┬────────────────────────────────────┘
│ (Invoca API del cliente)
┌────────────────────────────────────▼────────────────────────────────────┐
│ CAPA 2: MCP CLIENT │
│ (src/client.ts) │
│ - Implementa SSEClientTransport (@modelcontextprotocol/sdk). │
│ - Inicia GET /sse para handshake y recibe evento 'endpoint'. │
│ - Envía mensajes JSON-RPC (initialize, tools/list, tools/call) POST. │
│ - Recibe resultados JSON-RPC de forma asíncrona vía SSE stream. │
└────────────────────────────────────┬────────────────────────────────────┘
│ Red IP / HTTP (LAN, VPN o Internet)
│ GET /sse (Server -> Client stream)
│ POST /messages?sessionId=...
┌────────────────────────────────────▼────────────────────────────────────┐
│ CAPA 1: MCP SERVER │
│ (src/server.ts) │
│ - Servidor HTTP (Express) escuchando en 0.0.0.0:3001 │
│ - Implementa SSEServerTransport (@modelcontextprotocol/sdk). │
│ - Expone endpoints /sse (stream) y /messages (POST). │
│ - Valida parámetros estrictos con Zod. │
│ - Herramientas: `calculate_loan_amortization` y │
│ `analyze_investment_projection`. │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 🛠 Herramientas Disponibles para Razonamiento del Agente
1. **`calculate_loan_amortization`**:
- **Propósito**: Evalúa la deuda financiera y el impacto de prepagos a capital (sistema francés).
- **Parámetros**:
- `principal` (número > 0): Monto total financiado.
- `annualInterestRate` (número > 0): Tasa de interés anual (%).
- `termMonths` (entero > 0): Plazo en meses.
- `extraMonthlyPayment` (número opcional): Pago mensual adicional a capital.
- **Métricas devueltas**: Cuota mensual regular, desembolso total, intereses pagados, meses y dinero ahorrados por aceleración.
2. **`analyze_investment_projection`**:
- **Propósito**: Evalúa crecimiento compuesto de patrimonio, poder adquisitivo real deflactado y escenarios por riesgo.
- **Parámetros**:
- `initialCapital` (número >= 0): Capital inicial.
- `monthlyContribution` (número >= 0): Aportación mensual.
- `annualReturnRate` (número): Rendimiento nominal anual estimado (%).
- `timeHorizonYears` (entero > 0): Horizonte temporal en años.
- `annualInflationRate` (número opcional, default: 4.0%): Inflación estimada.
- `riskProfile` (`"conservative"` | `"moderate"` | `"aggressive"`): Perfil de volatilidad.
- **Métricas devueltas**: Capital nominal final, ganancias netas nominales, poder de compra real deflactado, escenarios pesimista/esperado/optimista y tasa real anual.
### 🧠 ¿Cómo probar el razonamiento de un Agente con estas herramientas?
Puedes plantearle al agente problemas de toma de decisiones como:
> *"Tengo $50,000 libres en el banco y un crédito automotriz de $250,000 con tasa del 12.5% a 48 meses. ¿Me conviene aportar esos $50,000 a capital para reducir mi deuda o invertirlos a 4 años con un rendimiento estimado del 11% anual moderado e inflación del 4%? Analiza ambas opciones con las herramientas disponibles y dame tu recomendación fundamentada."*
El agente deberá:
1. Identificar ambas herramientas en el catálogo (`tools/list`).
2. Mapear y ejecutar `calculate_loan_amortization` para medir el costo de los intereses.
3. Mapear y ejecutar `analyze_investment_projection` para calcular el rendimiento real.
4. Contrastar el costo de oportunidad financiero y formular una conclusión razonada.
---
## 🔄 Flujo Detallado de Red y Handshake MCP
1. **Apertura de Canal SSE:**
- El cliente realiza una solicitud HTTP `GET /sse`.
- El servidor responde con `Content-Type: text/event-stream` y mantiene abierta la conexión.
- El servidor genera un `sessionId` (UUID) único y emite inmediatamente un evento SSE llamado `endpoint`:
```http
event: endpoint
data: /messages?sessionId=df193f22-9b80-4297-9039-2e818054826d
```
2. **Handshake de Inicialización:**
- El cliente recibe el endpoint de retorno y envía una solicitud JSON-RPC `initialize` vía `POST /messages?sessionId=...`.
- El servidor procesa la inicialización y envía la respuesta JSON-RPC con sus capacidades y versión a través del **stream SSE**.
- El cliente confirma enviando la notificación `notifications/initialized` vía POST.
3. **Descubrimiento y Ejecución de Herramientas:**
- **`tools/list`**: El cliente envía `POST /messages?sessionId=...` con el método `tools/list`. El servidor responde por SSE con el catálogo y los esquemas Zod convertidos a JSON Schema.
- **`tools/call`**: El cliente envía `POST /messages?sessionId=...` con el método `tools/call`, el nombre de la herramienta y sus argumentos. El servidor ejecuta la lógica y devuelve el resultado enriquecido vía SSE.
4. **Desconexión Limpia:**
- El cliente cierra el canal SSE; el servidor detecta el evento `close`, elimina la sesión de memoria y libera los recursos.
---
## 🚀 Requisitos Previos
- Node.js v18 o superior.
- Dependencias instaladas:
```bash
npm install
```
---
## 🖥 Instrucciones de Ejecución
### Opción A: Prueba Local (Misma máquina, dos terminales)
**Terminal 1 (Servidor MCP):**
```bash
npm run start:server
```
El servidor quedará a la espera en `http://0.0.0.0:3001`.
**Terminal 2 (Host MCP):**
```bash
npm run start:host
```
Por defecto se conectará a `http://localhost:3001/sse`.
---
### Opción B: Entorno Distribuido en Red (Dos máquinas distintas)
1. **En la Máquina Servidor (ej. IP `192.168.1.50`):**
```bash
npm run start:server
```
*(Asegúrate de que el puerto `3001` no esté bloqueado por el firewall).*
2. **En la Máquina Cliente / Host:**
Puedes indicar la URL mediante argumento de línea de comandos o variable de entorno:
**Vía argumento CLI:**
```bash
npm run start:host -- http://192.168.1.50:3001/sse
```
**Vía variable de entorno:**
```bash
SERVER_URL=http://192.168.1.50:3001/sse npm run start:host
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues