Documentación

Construye sobre MCPEmails

Todo lo que hace un cliente MCP, un script puede hacerlo con HTTPS normal. Un endpoint, una clave de API, JSON de entrada y JSON de salida. Esta guía cubre la autenticación, las tres llamadas que necesitas, los errores, los reintentos, los límites y los webhooks para el correo nuevo.

El endpoint

Cada solicitud es un POST JSON-RPC 2.0 a https://mcpemails.com/api/mcp. Es el mismo endpoint que usan claude.ai, ChatGPT y otros clientes MCP, con las mismas herramientas y los mismos permisos, así que un script y un asistente de chat ven un único producto.

  • Envía Content-Type: application/json y Accept: application/json. La respuesta es un único objeto JSON.
  • El servidor no mantiene sesión. No hay nada que abrir ni cerrar, y cada solicitud lleva su propia cabecera Authorization.
  • Los resultados correctos de las herramientas llevan un objeto structuredContent tipado, así que nunca tienes que interpretar prosa. Los mismos datos están en content[0].text como cadena JSON.

Autenticación

Claves de API

  • Crea una clave en el panel, en Claves de API → Nueva clave. Ponle el nombre del script o flujo de trabajo que la usará.
  • Una clave es mcpe_ seguido de 64 caracteres hexadecimales. Se muestra una sola vez, al crearla, y solo se guarda un hash. Cópiala entonces; una clave perdida no se puede recuperar, solo sustituir.
  • Envíala en cada solicitud como Authorization: Bearer mcpe_.... Nunca pongas una clave en una URL ni en una query string.
  • Todos los planes pueden crear tantas claves como necesiten. Usa una clave por script o flujo de trabajo, para poder revocar una sin tocar las demás.
  • Revocar una clave en la página Claves de API corta el acceso de inmediato.

Ámbitos

Cada clave tiene solo los permisos que marcas al crearla. Puedes cambiarlos después con Editar acceso, y el cambio se aplica en la siguiente llamada sin volver a conectar.

ÁmbitoPermite
read:emailListar, leer y buscar mensajes, descargar adjuntos, listar buzones y carpetas.
search:emailSe conserva para claves antiguas. La búsqueda está cubierta por read:email.
send:emailEnviar, responder y reenviar.
manage:draftsCrear, actualizar y eliminar borradores. Enviar un borrador también necesita send:email.
manage:foldersMover, copiar, marcar y archivar mensajes; crear, renombrar y eliminar carpetas.
delete:emailEliminar mensajes, uno a uno o en bloque.
manage:contactsBuscar contactos en tu correo reciente.
schedule:emailProgramar envíos y gestionar los correos programados.
manage:automationsCrear reglas que se ejecutan según un horario sin que nadie las vigile.

Empieza con read:email y añade un ámbito de escritura solo cuando el flujo de trabajo lo necesite. Una llamada fuera de los ámbitos de la clave se rechaza antes de contactar con tu servidor de correo. Los lectores del espacio de trabajo solo pueden tener ámbitos de lectura.

Acceso a buzones

Por defecto una clave llega a todos los buzones del espacio de trabajo. Para fijarla a algunos, abre Editar acceso en la clave y elige los buzones en Acceso a buzones. El límite se comprueba en cada llamada, así que una clave fijada al buzón de soporte no puede leer el de finanzas, diga lo que diga la solicitud.

OAuth para apps conectoras

Las apps que la gente conecta por su cuenta, como claude.ai, ChatGPT y otros clientes MCP, inician sesión con OAuth en lugar de con una clave: registro dinámico de cliente, código de autorización con PKCE y una pantalla de consentimiento donde el usuario elige los ámbitos y los buzones. Consulta Conexión OAuth. Para tus propios scripts, servidores y flujos de trabajo, una clave de API es más sencilla.

Las tres llamadas

1. initialize (opcional)

