KI-Agenten & MCP

E-Mail für KI-Agenten: Registrierung bestätigen ohne Mensch

Ein Agent, der browsen, Formulare ausfüllen und bezahlen kann, bleibt trotzdem genau bei „Prüfen Sie Ihre E-Mails auf den Code“ hängen, weil er kein Postfach hat. Hier erfahren Sie, wie Sie ihm eines geben, das weder Konto noch Schlüssel braucht — über MCP mit einem blockierenden Warten, oder als einfache Tool-Funktion für LangChain, das OpenAI Agents SDK und das Vercel AI SDK — mit der vierstufigen Schleife, die eine Anmeldung durchträgt, und den Leitplanken, die ein unbeaufsichtigter Agent braucht.

  • Fortgeschritten
  • 21 Min. Lesezeit
Ein kleiner grauer Roboter an einem grauen Schreibtisch hält einen blauen Umschlag hoch, eine Hand auf einer Tastatur, darüber schwebt ein blaues Häkchen

Die Mauer, an die jeder Agent stößt

Sich für eine Testversion anmelden, einen Workspace anlegen, einen API-Schlüssel beanspruchen, einer Beta beitreten: Jedes davon endet bei einem Formular, und jedes Formular endet bei einem Postfach. Ein Mensch wirft einen Blick aufs Handy. Ein Agent hat nichts, worauf er einen Blick werfen könnte — er hat keine Adresse, die er lesen kann, und die vernünftigen unter ihnen halten an und bitten Sie um den Code, was den Sinn, überhaupt einen Agenten losgeschickt zu haben, zunichtemacht.

Die Lösung ist kein cleverer Prompt. Es ist ein Postfach, das der Agent programmatisch lesen kann, ohne zuerst ein Konto anzulegen (ein Agent, der ein Mail-Konto anlegt, stößt eine Ebene tiefer auf dieselbe Mauer) und ohne Schlüssel zu verwalten. Ein Wegwerf-Postfach ist genau das: Ein Postfach entsteht in dem Moment, in dem Mail es erreicht, und es zu lesen ist eine einzige HTTP-Anfrage.

Zwei Wege, ihm ein Postfach zu geben

Dasselbe Postfach ist auf zwei Wegen erreichbar, und die Wahl hängt davon ab, wie der Agent gebaut ist, nicht vom Postfach:

KriteriumMCP-ServerREST-Tool-Funktion
Passt, wennDer Agent in einem MCP-Client läuft — Claude Code, Cursor, Claude Desktop, oder ein Framework mit einem MCP-Adapter.Sie den Agenten im Code schreiben: LangChain, das OpenAI Agents SDK, das Vercel AI SDK, oder Ihre eigene Schleife.
Wartenwait_for_message blockiert serverseitig bis zu 25 Sekunden lang und gibt die Nachricht vollständig zurück. Null Tokens verbraucht, während gewartet wird.Die Tool-Funktion pollt einmal pro Sekunde bis zu ihrer Deadline. Ebenfalls null Tokens — die Schleife liegt in Ihrem Code, nicht im Modell.
EinrichtungEine URL in der Konfiguration des Clients. Kein Code.Zwei Funktionen, vierzig Zeilen, eine HTTP-Bibliothek.
Was das Modell siehtSechs Tools mit Beschreibungen, dazu ein Absatz an Anweisungen, den der Server beim Verbindungsaufbau schickt.Was auch immer Ihre Tool-Beschreibungen sagen. Die Docstrings weiter unten sind genau dafür geschrieben.

Der MCP-Weg ist eine Zeile, und die Einrichtung pro Client hat die genaue Zeile für sieben Clients; ein Postfach, das ein KI-Agent lesen kann erklärt die Tools ausführlich. Der Rest dieser Anleitung ist der REST-Weg, für Agenten, die Sie selbst bauen.

der MCP-Weg, vollständig
{"mcpServers":{"grabmail":{"url":"https://grabmail.io/mcp"}}}

Eine Tool-Funktion: Python, für LangChain und das OpenAI Agents SDK

Zwei einfache Funktionen mit sorgfältigen Docstrings. Die Docstrings zählen mehr als der Code: In beiden Frameworks werden sie zu der Beschreibung, die das Modell liest, um zu entscheiden, wann es das Tool aufruft und was es mit der Antwort macht — deshalb sagen sie die zwei Dinge, die ein Agent falsch macht: Alias gegen Adresse, und was timed_out bedeutet.

