Manejo 5 Apps SaaS y No Sé Cuál Está Fallando. Así que Le Di a Claude una Herramienta para Monitorearlas Todas

10 min read

43 notificaciones sin leer en Discord. Cinco confirmaciones de backup, todas en verde. Un bot de contenido que publicó 3 artículos en Shopify durante la noche. Una auditoría de créditos marcando un usuario con 10 créditos en lugar de 600. Y un feed RSS que se rompió hace 3 horas pero nadie se dio cuenta porque la alerta quedó enterrada bajo 12 mensajes de éxito.

TL;DR: Cuando manejas múltiples productos en diferentes stacks, tu monitoreo se fragmenta entre canales de Discord, dashboards y reportes de cron hasta que te conviertes en la capa de agregación humana. Construí un servidor MCP personalizado, una herramienta privada que permite a Claude consultar todas mis apps y decirme qué está roto. 16 commits, 4 horas de debugging, y una especificación OAuth que casi me mata. A continuación: lo que costó, qué cambió, y un framework para decidir cuándo necesitas esto vs. cuándo estás sobre-ingeniando.

Todo está bien Y algo se está quemando. Solo que no puedo distinguir qué sin leer las 43, ordenarlas mentalmente por severidad, y luego cruzar referencias entre canales.

Cinco productos SaaS. Convex por aquí, Supabase por allá, crons de n8n por todas partes. Cada producto reporta a su propio canal de Discord en su propio formato. Cada canal mezcla confirmaciones rutinarias con emergencias reales. La relación ruido-señal es de aproximadamente 15 a 1.

El monitoreo funciona. Cada app reporta. Los backups confirman. Las auditorías de créditos marcan. Los pipelines de contenido registran. Los datos existen.

Pero yo me convertí en la capa de agregación.

El tipo que abre 4 canales, 2 dashboards, y un log de ejecución de n8n para reconstruir mentalmente una imagen de "cómo va todo". App 1 está bien. App 2 tiene un problema menor. App 3 tiene un problema que necesitaba atención hace 3 horas. Solo que no lo sabía porque la alerta era la notificación 37 de 43.

Si eres como yo, probablemente intentaste arreglar esto construyendo más herramientas 🤓. Un dashboard personalizado aquí. Un bot de Slack allá. Un script de resumen semanal. Felicidades, ahora tienes 5 productos SaaS Y 3 herramientas de monitoreo que monitorear. No resolviste la fragmentación. Le agregaste una capa.

Se me ocurrió un dashboard unificado. Luego me di cuenta de que eso es un sexto producto que mantener. Y aún requiere que lo mire, lo interprete, decida qué importa. Lo que realmente necesitaba era algo que pudiera consultar las 5 fuentes y responder una pregunta, no otra pantalla.

Así que le di a Claude una herramienta personalizada para acceder a todas mis apps a la vez. La construí como un servidor MCP en Next.js, desplegado en Vercel, conectado a Convex, Supabase, mi API de contenido, y un adaptador de Discord. La idea era simple. La ejecución casi mata el proyecto.

Dashboard con datos de aplicaciones SaaS censurados mostrando métricas de monitoreo
Cuando tus métricas son tan secretas que ni tú las puedes ver

Antes de construir: cuándo realmente necesitas esto

Pongo esta sección primero porque casi construí algo que no necesitaba hace 6 meses, y no quiero que cometas el mismo error. Un servidor MCP personal es la herramienta correcta en un punto específico. Antes de ese punto, es desarrollo impulsado por el currículum.

Si manejas un proyecto en un stack, las notificaciones de Discord y un endpoint de health check lo cubren. Tal vez Grafana. Ves todo desde un lugar. Si estás aquí y pensando en MCP, para. Ve a construir features en su lugar.

¿Dos o tres proyectos en stacks similares? Un dashboard compartido lo cubre. Los scripts CLI manejan verificaciones ad-hoc. Sigo manteniendo que los CLIs superan a MCP para tareas de agentes de propósito único y nada sobre este proyecto cambió esa opinión. Si tu agente necesita ejecutar una acción contra un sistema, un CLI es más simple y predecible. Siempre.