Los clientes MCP empiezan con initialize. Un script puede saltárselo, porque el servidor no mantiene sesión, pero es una forma barata de comprobar que la clave funciona y de leer las instrucciones del servidor.

bash
curl -s https://mcpemails.com/api/mcp \
  -H "Authorization: Bearer $MCPEMAILS_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-06-18",
      "clientInfo": { "name": "my-agent", "version": "1.0" },
      "capabilities": {}
    }
  }'

2. tools/list

tools/list devuelve todas las herramientas que esta clave puede llamar, con su descripción, su esquema de entrada y su esquema de salida. Léelo al arrancar en lugar de fijar nombres de herramientas o argumentos en el código: la lista se filtra por los ámbitos de la clave, y las herramientas ganan argumentos con el tiempo.

bash
curl -s https://mcpemails.com/api/mcp \
  -H "Authorization: Bearer $MCPEMAILS_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'

3. tools/call

Llama a una herramienta por su nombre con sus argumentos. La mayoría aceptan una action que decide qué hacen, como list, read o search en email_read. Indica el buzón con inbox (su dirección) o inbox_id; inbox_list devuelve ambos.

bash
curl -s https://mcpemails.com/api/mcp \
  -H "Authorization: Bearer $MCPEMAILS_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "email_read",
      "arguments": { "action": "list", "inbox": "you@example.com", "unread": true, "limit": 20 }
    }
  }'

Un resultado correcto:

JSON
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{ "type": "text", "text": "{\"messages\":[...],\"total\":42, ...}" }],
    "structuredContent": {
      "messages": [{ "id": "...", "subject": "...", "from": { "name": "...", "email": "..." } }],
      "total": 42,
      "total_is_estimate": false,
      "has_more": true,
      "next_offset": 20
    }
  }
}

La referencia de herramientas describe cada herramienta, y la guía de curl tiene más ejemplos.

Errores

Hay dos tipos. Una solicitud que no pudo ejecutarse en absoluto vuelve como un error JSON-RPC con un estado HTTP. Una solicitud que llegó a una herramienta y fue rechazada por ella vuelve como un result normal con isError: true, para que un modelo de IA lea el motivo. Ramifica según data.error_code o el código de _meta, no según el texto del mensaje.

RespuestaCuándoQué hacer
HTTP 401, -32001Clave ausente, mal formada, revocada o caducada.Corrige la cabecera Authorization o crea una clave nueva. Reintentar no ayudará.
HTTP 403, -32004, insufficient_scopeA la clave le falta un ámbito que esta acción de la herramienta necesita. data.required_scopes lista lo que la permitiría.Añade el ámbito con Editar acceso, o usa otra clave.
HTTP 429, -32003, rate_limit_exceededDemasiadas solicitudes. data.window indica qué ventana se llenó.Espera Retry-After segundos (también en data.retry_after) y repite la misma solicitud.
-32602Nombre de herramienta desconocido. data.available_tools lista los nombres que esta clave puede llamar.Usa un nombre de tools/list.
isError: true, invalid_argumentsLos argumentos no pasaron el esquema de la herramienta. No se ejecutó nada. El texto nombra cada argumento incorrecto y, cuando un nombre de argumento fue adivinado, indica el que existe.Corrige los argumentos. Los mismos argumentos se rechazan de la misma forma.
isError: true, usage_limit_reachedUn espacio de trabajo Gratis ha agotado sus acciones de correo del mes.Espera a la fecha de reinicio en _meta, o mejora de plan. Los planes de pago no tienen tope mensual.
isError: true, otroLa herramienta se ejecutó y falló: mensaje no encontrado, el proveedor lo rechazó, destinatario inválido, etcétera.Lee content[0].text. Dice qué pasó y si se envió algo.
HTTP
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", ...

{
  "jsonrpc": "2.0",
  "id": 4,
  "error": {
    "code": -32004,
    "message": "Insufficient scope: the 'send:email' scope is required to call email_compose.",
    "data": { "error_code": "insufficient_scope", "required_scopes": ["send:email"], ... }
  }
}
HTTP
HTTP/1.1 429 Too Many Requests
Retry-After: 12

