MCP Emails
Servidor de correo alojado para Gmail, Fastmail, iCloud, Yahoo, Zoho, Yandex y cualquier buzón IMAP/SMTP: lee, busca, envía, organiza, redacta y programa correos desde cualquier cliente MCP, obtenidos en vivo y nunca almacenados. Documentación en https://mcpemails.com/docs
Documentación
MCPEmails
Dale a tu agente de IA una bandeja de entrada. Un servidor alojado de Model Context Protocol que permite a Claude, Cursor o cualquier cliente compatible con MCP leer, buscar, enviar, organizar y programar correos electrónicos a través de tus buzones existentes, sin almacenar tu correo.
Conecta un buzón una vez, pega una URL en tu agente y podrá trabajar tu bandeja de entrada en vivo. El correo se obtiene bajo demanda y nunca se retiene; las credenciales se cifran en reposo y solo se descifran en el momento de la llamada dentro de una función de borde aislada.
🔗 mcpemails.com · 📚 Docs · 💳 Precios
Contenido
- Cómo funciona
- Inicio rápido (conectar un agente)
- Capacidades
- Herramientas
- Ámbitos de OAuth
- Proveedores compatibles
- Precios
- Arquitectura
- Estructura del repositorio
- Desarrollo local
- Variables de entorno
- Base de datos y migraciones
- Despliegue
- Autoalojamiento
- Internacionalización
- Modelo de seguridad
Cómo funciona
- Conecta un buzón. Inicia sesión en mcpemails.com y conecta Outlook / Microsoft 365 (Iniciar sesión con Microsoft, a través de Microsoft Graph), Gmail (una contraseña de aplicación de Google por IMAP por defecto, o Iniciar sesión con Google), o cualquier cuenta IMAP/SMTP (contraseña de aplicación). Las credenciales se cifran con AES‑256‑GCM antes de tocar la base de datos.
- Obtén acceso. Los clientes compatibles con OAuth (claude.ai, Claude Desktop, Cursor) se conectan con un clic mediante OAuth 2.0 + PKCE. Todo lo demás usa una clave de API con ámbito (
mcpe_…). - Apunta tu cliente al servidor. El endpoint de MCP es una única URL:
https://mcpemails.com/api/mcp - Tu agente trabaja la bandeja de entrada. Llama a herramientas como
inbox_list,email_read(action: "search"),email_compose(action: "send") yschedule(action: "create"). Cada solicitud obtiene datos en vivo de tu proveedor: nada se refleja ni se almacena en caché en el servidor.
Los permisos se limitan por clave, por lo que puedes dar a un agente solo read:email, o concederle envío y gestión de carpetas sin exponer nunca la eliminación.
Inicio rápido (conectar un agente)
Claude Desktop / Cursor (OAuth): añade un servidor MCP remoto apuntando a https://mcpemails.com/api/mcp y aprueba la pantalla de consentimiento. Elige los ámbitos que debe tener el agente.
Clave de API (cualquier cliente MCP): crea una clave en el panel, elige sus ámbitos y (opcionalmente) restringe a buzones específicos, luego envíala como token de portador:
// Example MCP client config
{
"mcpServers": {
"mcpemails": {
"url": "https://mcpemails.com/api/mcp",
"headers": { "Authorization": "Bearer mcpe_your_key_here" }
}
}
}
El protocolo es JSON‑RPC 2.0 sobre HTTP (MCP 2025-06-18, transporte Streamable). Inicia cada sesión con inbox_list: devuelve los buzones a los que la clave puede acceder, sus capacidades por proveedor y un perfil de compatibilidad versionado. El perfil marca las operaciones normalizadas como exact, different o unavailable, para que los agentes puedan preservar las diferencias del proveedor en lugar de debilitar silenciosamente una solicitud.
Guías de configuración
Instrucciones de copiar y pegar por cliente, incluido dónde guarda cada uno su archivo de configuración: mcpemails.com/docs/clients.
Claude · Claude Code · ChatGPT · Cursor · VS Code · Cline · Windsurf · Gemini CLI · Zed · JetBrains · Raycast · Warp · curl
Capacidades
- En vivo, nunca almacenado — el correo se lee directamente de tu proveedor en cada llamada; no se persisten cuerpos de mensajes.
- Multi‑proveedor — Outlook / Microsoft 365 a través de Microsoft Graph (Iniciar sesión con Microsoft), Gmail mediante contraseña de aplicación de Google o Iniciar sesión con Google, además de cualquier buzón IMAP/SMTP (Fastmail, iCloud, Yahoo, Zoho, Yandex, autoalojado…) con contraseña de aplicación.
- Sin retransmisión — el correo saliente se envía a través del SMTP/API de tu proveedor, desde tu dirección real.
- Ámbitos granulares — ocho ámbitos de permiso, otorgables de forma independiente por clave de API y por buzón.
- Lote y buscar‑y‑actuar — lee, mueve, elimina o marca hasta cientos de mensajes en una sola llamada, incluidos combinadores de "buscar y luego mover/eliminar".
- Borradores y programación — redacta borradores y pon mensajes en cola para envío futuro (despacho en el servidor).
- Búsqueda independiente del proveedor — la sintaxis de Gmail, KQL de Microsoft Graph y
SEARCHde IMAP se normalizan detrás de una interfazemail_read(action: "search"). - Listo para equipos: espacios de trabajo, miembros, roles, SSO y un registro de auditoría en el plan Team.
Herramientas
17 herramientas que llamas directamente. La mayoría están orientadas a recursos y toman un argumento action que selecciona la operación específica (y, para acciones que necesitan privilegios diferentes, el ámbito requerido):
inbox_list- Lista los buzones a los que la clave puede acceder, con las capacidades del proveedor de cada uno.email_read- Lista, lee y busca mensajes, en lotes, además de adjuntos, texto extraído de adjuntos y el.emloriginal.email_organize- Mueve, copia, marca y archiva mensajes que nombras por id, individualmente o en lotes.email_search_and_move- Mueve cada mensaje que coincida con una búsqueda a una carpeta. Es su propia herramienta, y la destructiva, porque un filtro incorrecto reubica una bandeja de entrada completa.email_delete- Envía a la papelera o elimina permanentemente mensajes, individualmente, en lotes o por búsqueda.email_compose- Envía, responde y reenvía a través de tu propio proveedor, desde tu dirección real.folder_list- Lista carpetas (etiquetas en Gmail) con sus IDs nativos del proveedor y recuentos de mensajes. Solo lectura.folder- Crea, renombra y elimina carpetas (etiquetas en Gmail).draft_list- Lista los borradores guardados en la bandeja de entrada, con sus IDs de borrador. Solo lectura.draft- Crea, actualiza, envía y elimina borradores, incluidas respuestas nativas del proveedor.schedule_list- Lista lo que está en cola para entrega futura. Solo lectura.schedule- Pone un mensaje en cola para entrega futura y cancela uno que esté en cola.signature_get- Lee la firma y el nombre del remitente configurados para un buzón. Solo lectura.signature_set- Establece la firma que se añade al correo saliente y el nombre del remitente.automation_read- Lista reglas de triaje, lee una, muestra el historial de ejecución y hace una prueba en seco de un filtro. Solo lectura.automation- Crea, actualiza, habilita, deshabilita y elimina reglas de triaje automáticas, sin modelo en el bucle.contact_search- Busca contactos escaneando correo reciente en vivo, sin libreta de direcciones almacenada.
| Herramienta | Acciones | Ámbito(s) |
|---|---|---|
inbox_list | (acción única) | read:email |
email_read | list, read, read_batch, search, attachment, extract, original | read:email (search también acepta search:email) |
email_organize | move, move_batch, copy, copy_batch, flag, archive | manage:folders |
email_search_and_move | (acción única) | manage:folders |
email_delete | delete, delete_batch, search_and_delete | delete:email |
email_compose | send, reply, forward | send:email |
folder_list | (acción única) | read:email |
folder | create, rename, delete | manage:folders |
draft_list | (acción única) | manage:drafts |
draft | create, reply, update, send, delete | manage:drafts (crear/responder/actualizar/eliminar), read:email (responder también), send:email (enviar) |
schedule_list | (acción única) | schedule:email |
schedule | create, cancel | schedule:email |
signature_get | (acción única) | read:email |
signature_set | (acción única) | send:email |
automation_read | list, get, runs, preview | manage:automations |
automation | create, update, enable, disable, delete | manage:automations |
contact_search | (acción única) | manage:contacts (también acepta read:email) |
Una clave de ámbito completo ve 22 herramientas en tools/list: las 16 anteriores más seis herramientas solo de aplicación (approval_review, approval_decide, approval_update, approval_schedule, bulk_execute, bulk_cancel). Esas llevan _meta.ui.visibility: ["app"] y controlan la tarjeta de revisión que un cliente MCP muestra para un envío retenido o una operación masiva previsualizada, en lugar de componerse a mano.
Notas:
- Las herramientas aceptan un
inbox_idexplícito (UUID) o una dirección de correoinbox; las claves de un solo buzón resuelven el destino automáticamente. - Las acciones por lotes tienen un límite de 50 (
email_read'sread_batch) a 500 (mover/eliminar/marcar) mensajes por llamada. - Para una mutación específica, primero usa
email_readconaction: "search", luego pasa elmessage_idomessage_idsdevuelto aemail_organizeoemail_delete. Los campos de búsqueda solo los aceptanemail_search_and_movey la acciónsearch_and_deletedeemail_delete. contact_searchescanea correo reciente en vivo, por lo que no hay libreta de direcciones almacenada.- La acción
originaldeemail_readdevuelve un mensaje MIME completo almacenado por el proveedor como archivo.emlportátil (hasta 25 MB). Es de solo lectura y nunca marca el mensaje como leído. - La acción
senddedraftrequieresend:email, nomanage:drafts, para que una clave que solo puede gestionar borradores no pueda usarlos para eludir el consentimiento de envío de correo. - La acción
replydedraftcrea una respuesta nativa del proveedor sin enviar en la conversación de origen. Necesita tantomanage:draftscomoread:email, y por defecto responde solo al remitente. - Las mitades de solo lectura (
folder_list,draft_list,schedule_list,signature_get,automation_read) se separaron de sus herramientas de escritura el 2026-09-09, para que una clave de solo lectura nunca vea una herramienta de escritura. Las formas combinadas antiguas (folderaction: "list",draftaction: "list",scheduleaction: "list",signatureaction: "get"/"set",automationaction: "list"/"get"/"runs"/"preview") todavía se aceptan en el cable para clientes ya conectados, pero ya no se anuncian. automationgestiona reglas de triaje programadas automáticas: una búsqueda almacenada más una acción fija, evaluadas en una cadencia sin modelo en el bucle. No hay acción de eliminar correo, unforwardsiempre espera aprobación humana, ydraft_replysolo escribe un borrador. Verdocs/automations-trust-boundary.md.tools/listsolo devuelve las herramientas para las que tu clave (o token OAuth) tiene ámbito real.
Ámbitos de OAuth
| Alcance | Otorga |
|---|---|
read:email | Listar bandejas de entrada y carpetas; listar, leer y buscar mensajes; leer la firma de una bandeja de entrada; buscar contactos |
search:email | Alternativa más restringida que otorga solo la acción search de email_read |
send:email | Enviar, responder y reenviar; establecer la firma y el nombre del remitente; también se requiere para enviar un borrador |
manage:folders | Crear/renombrar/eliminar carpetas; mover, copiar, marcar y archivar mensajes |
delete:email | Mover a la papelera o eliminar permanentemente mensajes |
manage:drafts | Crear, editar y eliminar borradores (enviar uno también requiere send:email) |
manage:contacts | Búsqueda de contactos en vivo desde el correo reciente |
schedule:email | Poner mensajes en cola para entrega futura |
manage:automations | Crear y gestionar reglas de triaje programadas sin supervisión (sin acción de eliminación; los reenvíos permanecen sujetos a aprobación) |
Proveedores compatibles
| Proveedor | Conexión mediante | Leer/Buscar | Enviar | Carpetas | Eliminación permanente | Borradores |
|---|---|---|---|---|---|---|
| Gmail / Google Workspace | Contraseña de aplicación (IMAP/SMTP) por defecto, u OAuth 2.0 (Iniciar sesión con Google) | ✅ | ✅ | Etiquetas | Solo papelera | ✅ |
| Fastmail | Contraseña de aplicación (IMAP/SMTP) | ✅ | ✅ | ✅ | ✅ | ✅ |
| iCloud, Yahoo, AOL, GMX, Zoho, Yandex | Contraseña de aplicación (IMAP/SMTP) | ✅ | ✅ | ✅ | ✅ | ✅ |
| IONOS, STRATO, One.com, OVHcloud, Namecheap Private Email, Hostinger, Infomaniak, Hetzner | Contraseña del buzón (IMAP/SMTP) | ✅ | ✅ | ✅ | ✅ | ✅ |
| cPanel, Plesk, SiteGround, Bluehost y otros hosts compartidos | Contraseña del buzón (IMAP/SMTP) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Cualquier otro buzón IMAP/SMTP | Contraseña de aplicación o contraseña del buzón | ✅ | ✅ | ✅ | ✅ | ✅ |
El correo en tu propio dominio en un host web se conecta como cualquier buzón IMAP, y un espacio de trabajo puede contener varios de ellos (info@, sales@, support@). Configuración por host y problemas conocidos para 100 proveedores en mcpemails.com/connect. | Outlook / Microsoft 365 | OAuth 2.0 (Microsoft Graph) | ✅ | ✅ | ✅ (anidadas) | ✅ | ✅ |
Outlook usa Microsoft Graph con "Iniciar sesión con Microsoft", no IMAP. Las cuentas personales de Microsoft (outlook.com, hotmail.com, live.com, msn.com) se conectan directamente. En inquilinos de Microsoft 365 de trabajo o escuela, la política de consentimiento predeterminada de Microsoft impide que los empleados aprueben permisos de correo por sí mismos, por lo que un administrador de TI aprueba la aplicación una vez para la organización; el panel le da al usuario un enlace de aprobación compartible para enviarles (
/auth/outlook/admin-consent), y el administrador no necesita una cuenta de MCP Emails. Una cuenta de Microsoft sin buzón de Exchange Online es rechazada, con una indicación hacia IMAP. Las herramientas de etiquetas son solo de Gmail (Outlook usa carpetas), excepto que la acción de etiqueta de una automatización aplica una categoría de Outlook; y Graph no puede combinar una búsqueda de texto con los filtros de no leído, adjunto, marcado o fecha.
Precios
La métrica de valor son buzones conectados. Gratis conecta un buzón, Personal conecta hasta tres, Pro conecta todos los buzones que poseas, y Team añade personas, roles y un espacio de trabajo separado por cliente. La facturación anual ahorra alrededor del 20%.
| Gratis | Personal | Pro | Team | |
|---|---|---|---|---|
| Precio | $0 | $9/mes · $86.40/año ($7.20/mes) | $15/mes · $144/año ($12/mes) | $79/mes · $756/año ($63/mes) |
| Buzones conectados | 1 | 3 | Ilimitados | Ilimitados |
| Acciones de correo / mes | 150 (los primeros 7 días no se cuentan) | Sin límite mensual (uso justo) | Sin límite mensual (uso justo) | Sin límite mensual (uso justo) |
| Claves API | Ilimitadas | Ilimitadas | Ilimitadas | Ilimitadas |
| Miembros | 1 (solo propietario) | 1 (solo propietario) | 1 (solo propietario) | Ilimitados, con roles |
| Límite de tasa de uso justo | 60 req/min | 120 req/min | 300 req/min | 1,000 req/min |
| Roles y espacios de trabajo de equipo | No | No | No | ✅ |
| SSO (SAML/OIDC) + registro de auditoría | No | No | No | ✅ |
| Soporte | Comunidad | Correo electrónico | Correo electrónico | Prioritario |
También se aplican límites por clave API (100 req/min · 1,000/hora · 10,000/día). Los límites de tasa se pueden reintentar: regresan como error JSON-RPC -32003 con data.retry_after en segundos.
Los espacios de trabajo gratuitos obtienen 150 acciones de correo por mes calendario (UTC). Los primeros 7 días después del registro no se cuentan, y inbox_list y el panel nunca se cuentan. Cuando se alcanza la asignación, cada otra acción de correo es rechazada hasta el día 1 del mes siguiente, las automatizaciones sin supervisión se pausan y reanudan automáticamente el día 1, y el propietario recibe un correo al 80% y al 100%. Personal, Pro y Team no tienen límite mensual, sujeto a uso justo (sus techos son protecciones contra abuso, no características del plan, y no se imprimen en ningún lugar). Un rechazo no se puede reintentar y no es un error JSON-RPC: regresa como un resultado de herramienta normal con isError: true, texto que comienza con el conteo y la fecha de reinicio, y un bloque _meta["com.mcpemails/usage_limit"] que se limpia en reset_at.
Los ids internos de planes preceden a los nombres: solo se vende como Pro y pro se vende como Team. El id más nuevo personal es el único que coincide con su nombre mostrado, Personal. Cada usuario que existía antes del reajuste de precios del 2026-08-19 mantiene buzones ilimitados gratis, permanentemente, y cada espacio de trabajo creado antes del lanzamiento de la asignación de acciones del 2026-09-12 es un miembro temprano que no se mide contra ella. Ver apps/web/src/lib/stripe/plans.ts.
Arquitectura
flowchart LR
Agent["MCP client<br/>(Claude, Cursor, …)"] -->|"JSON-RPC / OAuth or API key"| Web
subgraph Vercel["Vercel — Next.js 16"]
Web["/api/mcp route<br/>+ marketing site + dashboard"]
end
subgraph Supabase
Edge["mcp-server<br/>edge function (Deno)"]
DB[("Postgres<br/>RLS + encrypted creds")]
Cron["token-refresh<br/>edge functions"]
end
Web -->|proxies| Edge
Edge -->|decrypt creds, fetch live| Providers["Email providers<br/>Gmail API · Microsoft Graph · IMAP/SMTP"]
Edge --> DB
Cron --> DB
Web --> Stripe[("Stripe<br/>billing")]
/api/mcpes un manejador de rutas delgado de Next.js que actúa como proxy a la función edge de Supabasemcp-server— la implementación real de MCP, donde se descifran las credenciales y se realizan las llamadas al proveedor.- La base de datos Postgres almacena espacios de trabajo, miembros, buzones (tokens/contraseñas cifrados), claves API con hash, clientes OAuth, envíos programados y un registro de actividad — todo protegido por Seguridad a Nivel de Fila.
- Funciones edge cron actualizan los tokens OAuth de Gmail/Outlook antes de que expiren.
Stack: Next.js 16 (App Router) · React 19 · next‑intl 4 · Supabase (Auth, Postgres, Edge Functions) · Stripe · Resend · TypeScript. Análisis/saneamiento de correo mediante mailparser, jsdom y isomorphic-dompurify.
Estructura del repositorio
.
├── apps/
│ └── web/ # Next.js 16 app (marketing, dashboard, /api/mcp proxy)
│ ├── app/ # App Router routes ([locale], dashboard, api, auth)
│ ├── components/ # marketing/ + dashboard/ React components
│ ├── messages/ # next-intl translations (en, nb, es, fr, zh)
│ ├── src/lib/ # stripe/, supabase/, blog/, crypto helpers
│ └── proxy.ts # middleware: i18n + Supabase session + CDN cache
├── supabase/
│ ├── functions/
│ │ ├── mcp-server/ # the MCP server (tools, auth, scopes)
│ │ ├── gmail-token-refresh/
│ │ └── outlook-token-refresh/
│ └── migrations/ # SQL migrations (schema + RLS)
└── package.json # npm workspaces (apps/*)
Desarrollo local
Requisitos previos: Node.js 20+, npm y la CLI de Supabase (para migraciones y funciones edge).
# 1. Install (npm workspaces — run from the repo root)
npm install
# 2. Configure environment
cp .env.example apps/web/.env.local
# then fill in the values (see below) and generate the two secrets:
openssl rand -hex 32 # ENCRYPTION_KEY
openssl rand -hex 32 # CSRF_SECRET
# 3. Run the web app (http://localhost:3000)
npm run dev
# 4. Production build
npm run build
next.config.jsvalida las variables de entorno requeridas en compilación/inicio y rechaza valores débiles deENCRYPTION_KEY, por lo que un entorno mal configurado falla rápidamente en lugar de en tiempo de ejecución.
Variables de entorno
Copia .env.example y completa con valores reales. Requeridas en todos los entornos:
| Variable | Propósito |
|---|---|
NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY | Cliente de Supabase (público) |
SUPABASE_SERVICE_ROLE_KEY | Clave de administración del lado del servidor (omite RLS) — secreto |
NEXT_PUBLIC_APP_URL | URL base canónica; impulsa las URI de redirección OAuth |
GOOGLE_SITE_VERIFICATION (opcional) | Token de verificación de etiqueta HTML de Google Search Console; configurar solo en producción |
ENCRYPTION_KEY | Clave AES‑256‑GCM de 64 hex para credenciales en reposo — secreto |
CSRF_SECRET | Clave HMAC de 64 hex para tokens CSRF (distinta de la anterior) — secreto |
Dependientes de funciones:
| Variable(s) | Necesaria para |
|---|---|
GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET | OAuth de Gmail (gmail.readonly, gmail.send, gmail.modify) |
OUTLOOK_CLIENT_ID / OUTLOOK_CLIENT_SECRET / OUTLOOK_TENANT_ID | OAuth de Outlook (Mail.ReadWrite, Mail.Send, offline_access, openid, profile, email). OUTLOOK_TENANT_ID es opcional y por defecto es common |
NEXT_PUBLIC_OAUTH_VERIFICATION_PENDING | Muestra la advertencia de aplicación no verificada hasta que se complete la verificación de Google (el editor de Microsoft ya está verificado) |
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET / NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Facturación |
STRIPE_PRICE_PERSONAL_MONTHLY / _YEARLY, STRIPE_PRICE_SOLO_MONTHLY / _YEARLY, STRIPE_PRICE_PRO_MONTHLY / _YEARLY | IDs de precios de planes (personal = Personal, solo = Pro, pro = Team) |
STRIPE_WEBHOOK_PROXY_KEY | Opcional. Restringe /api/stripe/webhook a la cola de entrega delante de él. Sin configurar = sin restricción. Configúralo SOLO DESPUÉS de que la cola esté enviando la clave, o cada entrega fallará con 401 y se enviará a la cola de mensajes muertos. |
STRIPE_WEBHOOK_TOLERANCE_SECONDS | Opcional. Límite de antigüedad de firma de Stripe, por defecto 7 días. Amplio porque una reproducción en cola lleva su firma original; el valor predeterminado de 300s de Stripe rechazaría cada reintento retenido. |
Fastmail y otros proveedores IMAP se conectan mediante contraseña de aplicación y no necesitan credenciales OAuth.
Los webhooks de Stripe se entregan a través de una cola. Stripe publica en el ingreso de Queuey, que reenvía a
/api/stripe/webhook. Dos configuraciones en esa cola son críticas: la carga útil debe ser paso directo sin procesar (la firma es sobre bytes exactos), y un encabezado mapeado debe reenviarheader:Stripe-Signature. Sin el encabezado mapeado, cada entrega falla conMissing stripe-signature header.. Ten en cuenta queSTRIPE_WEBHOOK_SECRETdebe ser el secreto de firma del endpoint de Stripe apuntado al ingreso, no de cualquier endpoint directo anterior.
Base de datos y migraciones
El esquema y las políticas de Seguridad a Nivel de Fila viven en supabase/migrations/. Tablas principales: workspaces, workspace_members, inboxes (credenciales cifradas, con borrado suave), api_keys (con hash, con alcance, restringido a buzón), oauth_clients, scheduled_sends, workspace_invites y un activity_log particionado por mes.
# Apply one migration to the linked project, then record it in the history
npx supabase db query --linked -f supabase/migrations/<file>.sql
npx supabase migration repair --status applied <version> --linked
# Regenerate TypeScript types from the live schema
npm run gen:types
La CLI de Supabase es la fuente de verdad para cambios de base de datos en este proyecto.
apps/web/src/types/database.types.ts es GENERADO
Está escrito en su totalidad por npm run gen:types (supabase gen types typescript --linked), que lee el proyecto vinculado en vivo, no los archivos de migración.
No lo edites a mano: una columna añadida manualmente se elimina silenciosamente en la
siguiente regeneración, y una columna añadida manualmente que nunca existió en la base de datos
compila limpiamente y falla con 500 en producción. Regenera en el mismo cambio que
aplica una migración, y confirma el resultado.
Las tablas, vistas y funciones RPC están todas cubiertas, y cada cliente de Supabase
declarado en .ts/.tsx de apps/web (que es todo el programa
verificado por tipos) es SupabaseClient<Database>, por lo que .from(), .select(), .insert()
y .rpc() están verificados por tipos contra el esquema real. Si una tabla o columna
parece faltante, la solución es npm run gen:types, nunca un as any local en el
cliente de Supabase. Los casts que permanecen son para cosas que el generador genuinamente
no puede expresar: formas de carga útil jsonb, argumentos de función anulables y un
helper que despacha muchos RPC a través de una sola firma. Cada uno de esos
lleva un comentario que lo dice.
El calificador .ts/.tsx es crítico. apps/web/tsconfig.json no establece
checkJs y su include es **/*.ts y **/*.tsx, por lo que un archivo .js como
app/dashboard/[[...section]]/page.js está completamente fuera del programa: sus
tipos JSDoc @param son documentación y nada los verifica. Están
escritos como SchemaTypedClient (un @typedef para
SupabaseClient<Database>) para que la documentación al menos coincida con lo que los
llamadores pasan, pero mantenerlo honesto es un trabajo manual.
Nunca escribas un SupabaseClient desnudo. Sin argumento de tipo se resuelve a
SupabaseClient<any, "public", any>, que es un as any que ningún grep de
as any encontrará: una función declarada db: SupabaseClient no obtiene verificación
de columnas en absoluto, y una columna inexistente en un .insert() compila limpiamente.
SupabaseClient<any> es el mismo agujero dicho en voz alta, y también lo son
SupabaseClient<any | Database> y SupabaseClient<Alias> donde el alias es
any. apps/web/eslint.config.mjs por lo tanto no busca any; requiere
Database, y npm run lint da error en cualquier referencia de tipo SupabaseClient
que no lo lleve, más un
createServerClient(...) / createBrowserClient(...) sin parametrizar.
Esos son selectores sintácticos (el repositorio no ejecuta linting consciente de tipos), por lo que coinciden con la ortografía en el sitio de uso y tres cosas podrían pasarlos por alto: import { SupabaseClient as SB }, import * as Supa y luego Supa.SupabaseClient, y const mk = createServerClient; mk(...). Ningún selector puede ver a través de ninguno de esos, por lo que la configuración los cierra un nivel más arriba: renombrar la importación SupabaseClient e importar el espacio de nombres @supabase/ssr o @supabase/supabase-js son en sí mismos errores, y no-restricted-imports mantiene createClient / createServerClient / createBrowserClient fuera de cada archivo excepto los envoltorios en src/lib/supabase/, que son el único lugar que construye un cliente y siempre pasa <Database>. Todo lo demás importa esos envoltorios.
Las reglas no están limitadas a **/*.ts(x), por lo que las restricciones de importación y llamada también se aplican en .js/.jsx/.mjs. Lo que no pueden hacer allí es leer un tipo JSDoc: un selector sintáctico no ve ningún nodo AST para él. Ese es el límite conocido mencionado anteriormente, no una brecha que las reglas pretendan cubrir.
Dos cosas que un tsc verde aún no te dice:
--linkedlee producción, nosupabase/migrations/. Lo que el proyecto en vivo realmente tiene es lo que llega al archivo, incluida la deriva, y una migración que existe localmente pero no se ha enviado es invisible para él. Regenerar requiere credenciales para el proyecto vinculado, por lo que CI no puede reproducir este archivo solo desde el repositorio. Verificasupabase migration list --linkedantes de creer que los tipos describen lo que dicen las migraciones. A partir de 2026-09-16 ese comando reporta ocho desajustes en ambas direcciones.- Un cast aún desactiva la verificación.
(row as any).some_columnyclient as unknown as SupabaseClient<Database>ambos compilan lo que escribas. Las reglas de lint anteriores detectan la declaración del cliente, no un cliente que se convierte aanyen el sitio de llamada;@typescript-eslint/no-explicit-anyes lo que detecta esos, y uneslint-disableen él es una elección deliberada que debería explicar por qué.
Despliegue
Aplicación web → Vercel (proyecto mcp-emails-web):
vercel --prod --yes
Los encabezados de seguridad y los tiempos de espera de funciones están definidos en vercel.json. Las rutas de marketing se sirven con un Cache-Control cacheable por CDN (establecido en proxy.ts) para que los rastreadores y visitantes recurrentes lleguen a la caché perimetral; el panel, la autenticación y las rutas API permanecen no-store.
Servidor MCP → función perimetral de Supabase:
npx supabase functions deploy mcp-server --project-ref <your-project-ref> --no-verify-jwt
Migraciones primero, función después. PostgREST no devuelve
undefinedpara una columna que no conoce — da error — y la proyección de bandeja de entrada compartida del servidor (INBOX_SELECT_COLUMNS) es utilizada por cada herramienta de correo, que todas leen un error de consulta como "bandeja de entrada no encontrada". Desplegar la función antes de su migración puede, por lo tanto, derribar toda la superficie de correo, no solo la función que quería la nueva columna. Las columnas de bandeja de entrada recién migradas se leen deliberadamente en sus propias consultas pequeñas (readSendReviewMode,readBulkReviewMode,readInboxDraftEditorHidden) para que un despliegue fuera de orden degrade esa única función en lugar de romper todo — eso es una red de seguridad, no una licencia para saltarse el orden.
Autoalojamiento
¿No quieres confiar tu correo al servicio alojado? Ejecuta el mismo servidor MCP en tu propia máquina. self-host/ incluye una pila contenerizada (Postgres + PostgREST + el servidor Deno, sin Supabase/Stripe/panel), por lo que tus credenciales se cifran con una clave que solo tú tienes y se descifran solo dentro de tu propio contenedor.
cd self-host
make setup # generate secrets (.env)
make up # build + start the stack
export IMAP_PASSWORD='your-app-password'
make provision EMAIL=you@example.com IMAP_HOST=imap.fastmail.com SMTP_HOST=smtp.fastmail.com SERVICE=fastmail
make key NAME="my agent" # mint an mcpe_ key, then point your client at http://localhost:8787
Es primero IMAP/SMTP (Fastmail, iCloud, Yahoo, Zoho, Yandex, genérico) mediante contraseña de aplicación; OAuth de Gmail/Outlook y el panel web siguen siendo solo alojados (Outlook también requeriría tu propio registro de aplicación de Microsoft Entra). El contenedor ejecuta supabase/functions/mcp-server/ sin modificar; consulta self-host/README.md para la guía completa.
Internacionalización
Construido con next‑intl (localePrefix: 'as-needed', localeDetection: false para URLs canónicas estables). El inglés se sirve en /; otros idiomas llevan un prefijo (/nb, /es, /fr, /zh). Las traducciones viven en apps/web/messages/.
Idiomas admitidos: Inglés, Noruego Bokmål, Español, Francés, Chino (Simplificado).
Modelo de seguridad
- Credenciales cifradas en reposo con AES‑256‑GCM; descifradas solo dentro de la función perimetral en el momento de la llamada.
- Sin almacenamiento de mensajes — los cuerpos de correo y los adjuntos se obtienen en vivo y nunca se persisten. La extracción de texto de adjuntos se ejecuta de forma transitoria en la solicitud y no devuelve bytes de adjuntos sin procesar.
- Las claves API están hasheadas (solo se almacena un prefijo para mostrar) y se limitan por permiso y por bandeja de entrada, con caducidad opcional.
- OAuth 2.0 + PKCE para autorización de clientes; Registro Dinámico de Clientes (RFC 7591) para clientes MCP.
- Seguridad a nivel de fila aísla los datos de cada espacio de trabajo en la capa de base de datos.
- CSP estricto, HSTS,
X-Frame-Options: DENYy encabezados relacionados en cada respuesta.
Licencia
MCP Emails es de código abierto bajo la GNU Affero General Public License v3.0 (AGPL‑3.0). El servicio alojado en mcpemails.com ejecuta el mismo servidor que puedes autoalojar, para que puedas leer el código, verificarlo y ejecutarlo tú mismo. Consulta /security para el modelo de confianza.
Envía y recibe correo desde cualquier agente. © MCPEmails, AGPL‑3.0.