inbox_tools.py
"""inbox_tools.py — two plain functions any agent framework can wrap. No key, no account."""
import secrets
import time

import requests

API = "https://grabmail.io/api/v1"


def create_inbox() -> dict:
    """Create a fresh disposable email inbox for this task.

    Returns the ADDRESS to poll and the ALIAS to give to websites. Put the alias
    into forms; never hand out the address. Nothing is created server-side.
    """
    address = f"agent-{secrets.token_hex(4)}@grabmail.io"
    r = requests.get(f"{API}/mailbox", params={"address": address}, timeout=15)
    r.raise_for_status()
    return {
        "address": address,
        "alias": r.json().get("alias"),
        "next_step": "Put the alias into the form. Then call wait_for_message with the address.",
    }


def wait_for_message(address: str, subject_contains: str = "", timeout_seconds: int = 60) -> dict:
    """Wait for an email to arrive at the address, up to timeout_seconds.

    Returns the message (from, subject, text, html) or {"status": "timed_out"}.
    On timed_out, call again — up to three times — before concluding no mail was sent.
    """
    deadline = time.monotonic() + timeout_seconds
    while time.monotonic() < deadline:
        r = requests.get(f"{API}/mailbox", params={"address": address}, timeout=15)
        if r.status_code == 429:                       # slow down, do not fail
            time.sleep(float(r.headers.get("Retry-After", 1)))
            continue
        r.raise_for_status()
        for m in r.json()["messages"]:
            if subject_contains.lower() in m["subject"].lower():
                full = requests.get(f"{API}/message/{m['id']}", params={"mailbox": address}, timeout=15)
                full.raise_for_status()
                return full.json()
        time.sleep(1)                                   # one read a second, never throttled
    return {"status": "timed_out", "hint": "Call again, up to three times, before giving up."}

Sie einzubinden ist ein Aufruf pro Framework. Das tool von LangChain liest den Docstring und die Type Hints; das function_tool des Agents SDK macht dasselbe und fügt die Tools einem Agenten hinzu, dessen Anweisungen die Schleife wiederholen:

LangChain
# LangChain: the docstring becomes the tool description the model reads.
from langchain_core.tools import tool

create_inbox_tool = tool(create_inbox)
wait_for_message_tool = tool(wait_for_message)
# agent = create_react_agent(model, tools=[create_inbox_tool, wait_for_message_tool, ...])
OpenAI Agents SDK
# OpenAI Agents SDK: same two functions, same docstrings.
from agents import Agent, Runner, function_tool

signup_agent = Agent(
    name="Signup agent",
    instructions=(
        "When a site needs an email address, call create_inbox once. Put the ALIAS in the form. "
        "Right after submitting, call wait_for_message with the ADDRESS and a word from the expected "
        "subject. If it returns timed_out, call it again, up to three times."
    ),
    tools=[function_tool(create_inbox), function_tool(wait_for_message)],
)

result = Runner.run_sync(signup_agent, "Sign up for a trial at https://app.example.com/signup and report the login.")
print(result.final_output)

Dasselbe Tool in TypeScript, für das Vercel AI SDK

Das tool() des AI SDK nimmt eine Beschreibung, ein Schema und ein execute entgegen; die Beschreibung trägt dieselben zwei Sätze. Übergeben Sie beide Tools an generateText oder streamText mit einem maxSteps über vier, denn die Schleife ist vier Tool-Aufrufe lang:

inbox-tools.ts
// inbox-tools.ts — the same two tools for the Vercel AI SDK (v5 shape: inputSchema + execute).
import { tool } from 'ai';
import { z } from 'zod';

const API = 'https://grabmail.io/api/v1';
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));

export const createInbox = tool({
  description: 'Create a fresh disposable email inbox for this task. Returns the ADDRESS to poll and the ALIAS to give to websites. Put the alias into forms; never hand out the address.',
  inputSchema: z.object({}),
  execute: async () => {
    const address = `agent-${crypto.randomUUID().slice(0, 8)}@grabmail.io`;
    const res = await fetch(`${API}/mailbox?address=${encodeURIComponent(address)}`);
    const { alias } = (await res.json()) as { alias: string | null };
    return { address, alias, next_step: 'Put the alias into the form. Then call waitForMessage with the address.' };
  },
});