{
  "jsonrpc": "2.0",
  "id": 5,
  "error": {
    "code": -32003,
    "message": "Rate limit exceeded",
    "data": { "error_code": "rate_limit_exceeded", "window": "per_minute", "retry_after": 12, ... }
  }
}
JSON
{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "isError": true,
    "content": [{ "type": "text", "text": "Invalid arguments for email_read. The call was rejected before it ran, ..." }],
    "_meta": {
      "com.mcpemails/invalid_arguments": {
        "error_code": "invalid_arguments",
        "retryable": false,
        "errors": [{ "path": "arguments.since", "message": "..." }]
      }
    }
  }
}

Paginación

Todo resultado de lista o búsqueda termina con los mismos cuatro campos:

  • has_more: true cuando existe otra página. Solo has_more: false significa que lo has visto todo.
  • next_offset: pásalo de vuelta como offset, con todos los demás argumentos sin cambios, para obtener la página siguiente. Es null cuando has_more es false.
  • total: el número de coincidencias, o null cuando el proveedor no puede contarlas. Nunca contradice la página que tienes.
  • total_is_estimate: true cuando total es una estimación o un mínimo y no un recuento exacto.

Los cuerpos de mensaje largos se trocean de la misma manera: cuando body_truncated es true, vuelve a leer con body_next_offset como body_offset.

Reintentos y claves de idempotencia

Una conexión que se corta tras un envío te deja sin saber si el correo salió. Pasa una idempotency_key y reintenta con la misma clave: el trabajo se ejecuta una sola vez.

bash
curl -s https://mcpemails.com/api/mcp \
  -H "Authorization: Bearer $MCPEMAILS_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 7,
    "method": "tools/call",
    "params": {
      "name": "email_compose",
      "arguments": {
        "action": "send",
        "inbox": "you@example.com",
        "to": ["customer@example.com"],
        "subject": "Your order has shipped",
        "body": "Hi, your order is on its way.",
        "idempotency_key": "order-1042-shipped"
      }
    }
  }'
  • Se acepta en envíos, respuestas, reenvíos, envíos programados, escritura de borradores y cambios en el buzón como mover, marcar y eliminar. tools/list muestra idempotency_key en el esquema de entrada de cada herramienta que la acepta.
  • Hasta 200 caracteres. Usa algo de tu propio sistema, como un número de pedido y un nombre de evento.
  • Las claves están ligadas a la clave de API que las envió y se recuerdan durante 24 horas.
  • Un reintento con la misma clave y los mismos argumentos no se ejecuta de nuevo. Devuelve idempotent_replay: true, el status original y el resultado original en result, incluido el message_id que quizá perdiste.
  • La misma clave con argumentos distintos se rechaza con idempotency_key_conflict. Usa una clave nueva para una operación nueva.
  • Un reintento mientras la primera llamada aún se ejecuta devuelve status: "processing". Reintenta en breve.
  • Cuando el proveedor de correo rechazó un envío antes de aceptarlo, no se envió nada y la clave se libera, así que el reintento idéntico sí envía.
  • Cuando el resultado es incierto, la repetición dice status: "unknown". Revisa Enviados antes de enviar otra vez con una clave nueva.
JSON
{
  "idempotency_key": "order-1042-shipped",
  "idempotent_replay": true,
  "status": "succeeded",
  "result": { "message_id": "...", "inbox_id": "...", "status": "sent" },
  "message": "This logical request was already processed. ..."
}

Aprobación de envíos

Cualquier buzón puede retener todos los envíos hasta que una persona los apruebe. Un envío desde tu script devuelve entonces un approval_id con status: "pending_approval" en lugar de salir, y sale cuando alguien lo aprueba en el panel. Funciona en todos los planes y es un buen valor por defecto mientras una automatización nueva demuestra lo que vale. Un reintento con la misma idempotency_key informa de la aprobación en lugar de crear una segunda.

