Alles, was ein MCP-Client kann, kann ein Skript über gewöhnliches HTTPS. Ein Endpunkt, ein API-Schlüssel, JSON rein und JSON raus. Dieser Leitfaden behandelt Authentifizierung, die drei Aufrufe, die Sie brauchen, Fehler, Wiederholungen, Limits und Webhooks für neue E-Mails.
Jede Anfrage ist ein JSON-RPC-2.0-POST an https://mcpemails.com/api/mcp. Das ist derselbe Endpunkt, den claude.ai, ChatGPT und andere MCP-Clients verwenden, mit denselben Tools und denselben Berechtigungen. Ein Skript und ein Chat-Assistent sehen also ein und dasselbe Produkt.
Content-Type: application/json und Accept: application/json. Die Antwort ist ein einzelnes JSON-Objekt.Authorization-Header.structuredContent-Objekt, sodass Sie nie Fließtext parsen müssen. Dieselben Daten stehen als JSON-String in content[0].text.mcpe_ und 64 Hexadezimalzeichen. Er wird einmal angezeigt, beim Erstellen, und nur ein Hash wird gespeichert. Kopieren Sie ihn dann; ein verlorener Schlüssel lässt sich nicht wiederherstellen, nur ersetzen.Authorization: Bearer mcpe_.... Setzen Sie einen Schlüssel nie in eine URL oder einen Query-String.Jeder Schlüssel hat nur die Berechtigungen, die Sie beim Erstellen ankreuzen. Sie können sie später unter Zugriff bearbeiten ändern; die Änderung gilt ab dem nächsten Aufruf, ohne neu zu verbinden.
| Scope | Erlaubt |
|---|---|
read:email | Nachrichten auflisten, lesen und durchsuchen, Anhänge herunterladen, Postfächer und Ordner auflisten. |
search:email | Für ältere Schlüssel beibehalten. Die Suche ist durch read:email abgedeckt. |
send:email | Senden, antworten und weiterleiten. |
manage:drafts | Entwürfe erstellen, aktualisieren und löschen. Einen Entwurf zu senden braucht zusätzlich send:email. |
manage:folders | Nachrichten verschieben, kopieren, markieren und archivieren; Ordner erstellen, umbenennen und löschen. |
delete:email | Nachrichten einzeln oder gesammelt löschen. |
manage:contacts | Kontakte in Ihren letzten E-Mails suchen. |
schedule:email | Sendungen planen und geplante E-Mails verwalten. |
manage:automations | Regeln erstellen, die nach Zeitplan laufen, ohne dass jemand zusieht. |
Beginnen Sie mit read:email und ergänzen Sie einen Schreib-Scope erst, wenn der Workflow ihn braucht. Ein Aufruf außerhalb der Scopes des Schlüssels wird abgelehnt, bevor wir Ihren Mailserver kontaktieren. Betrachter eines Workspace können nur Lese-Scopes halten.
Ein Schlüssel erreicht standardmäßig jedes Postfach im Workspace. Um ihn auf einige davon festzulegen, öffnen Sie Zugriff bearbeiten am Schlüssel und wählen die Postfächer unter Postfachzugriff. Die Einschränkung wird bei jedem Aufruf geprüft: Ein Schlüssel, der auf das Support-Postfach festgelegt ist, kann das Finanz-Postfach nicht lesen, egal was die Anfrage sagt.
Apps, die Nutzer selbst verbinden, etwa claude.ai, ChatGPT und andere MCP-Clients, melden sich mit OAuth statt mit einem Schlüssel an: dynamische Client-Registrierung, Authorization Code mit PKCE und ein Zustimmungsbildschirm, auf dem der Nutzer Scopes und Postfächer wählt. Siehe OAuth-Verbindung. Für Ihre eigenen Skripte, Server und Workflows ist ein API-Schlüssel einfacher.
MCP-Clients beginnen mit initialize. Ein Skript kann es überspringen, weil der Server keine Sitzung hält, aber es ist ein günstiger Weg, um zu prüfen, ob der Schlüssel funktioniert, und die Anweisungen des Servers zu lesen.
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": {}
}
}'tools/list liefert jedes Tool, das dieser Schlüssel aufrufen darf, mit Beschreibung, Eingabeschema und Ausgabeschema. Lesen Sie es beim Start, statt Tool-Namen oder Argumente fest zu verdrahten: Die Liste ist nach den Scopes des Schlüssels gefiltert, und Tools bekommen mit der Zeit neue Argumente.
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" }'Rufen Sie ein Tool über seinen Namen mit seinen Argumenten auf. Die meisten Tools nehmen eine action, die bestimmt, was sie tun, etwa list, read oder search bei email_read. Benennen Sie das Postfach mit inbox (seiner Adresse) oder inbox_id; inbox_list liefert beides.
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 }
}
}'Ein erfolgreiches Ergebnis:
{
"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
}
}
}Die Tool-Referenz beschreibt jedes Tool, und der curl-Leitfaden enthält weitere Beispiele.
Es gibt zwei Arten. Eine Anfrage, die gar nicht ausgeführt werden konnte, kommt als JSON-RPC-error mit einem HTTP-Status zurück. Eine Anfrage, die ein Tool erreicht hat und von ihm abgelehnt wurde, kommt als normales result mit isError: true zurück, damit ein KI-Modell den Grund liest. Verzweigen Sie anhand von data.error_code oder dem _meta-Code, nicht anhand des Meldungstexts.
| Antwort | Wann | Was zu tun ist |
|---|---|---|
HTTP 401, -32001 | Fehlender, fehlerhafter, widerrufener oder abgelaufener Schlüssel. | Korrigieren Sie den Authorization-Header oder erstellen Sie einen neuen Schlüssel. Ein erneuter Versuch hilft nicht. |
HTTP 403, -32004, insufficient_scope | Dem Schlüssel fehlt ein Scope, den diese Tool-Aktion braucht. data.required_scopes listet, was sie erlauben würde. | Ergänzen Sie den Scope unter Zugriff bearbeiten oder verwenden Sie einen anderen Schlüssel. |
HTTP 429, -32003, rate_limit_exceeded | Zu viele Anfragen. data.window sagt, welches Zeitfenster voll ist. | Warten Sie Retry-After Sekunden (auch in data.retry_after) und senden Sie dann dieselbe Anfrage erneut. |
-32602 | Unbekannter Tool-Name. data.available_tools listet die Namen, die dieser Schlüssel aufrufen kann. | Verwenden Sie einen Namen aus tools/list. |
isError: true, invalid_arguments | Die Argumente haben das Schema des Tools nicht bestanden. Nichts wurde ausgeführt. Der Text nennt jedes fehlerhafte Argument und, wenn ein Argumentname geraten wurde, den, der existiert. | Korrigieren Sie die Argumente. Dieselben Argumente werden auf dieselbe Weise abgelehnt. |
isError: true, usage_limit_reached | Ein Free-Workspace hat seine E-Mail-Aktionen für den Monat verbraucht. | Warten Sie auf das Rücksetzdatum in _meta oder führen Sie ein Upgrade durch. Bezahlte Tarife haben keine monatliche Obergrenze. |
isError: true, sonstige | Das Tool lief und ist gescheitert: Nachricht nicht gefunden, Anbieter hat abgelehnt, ungültiger Empfänger und so weiter. | Lesen Sie content[0].text. Dort steht, was passiert ist und ob etwas gesendet wurde. |
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/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, ... }
}
}{
"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": "..." }]
}
}
}
}Jedes Listen- und Suchergebnis endet mit denselben vier Feldern:
has_more: true, wenn eine weitere Seite existiert. Nur has_more: false bedeutet, dass Sie alles gesehen haben.next_offset: Geben Sie ihn als offset zurück, mit allen anderen Argumenten unverändert, um die nächste Seite zu erhalten. Er ist null, wenn has_more false ist.total: die Zahl der Treffer, oder null, wenn der Anbieter sie nicht zählen kann. Sie widerspricht nie der Seite, die Sie gerade halten.total_is_estimate: true, wenn total eine Schätzung oder eine Untergrenze ist und keine exakte Zählung.Lange Nachrichtentexte werden auf dieselbe Weise in Abschnitte geteilt: Wenn body_truncated true ist, lesen Sie erneut mit body_next_offset als body_offset.
Eine abgebrochene Verbindung nach einem Versand lässt Sie im Unklaren, ob die E-Mail rausgegangen ist. Übergeben Sie einen idempotency_key und wiederholen Sie mit demselben Schlüssel: Die Arbeit läuft genau einmal.
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"
}
}
}'tools/list zeigt idempotency_key im Eingabeschema jedes Tools, das ihn annimmt.idempotent_replay: true, den ursprünglichen status und das ursprüngliche Ergebnis in result, einschließlich der message_id, die Ihnen vielleicht verloren ging.idempotency_key_conflict abgelehnt. Verwenden Sie für eine neue Operation einen neuen Schlüssel.status: "processing". Versuchen Sie es kurz darauf erneut.status: "unknown". Prüfen Sie den Ordner Gesendet, bevor Sie unter einem neuen Schlüssel erneut senden.{
"idempotency_key": "order-1042-shipped",
"idempotent_replay": true,
"status": "succeeded",
"result": { "message_id": "...", "inbox_id": "...", "status": "sent" },
"message": "This logical request was already processed. ..."
}Jedes Postfach kann jede Sendung zurückhalten, bis eine Person sie freigibt. Eine Sendung aus Ihrem Skript liefert dann eine approval_id mit status: "pending_approval", statt rauszugehen, und geht raus, sobald jemand sie im Dashboard freigibt. Das funktioniert in jedem Tarif und ist eine gute Voreinstellung, solange sich eine neue Automatisierung erst bewähren muss. Eine Wiederholung mit demselben idempotency_key meldet die Freigabe, statt eine zweite anzulegen.
Jeder Tarif hat eine Obergrenze pro Minute für den gesamten Workspace. Ein einzelner Schlüssel kann das volle Minutenlimit des Tarifs nutzen; mehrere Schlüssel teilen es sich.
| Tarif | Anfragen pro Minute | Webhooks |
|---|---|---|
| Free | 60 | Nein |
| Personal | 120 | Nein |
| Pro | 300 | Ja |
| Business | 1.000 | Ja |
tools/call-Antworten tragen RateLimit-Limit, RateLimit-Remaining und RateLimit-Reset (in Sekunden) für das Zeitfenster, das am nächsten an seiner Grenze ist, damit ein Client sein Tempo anpassen kann, bevor er das Limit erreicht.data.window das Fenster, das voll ist.Retry-After in Sekunden. Warten Sie so lange und senden Sie dann dieselbe Anfrage erneut.In Pro und Business kann MCPEmails einen POST an Ihre URL senden, wenn E-Mails in einem Postfach eintreffen, sodass ein Workflow bei neuer Post startet, statt abzufragen. Erstellen Sie im Dashboard einen Endpunkt, wählen Sie seine Postfächer und kopieren Sie das Signaturgeheimnis, das einmal angezeigt wird. Die URL muss HTTPS sein, auf Port 443 oder 8443. Mit der Testschaltfläche senden Sie ein signiertes ping-Ereignis.
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 ist message.received. message_ids listet die neuen Nachrichten im Ordner INBOX dieses Postfachs, bis zu 10 pro Ereignis; count ist die Zahl der eingetroffenen Nachrichten und kann höher sein.messages-Liste mit id, from und subject jeder Nachricht.email_read (action: "read", der inbox_id und einer message_id) über einen API-Schlüssel. Die Scopes und der Postfachzugriff dieses Schlüssels gelten wie gewohnt.Jede Zustellung trägt MCPEmails-Signature: t=<Unix-Sekunden>,v1=<hex>, wobei v1 der HMAC-SHA256 über den Zeitstempel, einen Punkt und den rohen Anfragekörper ist, mit dem Geheimnis des Endpunkts als Schlüssel. Der Schlüssel ist das ganze Geheimnis wie angezeigt, einschließlich des Präfixes whsec_. Berechnen Sie die Signatur über den rohen Körper neu, vergleichen Sie in konstanter Zeit und lehnen Sie einen Zeitstempel ab, der älter als fünf Minuten ist.
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);
}MCPEmails-Event-Id, um eine bereits verarbeitete Zustellung zu ignorieren. Eine Wiederholung trägt dieselbe Ereignis-ID.POST hinzu und registrieren Sie seine Produktions-URL als Webhook-Endpunkt im MCPEmails-Dashboard.POST, URL https://mcpemails.com/api/mcp, Authentifizierung Generic Credential Type → Header Auth mit dem Namen Authorization und dem Wert Bearer mcpe_....result.structuredContent.NODE_FUNCTION_ALLOW_BUILTIN=crypto.{
"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] }}"
}
}
}Für einen AI-Agent-Node, der selbst E-Mails liest und sendet, siehe den n8n-Leitfaden.
Fügen Sie den Server mit einem Schlüssel in einem Header hinzu. Das passt zu Headless-Maschinen und CI, wo es keinen Browser für die OAuth-Anmeldung gibt:
claude mcp add --transport http mcpemails https://mcpemails.com/api/mcp \
--header "Authorization: Bearer $MCPEMAILS_KEY"Der Claude-Code-Leitfaden behandelt die OAuth-Anmeldung und die Wahl der Scopes.
Listet jede ungelesene Nachricht in einem Postfach, Seite für Seite, und wartet einen 429 ab:
// 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 meldet sich standardmäßig mit OAuth an; der OpenClaw-Leitfaden führt Sie hindurch. Um stattdessen einen eingeschränkten Schlüssel zu verwenden, tragen Sie ihn in die headers-Map des Servereintrags ein und lassen auth leer, denn OpenClaw ignoriert einen statischen Authorization-Header bei einem Eintrag, der auf OAuth steht.
{
"mcp": {
"servers": {
"mcpemails": {
"url": "https://mcpemails.com/api/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer mcpe_YOUR_KEY_HERE" }
}
}
}
}Erstellen Sie einen Schlüssel, rufen Sie tools/list auf und sehen Sie Ihre eigenen E-Mails als JSON zurückkommen.