export const waitForMessage = tool({
  description: 'Wait for an email to arrive at the address, up to timeoutSeconds. Returns the message (from, subject, text, html) or { status: "timed_out" }. On timed_out, call again — up to three times — before concluding no mail was sent.',
  inputSchema: z.object({
    address: z.string(),
    subjectContains: z.string().optional(),
    timeoutSeconds: z.number().int().min(5).max(120).default(60),
  }),
  execute: async ({ address, subjectContains = '', timeoutSeconds }) => {
    const deadline = Date.now() + timeoutSeconds * 1000;
    while (Date.now() < deadline) {
      const res = await fetch(`${API}/mailbox?address=${encodeURIComponent(address)}`);
      if (res.status === 429) { await sleep(Number(res.headers.get('retry-after') ?? 1) * 1000); continue; }
      if (!res.ok) throw new Error(`GET /mailbox answered ${res.status}`);
      const { messages } = (await res.json()) as { messages: { id: string; subject: string }[] };
      const hit = messages.find(m => m.subject.toLowerCase().includes(subjectContains.toLowerCase()));
      if (hit) return (await fetch(`${API}/message/${hit.id}?mailbox=${encodeURIComponent(address)}`)).json();
      await sleep(1000);
    }
    return { status: 'timed_out', hint: 'Call again, up to three times, before giving up.' };
  },
});

Die Form ist in jedem Framework identisch, das einen Begriff von „Tool“ kennt: eine Beschreibung, die das Modell liest, ein Schema für die Argumente, und eine Funktion, die auf Ihrer Seite läuft. Die zwei Dinge, die Sie übernehmen müssen, sind der Alias-Satz und der timed_out-Satz; alles andere ist der Client aus der Node.js-Anleitung oder der Python-Anleitung.

Die Schleife, in vier Schritten

Ganz gleich welches Framework, eine Anmeldung sind dieselben vier Tool-Aufrufe, und der Agent sollte das in seinen Anweisungen gesagt bekommen, statt es selbst herausfinden zu müssen:

  1. create_inbox, einmal pro Aufgabe. Zurück kommen eine Adresse, ein Alias und ein Satz, der sagt, welches welches ist.
  2. Der Alias kommt ins Formular. Absenden.
  3. wait_for_message auf die Adresse, sofort, mit subject_contains gesetzt auf ein Wort, das die Bestätigungsmail tragen wird — „code“, „verify“, „confirm“. Nicht nach einem Timer, nicht nach anderer Arbeit: Die Mail ist schon unterwegs.
  4. Der Code kommt aus der Nachricht, die das Warten zurückgegeben hat; der Agent tippt ihn ein oder folgt dem Link. Sechs Ziffern nach den Wörtern, die die Vorlage verwendet — OTP-Codes in automatisierten Tests hat die Extraktionsregeln, die für einen Agenten genauso gelten wie für einen Test.

Ein sechzig Sekunden langes Warten, das mit timed_out zurückkommt, ist kein Fehlschlag; es ist ein „noch nicht“. Die Anweisungen sollten sagen: erneut aufrufen, bis zu dreimal. Drei Aufrufe sind über drei Minuten, was jede transaktionale Mail abdeckt, die tatsächlich verschickt wurde — und dem Agenten drei Gelegenheiten gibt zu bemerken, dass das Formular einen Fehler angezeigt hat, oder dass er den Alias falsch eingetippt hat.

Die Alias-Regel

Jedes Postfach hat zwei Adressen. Die Adresse ist die, mit der der Agent liest; wer sie hat, kann das Postfach öffnen, denn es gibt kein Konto, und die Adresse ist der einzige Schlüssel. Der Alias ist eine zweite Adresse auf einer separaten Domain, die in dasselbe Postfach zustellt und sich nicht zum Lesen daraus verwenden lässt.

Die Website bekommt also den Alias, und der Agent behält die Adresse. Ein Agent, der die Adresse in ein Formular einfügt, hat der Website — und jedem, an den die Website sie weitergibt — die Fähigkeit gegeben, jede Nachricht zu lesen, die der Agent dort je empfangen wird. Die Tool-Funktionen oben geben beide zurück, mit einem next_step, der sagt, was wohin gehört, und die Anweisungen wiederholen es, denn eine Regel, die zweimal gesagt wird, ist eine Regel, die befolgt wird.

