Documentation

Construire sur MCPEmails

Tout ce qu'un client MCP fait, un script peut le faire en HTTPS ordinaire. Un endpoint, une clé API, du JSON en entrée et du JSON en sortie. Ce guide couvre l'authentification, les trois appels dont vous avez besoin, les erreurs, les nouvelles tentatives, les limites et les webhooks pour le nouveau courrier.

L'endpoint

Chaque requête est un POST JSON-RPC 2.0 vers https://mcpemails.com/api/mcp. C'est le même endpoint que celui qu'utilisent claude.ai, ChatGPT et les autres clients MCP, avec les mêmes outils et les mêmes permissions : un script et un assistant de chat voient un seul et même produit.

  • Envoyez Content-Type: application/json et Accept: application/json. La réponse est un seul objet JSON.
  • Le serveur ne garde aucune session. Il n'y a rien à ouvrir ni à fermer, et chaque requête porte son propre en-tête Authorization.
  • Les résultats d'outil réussis portent un objet structuredContent typé, vous n'avez donc jamais à analyser de la prose. Les mêmes données sont dans content[0].text sous forme de chaîne JSON.

Authentification

Clés API

  • Créez une clé dans le tableau de bord sous Clés API → Nouvelle clé. Nommez-la d'après le script ou le workflow qui l'utilisera.
  • Une clé est mcpe_ suivi de 64 caractères hexadécimaux. Elle est affichée une seule fois, à sa création, et seul un hachage est conservé. Copiez-la à ce moment-là ; une clé perdue ne se récupère pas, elle se remplace.
  • Envoyez-la à chaque requête sous la forme Authorization: Bearer mcpe_.... Ne mettez jamais une clé dans une URL ou une query string.
  • Chaque formule peut créer autant de clés que nécessaire. Utilisez une clé par script ou workflow, pour pouvoir en révoquer une sans toucher aux autres.
  • Révoquer une clé sur la page Clés API coupe l'accès immédiatement.

Portées

Chaque clé ne détient que les permissions que vous cochez à sa création. Vous pouvez les modifier plus tard avec Modifier l'accès, et le changement s'applique dès l'appel suivant, sans reconnexion.

PortéeAutorise
read:emailLister, lire et rechercher des messages, télécharger des pièces jointes, lister les boîtes et les dossiers.
search:emailConservée pour les anciennes clés. La recherche est couverte par read:email.
send:emailEnvoyer, répondre et transférer.
manage:draftsCréer, modifier et supprimer des brouillons. Envoyer un brouillon demande aussi send:email.
manage:foldersDéplacer, copier, marquer et archiver des messages ; créer, renommer et supprimer des dossiers.
delete:emailSupprimer des messages, un par un ou en masse.
manage:contactsRechercher des contacts dans votre courrier récent.
schedule:emailProgrammer des envois et gérer les e-mails programmés.
manage:automationsCréer des règles qui s'exécutent selon un calendrier sans que personne ne surveille.

Commencez avec read:email et n'ajoutez une portée d'écriture que lorsque le workflow en a besoin. Un appel hors des portées de la clé est refusé avant que nous contactions votre serveur de messagerie. Les lecteurs d'un espace de travail ne peuvent détenir que des portées de lecture.

Accès aux boîtes

Par défaut, une clé atteint toutes les boîtes de l'espace de travail. Pour la restreindre à certaines d'entre elles, ouvrez Modifier l'accès sur la clé et choisissez les boîtes sous Accès aux boîtes. La restriction est vérifiée à chaque appel : une clé limitée à la boîte support ne peut pas lire celle de la comptabilité, quoi que dise la requête.

OAuth pour les applications connecteurs

Les applications que les gens connectent eux-mêmes, comme claude.ai, ChatGPT et les autres clients MCP, se connectent avec OAuth plutôt qu'avec une clé : enregistrement dynamique du client, code d'autorisation avec PKCE et un écran de consentement où l'utilisateur choisit les portées et les boîtes. Voir Connexion OAuth. Pour vos propres scripts, serveurs et workflows, une clé API est plus simple.

Les trois appels

1. initialize (facultatif)

Les clients MCP commencent par initialize. Un script peut s'en passer, puisque le serveur ne garde aucune session, mais c'est un moyen peu coûteux de vérifier que la clé fonctionne et de lire les instructions du serveur.

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 renvoie chaque outil que cette clé peut appeler, avec sa description, son schéma d'entrée et son schéma de sortie. Lisez-le au démarrage plutôt que de coder en dur des noms d'outils ou des arguments : la liste est filtrée selon les portées de la clé, et les outils gagnent des arguments au fil du temps.

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

Appelez un outil par son nom avec ses arguments. La plupart des outils prennent une action qui choisit ce qu'ils font, comme list, read ou search sur email_read. Désignez la boîte avec inbox (son adresse) ou inbox_id ; inbox_list renvoie les deux.

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 résultat réussi :

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 référence des outils décrit chaque outil, et le guide curl contient d'autres exemples.

Erreurs