Tres a cinco proyectos en stacks mixtos es donde empieza a doler. Convex aquí, Supabase allá, APIs REST personalizadas, diferentes sistemas de auth, diferentes formas de datos. Ninguna herramienta única los consulta a todos. Tu rutina matutina involucra 3 pestañas y un scroll por Discord. Empiezas a perderte cosas. No porque no te importe, sino porque agregar 4 formatos diferentes antes del café excede lo que un cerebro humano debería estar haciendo a las 7 AM.

Cinco o más, te das cuenta de que eres el middleware. Cada fuente funciona bien individualmente. El problema es la síntesis. "¿Cuál de mis apps necesita atención?" requiere consultar 4 bases de datos, comparar resultados, clasificar por severidad. Un dashboard no puede hacer eso. Un LLM con acceso a tus datos sí puede.

Estaba sólidamente en esa última etapa antes de admitirlo.

El umbral que sugeriría: si pasas más de 15 minutos al día cambiando de contexto entre fuentes de monitoreo, y los datos son accesibles vía API o base de datos, esto se paga solo en la primera semana.

El desastre de 16 commits

Pensé que esto tomaría 10 minutos 😬. Un servidor MCP son unos cuantos route handlers, un par de definiciones de herramientas, desplegar a Vercel, listo. Ya estaba pensando en qué cocinar para la cena cuando empecé.

Cuatro horas después aún estaba debuggeando OAuth. Tomó 4 horas, aproximadamente 40 intercambios de ida y vuelta con Claude Code, unos $8–12 estimados en tokens de API, y 16 commits para obtener un prototipo funcional. Ver el log de git a la mañana siguiente fue humillante.

El servidor MCP en sí es una sola app de Next.js. Seis archivos de rutas, un módulo de utilidades. Declaras herramientas que Claude puede llamar, cada herramienta consulta una fuente de datos, Claude decide qué herramientas usar basándose en tu pregunta.

app/
├── .well-known/
   ├── oauth-authorization-server/route.ts
   └── oauth-protected-resource/route.ts
├── authorize/route.ts
├── token/route.ts
└── mcp/route.ts
lib/
└── oauth.ts

Declarar las herramientas tomó tal vez 30 minutos. Cada una es una función con un nombre, una descripción, un esquema de parámetros, y lógica que consulta Convex o Supabase o una API. Esa es la parte fácil. El log de git te dice exactamente dónde se fue el tiempo.

Primera pared: sesiones MCP. Empecé con sesiones con estado porque eso es lo que muestran los ejemplos del SDK. Desplegué a Vercel. Nada funcionó. Las funciones serverless no mantienen estado entre invocaciones. Dos commits para arrancar eso y volverse stateless. Debería haberlo sabido. No lo hice.

Segunda pared: enrutamiento de Next.js. Tres commits para descubrir que la ruta dinámica del App Router [transport] estaba causando problemas de resolución de rutas y necesitaba una ruta fija /mcp en su lugar. Luego el basePath estaba mal. Luego un favicon de alguna manera estaba interfiriendo. Tâtonnement es la palabra educada.

Luego OAuth.

Siete commits. Aquí es donde casi abandono el proyecto.

Claude.ai requiere OAuth 2.1 para conexiones MCP. La especificación define endpoints de descubrimiento, flujos de autorización, intercambio de tokens, PKCE. En papel es limpio. En la práctica, las restricciones no están documentadas lo suficientemente claro para implementar sin prueba y error. Y cada prueba requiere un despliegue completo de Vercel porque no puedes probar el flujo OAuth de Claude.ai localmente. Desplegar, esperar 30 segundos, hacer clic en conectar en Claude, verlo fallar, leer el error críptico, arreglar, redesplegar. Repetir.

Lo que aprendí quemando esos 7 commits: Claude ignora las URLs de endpoint que declaras en tus metadatos OAuth. Puedes establecer authorization_endpoint a /oauth/authorize todo el día. Claude lee el campo issuer y construye las rutas él mismo. Tus endpoints deben estar en /authorize y /token en la raíz, punto. Descubrí esto después de desplegar 3 veces con diferentes configuraciones de rutas.

Un tipo diferente de falla: mis route handlers devolvían Response.json(). API Web estándar. Funciona localmente. Funciona en tests. En Vercel en producción, devuelve una respuesta vacía sin error, sin log, sin indicación de que algo salió mal. Solo un 200 en blanco y un flujo OAuth roto. La solución es NextResponse de next/server. Encontré esto en un issue de GitHub con 3 upvotes enterrado en un hilo sobre algo completamente diferente.

