Skip to main content
Glama
UcemaRepo

LeadFunnel MCP

by UcemaRepo
README.md
# LeadFunnel MCP — Render

Servidor MCP con la sesión en **RAM del proceso**. Sin base, sin disco, sin cache.

```
POST /cargar   →  Salesforce → depura → scorea → RAM
POST /mcp      →  Claude consulta (lee de RAM)
POST /purgar   →  RAM = null
GET  /health   →  ¿está vivo? ¿hay sesión?
```

Cuando Render apaga el servicio por inactividad, el proceso muere y los datos
se van con él. Esa es la garantía de efimeridad — no depende de un TTL.

---

## Deploy

1. Subí este repo a GitHub (privado).
2. Render → **New → Web Service** → conectá el repo.
3. Configuración:
   - Runtime: **Node**
   - Build command: `npm install`
   - Start command: `npm start`
   - Health check path: `/health`

### Variables de entorno

| Variable | Valor |
|---|---|
| `SF_DOMAIN` | `ucema2.my.salesforce.com` |
| `SF_CLIENT_ID` | consumer key de la Connected App |
| `SF_CLIENT_SECRET` | consumer secret |
| `MCP_TOKEN` | `openssl rand -hex 32` |
| `ENMASCARAR_PII` | `true` (poné `false` solo si necesitás el dato completo) |

---

## Prueba

```bash
# Salud
curl https://TU-APP.onrender.com/health

# Cargar (tarda: hace todo el pull de Salesforce)
curl -X POST https://TU-APP.onrender.com/cargar \
  -H "Authorization: Bearer $MCP_TOKEN"

# Handshake MCP
curl -X POST "https://TU-APP.onrender.com/mcp?k=$MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

# Purgar
curl -X POST https://TU-APP.onrender.com/purgar \
  -H "Authorization: Bearer $MCP_TOKEN"
```

El header `Accept` con `text/event-stream` es **obligatorio** en Streamable HTTP.
Sin él, el SDK rechaza el request.

Inspector oficial antes de conectar a Claude:
```bash
npx @modelcontextprotocol/inspector
```

---

## Conectar a Claude

`claude.ai → Customize → Connectors → + → Add custom connector`

URL: `https://TU-APP.onrender.com/mcp?k=TU_TOKEN`

---

## Flujo de trabajo diario

1. `POST /cargar` cuando arrancás
2. Consultás desde Claude todo lo que quieras
3. Si te fuiste un rato largo, Render apagó el servicio: volvé a `/cargar`

---

## Limitaciones conocidas

**Cold start.** En el free tier, tras ~15 min sin tráfico Render apaga el
servicio. La siguiente request lo despierta (~50 s) **y la sesión se perdió**.
Si molesta: plan pago, o un cron que le pegue a `/health` cada 10 min (aunque
eso mantiene los datos vivos en RAM indefinidamente, que es justo lo que no
querías).

**Token en la URL.** La UI de conectores de Claude no permite headers
arbitrarios. El token en query param es más débil que un header: queda en la
config del conector y potencialmente en logs. El paso siguiente es OAuth 2.0
real, que con el SDK es factible pero es otro proyecto.

**RAM.** 12k leads ≈ 25 MB en objetos JS. El free tier de Render da 512 MB.
Holgado, pero si el dataset creciera mucho habría que paginar.

---

## Qué se guarda

| Campo | Se guarda |
|---|---|
| Nombre y apellido | sí |
| `sfId` | sí (para saltar al registro) |
| DNI | **nunca** |
| Teléfono | enmascarado (`***4821`) |
| Email | enmascarado (`ni***@gmail.com`) |
| Motivación | solo el largo, no el texto |

Con `ENMASCARAR_PII=false` se guardan teléfono y mail completos. Es una decisión
de riesgo consciente: con esa variable en `false`, los datos de contacto de
candidatos reales entran al contexto del modelo en cada consulta.