alias@examplewhat you hand overGrabMailroutes it to the mailboxyou@examplewhat you keepRead it back and you getan empty mailbox. Always.Read it back and you getevery message.The service you signed up to only ever holds the left-hand one.
Der Alias ist das, was der Agent herausgibt und dabei ein leeres Postfach zurückliest; die Adresse ist das, was er behält und dabei jede Nachricht zurückliest.

Leitplanken für einen unbeaufsichtigten Agenten

Eine Testsuite schlägt fehl und hält an. Ein Agent, der eine Situation falsch einschätzt, macht weiter — und gibt weiter aus. Fünf Grenzen verhindern, dass ein E-Mail-Schritt zum teuersten Teil einer Aufgabe wird:

Ein Postfach pro Aufgabe
Verwenden Sie eine Adresse nie über Aufgaben oder Läufe hinweg wieder. Eine alte Nachricht mit einem plausibel aussehenden Code darin ist der schnellste Weg für einen Agenten, selbstsicher das Falsche zu tun. create_inbox kostet nichts; rufen Sie es jedes Mal auf.
Ein Budget an Wartevorgängen
Drei Aufrufe von wait_for_message, dann anhalten und melden. Ein Agent, der unbegrenzt auf eine Mail wartet, die nie verschickt wurde, verbrennt einen Worker-Slot und eine Rechnung.
Eine Deadline für den gesamten Schritt
Fünf Minuten vom Absenden bis zum Code, von Anfang bis Ende. Danach ist die richtige Reaktion, einem Menschen zu sagen, was passiert ist, nicht das Formular erneut zu versuchen.
Betrefffilterung
Übergeben Sie immer subject_contains. Eine Willkommensmail, die vor der Code-Mail ankommt, ist sonst „die Nachricht“, und der Agent liest sechs Ziffern aus einer Marketing-Fußzeile heraus.
Die Adresse protokollieren
Schreiben Sie die Adresse in das Aufgabenprotokoll. Nachrichten bleiben 5 Tage, ein Mensch kann das Postfach also im Nachhinein öffnen und genau sehen, was der Agent gesehen hat — das mit Abstand Nützlichste, wenn ein Lauf schiefgeht.

Browser-Agenten

Ein Agent, der einen echten Browser steuert — Browser Use, ein Playwright-MCP-Server, ein Computer-Use-Modell — ist der Fall, in dem der E-Mail-Schritt am härtesten zuschlägt, weil er auf das Formular treffen wird, bevor irgendjemand das eingeplant hat. Drei Dinge sorgen dafür, dass es funktioniert:

  • Geben Sie ihm beide Server. Die Browser-Tools und die Postfach-Tools in derselben Sitzung, sodass „Prüfen Sie Ihre E-Mails“ ein Tool-Aufruf ist und keine Sackgasse.
  • Setzen Sie die Schleife in den System-Prompt. Vier Zeilen: ein Postfach anlegen beim ersten E-Mail-Feld; den Alias ins Formular; unmittelbar nach dem Absenden auf die Adresse warten; drei Wiederholungen bei timed_out.
  • Rechnen Sie mit Ablehnungen. Ein Formular, das die Domain des Alias zurückweist, wird das auf der Seite sagen; der Agent sollte den Fehler lesen und anhalten, statt Namen durchzuprobieren. Die ehrlichen Wege um eine Ablehnung herum — eine eigene Domain, oder eine Domain aus dem Pool, der von den Sperrlisten ferngehalten wird — sind Konfigurationsentscheidungen für Sie, nicht für den Agenten.

Für die eigene Lektüre des Agenten veröffentlicht die Website llms.txt, eine Klartext-Karte, die dasselbe sagt wie diese Seite, in der Form, die ein Modell bevorzugt, und ein OpenAPI-Dokument, aus dem ein codeschreibender Agent den Client bauen kann.