Y el que me costó una noche: establecí mi secreto JWT usando echo "value" | vercel env add. El comando echo agrega un salto de línea. El salto de línea se convierte en parte del secreto. Cada firma JWT falla silenciosamente. Cada intercambio de token devuelve "invalid_grant." Los logs no dicen nada útil. Ejecuté printf en lugar de echo, redespliegué, y todo funcionó. Me quedé mirando el techo un rato después de eso 💀

Ese fue el commit 14 de 16. Los últimos dos fueron limpieza.

Todo habría sido 3 o 4 commits limpios si las restricciones hubieran estado documentadas. Pero ese es siempre el juego cuando estás integrando con sistemas cuyo comportamiento exacto descubres desplegando y fallando. La especificación MCP aún es joven. La implementación de Claude.ai tiene bugs conocidos, especialmente alrededor del refresh de tokens. Trata cualquier integración MCP como un prototipo de producción por ahora. Lo suficientemente estable para uso diario. No algo que enviaría a clientes pagos sin más endurecimiento.

Para el panorama más amplio de cómo estos servicios se conectan, documenté la arquitectura completa con cron jobs, memoria, y dashboard en un artículo anterior. El servidor MCP se conecta a ese mismo patrón.

Cómo se ve realmente ahora

Después del bautismo de 16 commits, aquí es donde están las cosas.

Abro Claude y escribo:

¿Algo que necesite mi atención esta mañana?

Claude consulta el servidor MCP. El servidor consulta Convex para datos de usuarios y créditos, Supabase para salud de backups y crons, la API de contenido para estado de publicación, y un adaptador de Discord para alertas recientes filtradas por severidad. La primera vez que ejecuté esto, la respuesta tomó 11 segundos y los datos de crédito regresaron como un blob JSON crudo que Claude alegremente describió como "alguna información financiera." No exactamente la experiencia de cabina pulida.

Después de ajustar las descripciones de herramientas y el formato de respuesta, devuelve algo realmente útil:

2 elementos necesitan atención:

1. Anomalía de créditos en [ClientApp]: usuario sa57*** muestra 10 créditos,
   esperados 600. Deriva: -590. Marcado hace 14 horas, sin resolver.
2. Error de feed RSS en ContentForge: "Stories by [author]" caído 
   por 3h. Sin contenido nuevo desde la 1:00 AM.
Todo lo demás en verde:
- 3/3 backups completados
- Pipeline de contenido: 6 scrapeados, 3 publicados, social programado 
  hasta Feb 27
- No otras anomalías en 16 usuarios activos

Esto es lo que no esperaba: las descripciones de herramientas importan más que el código. Claude decide qué herramientas llamar basándose en las descripciones que escribes. E interpreta los resultados basándose en lo que le dijiste que hace la herramienta.

Espera, déjame reformular eso. No es que las descripciones "importen más" en algún sentido abstracto. Es que pasé 2 horas escribiendo TypeScript y 45 minutos reescribiendo descripciones, y las descripciones movieron la aguja diez veces más.

Mi primer intento en la herramienta de monitoreo de créditos:

Name: "get_credits"
Description: "Query credit data from Convex"

Claude la llamó. Obtuvo JSON crudo. Me lo describió como "alguna información financiera que parece contener balances de usuarios." Técnicamente correcto. Completamente inútil a las 6am.

Segunda versión:

Name: "check_credit_anomalies"  
Description: "Find users whose current credit balance differs 
from their expected balance by more than 10%. Returns user ID 
(anonymized), current balance, expected balance, and drift. 
Flag any drift over 50 credits as urgent."

Mismos datos subyacentes. Misma consulta Convex. Pero ahora Claude devuelve una lista priorizada con flags de severidad y me dice cuáles necesitan atención inmediata. La descripción es un prompt. Trátala como uno. Escríbela como si estuvieras briefeando a un dev junior que necesita saber qué importa, no solo qué devuelve la función.

Esto aplica a cada herramienta. Un verificador de backup descrito como "get backup status" te da timestamps y tamaños de archivo. Uno descrito como "check if any scheduled backups failed or are overdue in the last 24 hours, flag missing backups by app name" te da respuestas accionables.