Il en existe deux sortes. Une requête qui n'a pas pu s'exécuter du tout revient comme une error JSON-RPC avec un statut HTTP. Une requête qui a atteint un outil et a été refusée par lui revient comme un result normal avec isError: true, pour qu'un modèle d'IA lise la raison. Branchez sur data.error_code ou sur le code _meta, pas sur le texte du message.

RéponseQuandQue faire
HTTP 401, -32001Clé absente, malformée, révoquée ou expirée.Corrigez l'en-tête Authorization ou créez une nouvelle clé. Réessayer ne servira à rien.
HTTP 403, -32004, insufficient_scopeLa clé n'a pas une portée dont cette action d'outil a besoin. data.required_scopes liste ce qui l'autoriserait.Ajoutez la portée avec Modifier l'accès, ou utilisez une autre clé.
HTTP 429, -32003, rate_limit_exceededTrop de requêtes. data.window indique quelle fenêtre est saturée.Attendez Retry-After secondes (aussi dans data.retry_after), puis renvoyez la même requête.
-32602Nom d'outil inconnu. data.available_tools liste les noms que cette clé peut appeler.Utilisez un nom tiré de tools/list.
isError: true, invalid_argumentsLes arguments n'ont pas passé le schéma de l'outil. Rien n'a été exécuté. Le texte nomme chaque argument fautif et, quand un nom d'argument a été deviné, indique celui qui existe.Corrigez les arguments. Les mêmes arguments sont refusés de la même manière.
isError: true, usage_limit_reachedUn espace de travail Gratuit a épuisé ses actions e-mail du mois.Attendez la date de réinitialisation dans _meta, ou passez à une formule supérieure. Les formules payantes n'ont pas de plafond mensuel.
isError: true, autreL'outil s'est exécuté et a échoué : message introuvable, refus du fournisseur, destinataire invalide, etc.Lisez content[0].text. Il dit ce qui s'est passé et si quelque chose a été envoyé.
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": "..." }]
      }
    }
  }
}

Pagination

Chaque résultat de liste ou de recherche se termine par les mêmes quatre champs :

  • has_more : true quand une autre page existe. Seul has_more: false signifie que vous avez tout vu.
  • next_offset : renvoyez-le comme offset, avec tous les autres arguments inchangés, pour obtenir la page suivante. Il vaut null quand has_more est false.
  • total : le nombre de correspondances, ou null quand le fournisseur ne peut pas les compter. Il ne contredit jamais la page que vous tenez.
  • total_is_estimate : true quand total est une estimation ou un minimum plutôt qu'un décompte exact.

Les corps de message longs sont découpés de la même façon : quand body_truncated est true, relisez avec body_next_offset comme body_offset.

Nouvelles tentatives et clés d'idempotence

Une connexion coupée après un envoi vous laisse sans savoir si le courrier est parti. Passez une idempotency_key et réessayez avec la même clé : le travail ne s'exécute qu'une fois.

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"
      }
    }
  }'
  • Acceptée sur les envois, réponses, transferts, envois programmés, écritures de brouillons et modifications de boîte comme déplacer, marquer et supprimer. tools/list affiche idempotency_key dans le schéma d'entrée de chaque outil qui l'accepte.
  • Jusqu'à 200 caractères. Utilisez quelque chose de votre propre système, comme un numéro de commande et un nom d'événement.
  • Les clés sont rattachées à la clé API qui les a envoyées et mémorisées pendant 24 heures.
  • Une nouvelle tentative avec la même clé et les mêmes arguments ne s'exécute pas à nouveau. Elle renvoie idempotent_replay: true, le status d'origine et le résultat d'origine dans result, y compris le message_id que vous avez peut-être perdu.
  • La même clé avec des arguments différents est refusée avec idempotency_key_conflict. Utilisez une nouvelle clé pour une nouvelle opération.
  • Une nouvelle tentative pendant que le premier appel s'exécute encore renvoie status: "processing". Réessayez peu après.
  • Quand le fournisseur de messagerie a refusé un envoi avant de l'accepter, rien n'a été envoyé et la clé est libérée, de sorte que la nouvelle tentative identique envoie réellement.
  • Quand l'issue est incertaine, la relecture indique status: "unknown". Vérifiez les éléments envoyés avant de renvoyer sous une nouvelle clé.
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. ..."
}

Approbation des envois

N'importe quelle boîte peut retenir chaque envoi jusqu'à ce qu'une personne l'approuve. Un envoi depuis votre script renvoie alors un approval_id avec status: "pending_approval" au lieu de partir, et il part quand quelqu'un l'approuve dans le tableau de bord. Cela fonctionne sur toutes les formules et constitue un bon réglage par défaut le temps qu'une nouvelle automatisation fasse ses preuves. Une nouvelle tentative avec la même idempotency_key signale l'approbation au lieu d'en créer une seconde.

Limites de débit

Chaque formule a un plafond par minute pour l'ensemble de l'espace de travail. Une seule clé peut utiliser toute la limite par minute de la formule ; plusieurs clés la partagent.