Límites de tasa

Cada plan tiene un tope por minuto para todo el espacio de trabajo. Una sola clave puede usar el límite por minuto completo del plan; varias claves lo comparten.

PlanSolicitudes por minutoWebhooks
Free60No
Personal120No
Pro300Sí
Business1000Sí
  • Las respuestas de tools/call llevan RateLimit-Limit, RateLimit-Remaining y RateLimit-Reset (en segundos) para la ventana más cercana a llenarse, de modo que un cliente puede regular su ritmo antes de alcanzar el límite.
  • Cada clave tiene además ventanas por hora y por día. Un 429 indica en data.window qué ventana se llenó.
  • Un 429 lleva Retry-After en segundos. Espera ese tiempo y repite la misma solicitud.
  • Gratis incluye una asignación mensual de acciones de correo. Los planes de pago no tienen tope mensual ni cargo por llamada.

Webhooks para el correo nuevo

En Pro y Business, MCPEmails puede hacer un POST a tu URL cuando llega correo a un buzón, para que un flujo de trabajo arranque con el correo nuevo en lugar de consultar periódicamente. Crea un endpoint en el panel, elige sus buzones y copia el secreto de firma, que se muestra una sola vez. La URL debe ser HTTPS, en el puerto 443 u 8443. Usa el botón de prueba para enviar un evento ping firmado.

El evento

HTTP
POST /your/webhook HTTP/1.1
Content-Type: application/json
MCPEmails-Event-Id: evt_...
MCPEmails-Event-Type: message.received
MCPEmails-Signature: t=1792843200,v1=5f2b...

{
  "id": "evt_...",
  "type": "message.received",
  "created_at": "2026-10-24T09:20:00.000Z",
  "workspace_id": "...",
  "inbox_id": "...",
  "folder": "INBOX",
  "count": 1,
  "message_ids": ["..."]
}
  • type es message.received. message_ids lista los mensajes nuevos de la carpeta INBOX de ese buzón, hasta 10 por evento; count es cuántos llegaron, que pueden ser más.
  • Por defecto la carga lleva solo ids, sin contenido. Un endpoint configurado para incluir cabeceras recibe además una lista messages con el id, from y subject de cada mensaje.
  • Obtén el mensaje con email_read (action: "read", el inbox_id y un message_id) usando una clave de API. Los ámbitos y el acceso a buzones de esa clave se aplican como siempre.
  • La entrega suele producirse unos dos minutos después de llegar el mensaje. Los buzones se revisan según un horario, así que no es instantánea.

Verifica la firma

Cada entrega lleva MCPEmails-Signature: t=<segundos unix>,v1=<hex>, donde v1 es el HMAC-SHA256 de la marca de tiempo, un punto y el cuerpo crudo de la solicitud, con el secreto del endpoint como clave. La clave es el secreto completo tal como se muestra, prefijo whsec_ incluido. Recalcula la firma sobre el cuerpo crudo, compara en tiempo constante y rechaza una marca de tiempo de más de cinco minutos.

JavaScript
import crypto from 'node:crypto';