Me di cuenta durante esta fase de que el TypeScript es casi irrelevante. Cualquier dev puede escribir una función que consulte Convex. La parte difícil es escribir descripciones que hagan que Claude haga lo correcto al primer intento. Si has trabajado con contratos de prompt, mismo principio. Ya no compartes código. Compartes intención, restricciones, y comportamiento esperado. La descripción de herramienta ES el contrato entre tú y Claude.

Puedo hacer seguimiento naturalmente. "Muéstrame el log de transacciones de ese usuario" envía a Claude de vuelta al servidor MCP con una consulta dirigida a Convex. "¿Cuándo funcionó por última vez el feed RSS?" consulta una herramienta diferente. La conversación fluye pero los datos están estructurados por debajo.

La verificación matutina pasó de 15 minutos de scrolling y cambio de contexto a unos 30 segundos y tal vez una pregunta de seguimiento.

En dos ocasiones en la semana pasada, detectó problemas que habría perdido hasta que un usuario se quejara. No porque las alertas no estuvieran ahí. Porque estaban enterradas.

Lo que cambia cuando le das a Claude acceso a tus datos reales no es el alcance. Los datos siempre estuvieron ahí. Dejas de ser el router. Mi teléfono aún vibra con 43 notificaciones de Discord cada mañana. Solo que ya no las leo primero.

El stack, para quienes quieren construir uno

Un servidor MCP seguro con OAuth 2.1, desplegado y funcionando, cuesta cero dólares. El tier gratuito de Vercel lo maneja. No se necesita base de datos. Sin factura mensual. Lo que describí arriba es la base, pero es extensible a lo que necesites. Cada nueva fuente de datos es una función de herramienta más. Cada nueva pregunta que quieras hacerle a Claude es una descripción más que escribir. La arquitectura no cambia.

La configuración completa son 6 archivos de rutas en una app Next.js. Los tokens son JWTs stateless firmados con jose. El endpoint de autorización auto-aprueba porque es un servidor personal.

Lo que necesitas averiguar antes de empezar:

¿Puedes consultar tus fuentes de datos desde una función serverless? Si sí, cada una se convierte en una herramienta. Si tus datos viven detrás de una VPN o requieren una conexión persistente, Vercel serverless no funcionará y necesitarás un modelo de despliegue diferente.

¿Te sientes cómodo con OAuth 2.1? Si no has implementado un servidor OAuth antes, presupuesta un día completo solo para la capa de auth. Las herramientas MCP en sí son triviales. El baile OAuth es donde mueren los proyectos.

La configuración toma aproximadamente una hora si conoces las restricciones exactas por adelantado. Como ya pagué el impuesto de 4 horas y documenté las trampas arriba, podrías tener suerte.

La mañana después

Logo de RentierDigital para artículo sobre monitoreo de aplicaciones SaaS
El logo que supervisa cinco apps mientras yo duermo mal

Pequeña isla al norte de Panamá. Un tipo en la playa vendía café artesanal por más de lo que cobra Starbucks. Compré uno porque estaba medio dormido y él era persuasivo. Prefiero café instantáneo. No me juzgues.

Me senté, abrí Claude en mi teléfono, escribí "algo roto," obtuve la respuesta en 4 segundos, y volví a mirar el océano. Discord tenía 38 notificaciones sin leer. No lo abrí.

El servidor MCP no me dio datos nuevos. Todo lo que sabe ya estaba en mis canales de Discord, mis bases de datos, mis logs de cron. Solo me reemplazó como el algoritmo de ordenamiento. Y honestamente, yo era un algoritmo de ordenamiento terrible 🤷‍♂️

Si tus proyectos paralelos se multiplican más rápido que tu atención, la respuesta probablemente no es un mejor dashboard.

Si construyes cosas y ocasionalmente quieres leer sobre cómo se rompen en producción, sígueme.


* Sí, la imagen de portada es generada por IA. Mis habilidades artísticas alcanzan su pico en diagramas de cajas en Excalidraw.


En un mar de notificaciones y servicios SaaS, construí una herramienta con Claude para cortar el ruido y ver lo que realmente importa. Mi experiencia procesando 5 apps en tiempo real.

Únete a la newsletter de infraestructura AI