Bevor Sie ihn unbeaufsichtigt laufen lassen

  • Tool-Beschreibungen, die die Alias-Regel nennen und was timed_out bedeutet.
  • Anweisungen mit der vierstufigen Schleife und einem Budget von drei Wartevorgängen.
  • create_inbox einmal pro Aufgabe aufgerufen, nie wiederverwendet.
  • subject_contains bei jedem Warten.
  • Die Adresse ins Aufgabenprotokoll geschrieben.
  • Eine Zeile, die verbietet, etwas Vertrauliches an das Postfach zu schicken.

Das ist alles, was ein Agent braucht. Dasselbe Postfach dient tagsüber einer Testsuite und nachts einem Agenten, denn darunter liegen dieselben drei HTTP-Aufrufe — und wenn die Website, bei der sich der Agent anmeldet, die öffentlichen Domains ablehnt, lässt sich eine eigene Domain oder der Pool, der von den Listen ferngehalten wird, ohne Änderung an den Tools einsetzen.

Fragen

Brauche ich einen API-Schlüssel für den Agenten?

Nein. Die öffentlichen Domains verlangen weder Schlüssel noch Konto noch Header, über REST genauso wie über MCP. Nur der kostenpflichtige Pool von Domains, der von den Wegwerf-Mail-Sperrlisten ferngehalten wird, verwendet ein Bearer-Token, und die Tool-Funktionen sind dafür ansonsten identisch.

MCP oder ein REST-Tool — was soll ich wählen?

Wenn der Agent schon in einem MCP-Client lebt, MCP: Es ist eine Zeile, und das Warten läuft serverseitig. Wenn Sie den Agenten in einem Framework schreiben, eine Tool-Funktion: Es sind vierzig Zeilen, sie funktioniert mit jedem Modell, und Sie kontrollieren die Beschreibung, die das Modell liest. Beide erreichen dasselbe Postfach.

Wie viel kostet das Warten an Tokens?

Nichts, so oder so. Das MCP-Warten blockiert auf dem Server; das REST-Tool pollt in Ihrem Code. Das Modell gibt Tokens für den Tool-Aufruf und das Lesen des Ergebnisses aus, nicht für die sechzig Sekunden dazwischen — genau deshalb sollte man ein Modell nie selbst ein Postfach pollen lassen.

Können mehrere Agenten gleichzeitig laufen?

Ja. Jede Aufgabe bekommt ihr eigenes Postfach, und es gibt keinen Sitzungsstatus. Die Grenzen sind ein Lesevorgang pro Sekunde und Adresse sowie 1200 Anfragen pro Minute und Client über REST, und 8 gleichzeitige wait_for_message-Aufrufe über MCP — darüber hinaus antwortet das Tool sofort mit timed_out, und der Agent ruft erneut auf.

Kann der Agent E-Mails aus dem Postfach versenden?

Nein. Der Dienst empfängt nur, mit Absicht — ein kostenloses Postfach ohne Konto, das versenden könnte, wäre binnen einer Stunde ein Spam-Relay. Ein Agent, der Mail versenden muss, braucht einen Versand-Provider und eigene Zugangsdaten.

Was, wenn die Website die Domain des Alias ablehnt?

Dann steht sie auf einer Sperrliste für Wegwerf-Domains, und kein Name vor dem @ wird daran etwas ändern. Richten Sie eine Domain, die Ihnen gehört, auf den Dienst aus (ein MX-Eintrag, kostenlos), oder verwenden Sie eine Domain aus dem kostenpflichtigen Pool, der von den Listen ferngehalten wird — beide lassen sich in dieselben Tools einsetzen, indem Sie die Domain-Konstante ändern. Warum Anmeldeformulare Wegwerf-E-Mail blockieren erklärt, welche Prüfung Sie abgelehnt hat.

Ist das Postfach privat für meinen Agenten?

Nein. Wer die Adresse kennt, kann es lesen, weshalb es den Alias gibt und weshalb dort nie etwas Vertrauliches hingeschickt werden sollte. Für einen Code, der zehn Minuten lebt, ist das unproblematisch; es ist die eine Regel, die die Anweisungen des Agenten unmissverständlich festhalten müssen.

Probieren Sie es aus, solange es frisch ist

Eine Adresse braucht einen Klick, kein Konto und keine Karte. Alles in dieser Anleitung funktioniert damit sofort.

Willkommen zurück

Ihre Postfächer und Ihre Domains an einem Ort.