FormuleRequêtes par minuteWebhooks
Free60Non
Personal120Non
Pro300Oui
Business1 000Oui
  • Les réponses de tools/call portent RateLimit-Limit, RateLimit-Remaining et RateLimit-Reset (en secondes) pour la fenêtre la plus proche de la saturation, afin qu'un client puisse régler son rythme avant d'atteindre la limite.
  • Chaque clé a aussi des fenêtres horaire et quotidienne. Un 429 nomme la fenêtre saturée dans data.window.
  • Un 429 porte Retry-After en secondes. Attendez ce délai, puis renvoyez la même requête.
  • Gratuit inclut une allocation mensuelle d'actions e-mail. Les formules payantes n'ont ni plafond mensuel ni facturation à l'appel.

Webhooks pour le nouveau courrier

Sur Pro et Business, MCPEmails peut envoyer un POST à votre URL quand du courrier arrive dans une boîte, pour qu'un workflow démarre sur le nouveau courrier au lieu d'interroger le serveur. Créez un endpoint dans le tableau de bord, choisissez ses boîtes et copiez le secret de signature, affiché une seule fois. L'URL doit être en HTTPS, sur le port 443 ou 8443. Utilisez le bouton de test pour envoyer un événement ping signé.

L'événement

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 vaut message.received. message_ids liste les nouveaux messages du dossier INBOX de cette boîte, jusqu'à 10 par événement ; count indique combien sont arrivés, ce qui peut être plus.
  • Par défaut, la charge utile ne contient que des identifiants, aucun contenu. Un endpoint réglé pour inclure les en-têtes reçoit aussi une liste messages avec id, from et subject de chaque message.
  • Récupérez le message avec email_read (action: "read", l'inbox_id et un message_id) en utilisant une clé API. Les portées et l'accès aux boîtes de cette clé s'appliquent comme d'habitude.
  • La livraison intervient généralement dans les deux minutes environ après l'arrivée du message. Les boîtes sont vérifiées selon un calendrier, ce n'est donc pas instantané.

Vérifier la signature

Chaque livraison porte MCPEmails-Signature: t=<secondes unix>,v1=<hex>, où v1 est le HMAC-SHA256 de l'horodatage, d'un point et du corps brut de la requête, avec le secret de l'endpoint comme clé. La clé est le secret entier tel qu'affiché, préfixe whsec_ inclus. Recalculez la signature sur le corps brut, comparez en temps constant et rejetez tout horodatage de plus de cinq minutes.

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);
}

Nouvelles tentatives

  • Répondez avec n'importe quel statut 2xx en moins de 10 secondes. Tout le reste compte comme un échec, y compris une redirection, qui n'est pas suivie.
  • Une livraison échouée est retentée après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures et 24 heures. Chaque nouvelle tentative est signée à nouveau avec un horodatage frais.
  • Un endpoint qui échoue depuis 24 heures, ou 50 fois de suite, est désactivé et le propriétaire de l'espace de travail en est informé par e-mail. Vous pouvez le réactiver depuis le tableau de bord.
  • Utilisez MCPEmails-Event-Id pour ignorer une livraison déjà traitée. Une nouvelle tentative porte le même identifiant d'événement.

Recettes

n8n : lancer un workflow sur le nouveau courrier

  • Ajoutez un nœud déclencheur Webhook avec la méthode HTTP POST, et enregistrez son URL de production comme endpoint de webhook dans le tableau de bord MCPEmails.
  • Ajoutez un nœud HTTP Request : méthode POST, URL https://mcpemails.com/api/mcp, authentification Generic Credential Type → Header Auth avec le nom Authorization et la valeur Bearer mcpe_....
  • Activez Send Body, choisissez JSON et utilisez le corps ci-dessous pour lire le nouveau message. Les nœuds suivants reçoivent le message dans result.structuredContent.
  • Pour vérifier la signature dans n8n, activez Raw Body sur le nœud Webhook et exécutez la vérification ci-dessus dans un nœud Code. Sur un n8n auto-hébergé, autorisez le module crypto avec 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] }}"
    }
  }
}

Pour un nœud AI Agent qui lit et envoie lui-même du courrier, voir le guide n8n.

Claude Code

Ajoutez le serveur avec une clé dans un en-tête. Cela convient aux machines sans interface et à la CI, où il n'y a pas de navigateur pour la connexion OAuth :

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

Le guide Claude Code couvre la connexion OAuth et le choix des portées.

Un court script Node

Liste chaque message non lu d'une boîte, page par page, et patiente le temps d'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 se connecte avec OAuth par défaut ; le guide OpenClaw le détaille. Pour utiliser plutôt une clé à portées limitées, placez-la dans la map headers de l'entrée du serveur et laissez auth non défini, car OpenClaw ignore un en-tête Authorization statique sur une entrée réglée en OAuth.

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

Commencez avec une boîte, gratuitement

Créez une clé, appelez tools/list et regardez votre propre courrier revenir en JSON.