// rawBody: the request body exactly as received, before JSON.parse.
export function verifyMcpemailsSignature(rawBody, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    String(header ?? '').split(',').map((part) => part.trim().split('=')),
  );
  const timestamp = Number(parts.t);
  if (!Number.isFinite(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest();
  const received = Buffer.from(parts.v1 ?? '', 'hex');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

Reintentos

  • Responde con cualquier estado 2xx en 10 segundos. Cualquier otra cosa cuenta como fallo, incluida una redirección, que no se sigue.
  • Una entrega fallida se reintenta tras 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas y 24 horas. Cada reintento se firma de nuevo con una marca de tiempo nueva.
  • Un endpoint que ha fallado durante 24 horas, o 50 veces seguidas, se desactiva y se avisa por correo al propietario del espacio de trabajo. Puedes volver a activarlo desde el panel.
  • Usa MCPEmails-Event-Id para ignorar una entrega que ya has procesado. Un reintento lleva el mismo id de evento.

Recetas

n8n: ejecutar un flujo de trabajo con el correo nuevo

  • Añade un nodo disparador Webhook con método HTTP POST, y registra su URL de producción como endpoint de webhook en el panel de MCPEmails.
  • Añade un nodo HTTP Request: método POST, URL https://mcpemails.com/api/mcp, autenticación Generic Credential Type → Header Auth con nombre Authorization y valor Bearer mcpe_....
  • Activa Send Body, elige JSON y usa el cuerpo de abajo para leer el mensaje nuevo. Los nodos siguientes reciben el mensaje en result.structuredContent.
  • Para comprobar la firma en n8n, activa Raw Body en el nodo Webhook y ejecuta la verificación de arriba en un nodo Code. En n8n autoalojado, permite el módulo crypto con NODE_FUNCTION_ALLOW_BUILTIN=crypto.
JSON
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "email_read",
    "arguments": {
      "action": "read",
      "inbox_id": "{{ $json.body.inbox_id }}",
      "message_id": "{{ $json.body.message_ids[0] }}"
    }
  }
}

Para un nodo AI Agent que lea y envíe correo por sí mismo, consulta la guía de n8n.

Claude Code

Añade el servidor con una clave en una cabecera. Es lo adecuado para máquinas sin interfaz y CI, donde no hay navegador para el inicio de sesión OAuth:

bash
claude mcp add --transport http mcpemails https://mcpemails.com/api/mcp \
  --header "Authorization: Bearer $MCPEMAILS_KEY"

La guía de Claude Code cubre el inicio de sesión OAuth y la elección de ámbitos.

Un script corto en Node

Lista todos los mensajes no leídos de un buzón, página a página, y espera a que pase un 429:

JavaScript
// node agent.mjs  (Node 18 or later, MCPEMAILS_KEY in the environment)
const ENDPOINT = 'https://mcpemails.com/api/mcp';
let nextId = 1;

async function callTool(name, args) {
  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.MCPEMAILS_KEY}`,
      'Content-Type': 'application/json',
      Accept: 'application/json',
    },
    body: JSON.stringify({ jsonrpc: '2.0', id: nextId++, method: 'tools/call', params: { name, arguments: args } }),
  });
  if (res.status === 429) {
    const wait = Number(res.headers.get('Retry-After') ?? 1);
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
    return callTool(name, args);
  }
  const reply = await res.json();
  if (reply.error) throw new Error(`${reply.error.code}: ${reply.error.message}`);
  if (reply.result.isError) throw new Error(reply.result.content[0].text);
  return reply.result.structuredContent;
}

const inbox = 'you@example.com';
let offset = 0;
do {
  const page = await callTool('email_read', { action: 'list', inbox, unread: true, limit: 50, offset });
  for (const m of page.messages) console.log(m.id, m.from?.email, m.subject);
  offset = page.has_more ? page.next_offset : null;
} while (offset !== null);

OpenClaw

OpenClaw inicia sesión con OAuth por defecto; la guía de OpenClaw lo explica paso a paso. Para usar en su lugar una clave con ámbitos, ponla en el mapa headers de la entrada del servidor y deja auth sin definir, porque OpenClaw ignora una cabecera Authorization estática en una entrada configurada con OAuth.

JSON
{
  "mcp": {
    "servers": {
      "mcpemails": {
        "url": "https://mcpemails.com/api/mcp",
        "transport": "streamable-http",
        "headers": { "Authorization": "Bearer mcpe_YOUR_KEY_HERE" }
      }
    }
  }
}

Empieza con un buzón, gratis

Crea una clave, llama a tools/list y mira cómo tu propio correo vuelve como JSON.