Her ajanın çarptığı duvar
Bir deneme sürümüne kaydolun, bir çalışma alanı oluşturun, bir API anahtarı alın, bir betaya katılın: bunların hepsi bir formda biter ve her form bir posta kutusunda biter. Bir insan telefonuna göz atar. Bir ajanın göz atacağı hiçbir şey yoktur — okuyabileceği bir adresi yoktur ve makul olanlar durup sizden kodu ister, bu da bir ajan göndermenin amacını boşa çıkarır.
Çözüm daha akıllıca bir prompt değildir. Ajanın programatik olarak okuyabileceği, önce oluşturulacak bir hesabı olmayan (bir posta hesabı oluşturan bir ajan, bir kat aşağıda aynı duvarla karşılaşır) ve yönetilecek bir anahtarı olmayan bir gelen kutusudur. Tek kullanımlık bir posta kutusu tam olarak budur: posta ona ulaştığı anda bir posta kutusu var olur ve onu okumak tek bir HTTP isteğidir.
Ona bir gelen kutusu vermenin iki yolu
Aynı posta kutusuna iki şekilde ulaşılabilir ve seçim, posta kutusuna değil ajanın nasıl inşa edildiğine bağlıdır:
| — | MCP sunucusu | REST araç fonksiyonu |
|---|---|---|
| Uyduğu durum | Ajan bir MCP istemcisinde çalışıyor — Claude Code, Cursor, Claude Desktop ya da bir MCP adaptörü olan bir framework. | Ajanı kodla siz yazıyorsunuz: LangChain, OpenAI Agents SDK, Vercel AI SDK ya da kendi döngünüz. |
| Bekleme | wait_for_message, sunucu tarafında en fazla 25 saniye boyunca bloklar ve mesajı tam olarak döndürür. Beklerken sıfır token harcanır. | Araç fonksiyonu, son tarihine kadar saniyede bir döngüye girer. Bu da sıfır token — döngü modelde değil, sizin kodunuzdadır. |
| Kurulum | İstemcinin yapılandırmasında tek bir URL. Kod yok. | İki fonksiyon, kırk satır, bir HTTP kütüphanesi. |
| Modelin gördüğü şey | Açıklamalı altı araç, artı sunucunun bağlantı anında gönderdiği bir talimat paragrafı. | Araç açıklamalarınızın ne dediği. Aşağıdaki docstring'ler tam olarak bunlar olacak şekilde yazılmıştır. |
MCP yolu tek bir satırdır ve istemci başına kurulum rehberinde yedi istemci için tam satır var; bir yapay zekâ ajanının okuyabileceği bir gelen kutusu ise araçları derinlemesine anlatır. Bu rehberin geri kalanı, kendi inşa ettiğiniz ajanlar için REST yoludur.
{"mcpServers":{"grabmail":{"url":"https://grabmail.io/mcp"}}}Bir araç fonksiyonu: LangChain ve OpenAI Agents SDK için Python
Özenli docstring'leri olan iki düz fonksiyon. Docstring'ler koddan daha önemlidir: her iki framework'te de modelin, aracı ne zaman çağıracağına ve yanıtla ne yapacağına karar vermek için okuduğu açıklama haline gelirler, bu yüzden bir ajanın yanlış yaptığı iki şeyi söylerler — takma ad ile adres arasındaki fark, ve timed_out'un ne anlama geldiği.
"""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."}Onları sarmalamak, framework başına tek bir çağrıdır. LangChain'in tool'u docstring'i ve tip ipuçlarını okur; Agents SDK'nın function_tool'u aynısını yapar ve araçları, talimatları döngüyü tekrarlayan bir ajana ekler:
# 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: 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)Aynı araç TypeScript'te, Vercel AI SDK için
AI SDK'nın tool()'u bir açıklama, bir şema ve bir execute alır; açıklama aynı iki cümleyi taşır. Her iki aracı da dörtten yüksek bir maxSteps ile generateText ya da streamText'e geçirin, çünkü döngü dört araç çağrısı uzunluğundadır:
// 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.' };
},
});Şekil, araç kavramı olan her framework'te aynıdır: modelin okuduğu bir açıklama, argümanlar için bir şema ve sizin tarafınızda çalışan bir fonksiyon. Taşınması gereken iki şey takma ad cümlesi ve timed_out cümlesidir; geri kalan her şey Node rehberinden ya da Python rehberinden gelen istemcidir.
Dört adımda döngü
Framework ne olursa olsun, bir kayıt aynı dört araç çağrısıdır ve ajana bunu kendi keşfetmesi değil, talimatlarında söylenmesi gerekir:
- Görev başına bir kez
create_inbox. Karşılığında bir adres, bir takma ad ve hangisinin hangisi olduğunu söyleyen bir cümle gelir. - Takma ad forma girer. Gönderin.
- Adres üzerinde, hemen,
wait_for_message, onay postasının taşıyacağı bir kelimeye ayarlanmışsubject_containsile — "code", "verify", "confirm". Bir zamanlayıcıyla değil, başka bir işten sonra da değil: posta zaten yoldadır. - Kod, beklemenin döndürdüğü mesajdan çıkar; ajan onu yazar ya da bağlantıyı izler. Şablonun kullandığı kelimelerden sonra altı hane — otomatik testlerde OTP kodları rehberinde, bir teste uyduğu gibi bir ajana da uyan ayıklama kuralları var.
timed_out döndüren altmış saniyelik bir bekleme bir başarısızlık değildir; "henüz değil" demektir. Talimatlar şunu söylemelidir: en fazla üç kez yeniden çağır. Üç çağrı üç dakikayı aşar, ki bu gerçekten gönderilmiş herhangi bir işlemsel postayı kapsar — ve ajana, formun bir hata gösterdiğini ya da takma adı yanlış yazdığını fark etmesi için üç şans verir.
Takma ad kuralı
Her posta kutusunun iki adresi vardır. Adres, ajanın okumak için kullandığı adrestir; ona sahip olan herkes posta kutusunu açabilir, çünkü hesap yoktur ve adres tek anahtardır. Takma ad ise ayrı bir alan adında, aynı posta kutusuna teslimat yapan ama onu okumak için kullanılamayan ikinci bir adrestir.
Yani site takma adı alır, ajan ise adresi elinde tutar. Adresi bir forma yapıştıran bir ajan, siteye — ve sitenin onu sızdırdığı herkese — ajanın orada bundan sonra alacağı her mesajı okuma yetisini vermiş olur. Yukarıdaki araç fonksiyonları ikisini de, hangisinin nereye gideceğini söyleyen bir next_step ile döndürür ve talimatlar bunu tekrarlar, çünkü iki kez söylenen bir kural, uyulan bir kuraldır.
Gözetimsiz bir ajan için korkuluklar
Bir test paketi başarısız olur ve durur. Bir durumu yanlış okuyan bir ajan ise devam eder, ve harcamaya devam eder. Beş sınır, bir e-posta adımının bir görevin pahalı kısmına dönüşmesini engeller:
- Görev başına bir gelen kutusu
- Bir adresi görevler ya da çalıştırmalar arasında asla yeniden kullanmayın. İçinde inandırıcı bir kod bulunan eski bir mesaj, bir ajanın kendinden emin biçimde yanlış şeyi yapmasının en hızlı yoludur.
create_inbox'ın hiçbir bedeli yoktur; her seferinde çağırın. - Bir bekleme bütçesi
wait_for_message'a üç çağrı, ardından durup rapor verin. Hiç gönderilmemiş bir postayı süresiz bekleyen bir ajan, bir worker yuvasını ve bir faturayı tüketir.- Adımın tamamı için bir son tarih
- Gönderimden koda kadar uçtan uca beş dakika. Bunu aştığında doğru eylem, formu yeniden denemek değil, bir insana ne olduğunu söylemektir.
- Konu filtreleme
- Her zaman
subject_containsgeçirin. Kod postasından önce ulaşan bir hoş geldin postası, aksi halde "o mesaj" olur ve ajan bir pazarlama altbilgisinden altı hane ayıklamaya çalışır. - Adresi günlüğe kaydetme
- Adresi görev günlüğüne yazın. Mesajlar 5 gün kalır, böylece bir insan daha sonra posta kutusunu açıp ajanın tam olarak ne gördüğünü görebilir — bir çalıştırma ters gittiğinde en yararlı tek şey budur.
Tarayıcı ajanları
Gerçek bir tarayıcıyı süren bir ajan — Browser Use, bir Playwright MCP sunucusu, bir computer-use modeli — e-posta adımının en sert ısırdığı durumdur, çünkü formla kimsenin planladığından önce karşılaşacaktır. Üç şey bunu çalışır kılar:
- Ona her iki sunucuyu da verin. Tarayıcı araçları ve gelen kutusu araçları aynı oturumda, böylece "e-postanızı kontrol edin" bir çıkmaz değil bir araç çağrısı olur.
- Döngüyü sistem promptuna koyun. Dört satır: ilk e-posta alanında bir gelen kutusu oluşturun; takma adı forma girin; gönderimin hemen ardından adres üzerinde bekleyin;
timed_out'ta üç yeniden deneme. - Reddedilmeyi bekleyin. Takma adın alan adını reddeden bir form, bunu sayfada söyler; ajan hatayı okuyup durmalı, isim denemeye devam etmemelidir. Bir reddi aşmanın dürüst yolları — kendi alan adınız ya da kara listelerin dışında tutulan havuzdan bir alan adı — ajanın değil, sizin verdiğiniz yapılandırma kararlarıdır.
Ajanın kendi okuması için site, bu sayfanın söylediklerini bir modelin tercih ettiği biçimde söyleyen düz metin bir harita olan llms.txt'i ve kod yazan bir ajanın istemciyi üzerine inşa edebileceği bir OpenAPI belgesini yayımlar.
Gözetimsiz çalıştırmasına izin vermeden önce
- Takma ad kuralını ve
timed_out'un ne anlama geldiğini belirten araç açıklamaları. - Dört adımlı döngüyü ve üç beklemelik bir bütçeyi içeren talimatlar.
- Görev başına bir kez çağrılan, asla yeniden kullanılmayan
create_inbox. - Her beklemede
subject_contains. - Görev günlüğüne yazılan adres.
- Gelen kutusuna gizli hiçbir şeyin gönderilmesini yasaklayan bir satır.
Bir ajanın ihtiyaç duyduğu her şey bu kadar. Aynı gelen kutusu gündüz bir test paketine, gece bir ajana hizmet eder, çünkü altında yatan aynı üç HTTP çağrısıdır — ve ajanın kaydolduğu site genel alan adlarını reddederse, kendi alan adınız ya da listelerin dışında tutulan havuz, araçlarda hiçbir değişiklik yapmadan devreye girer.
Sorular
Ajan için bir API anahtarına ihtiyacım var mı?
Hayır. Genel alan adları, REST ve MCP üzerinden aynı şekilde, ne anahtar ne hesap ne de başlık ister. Yalnızca tek kullanımlık posta kara listelerinin dışında tutulan ücretli alan adı havuzu bir bearer token kullanır, ve araç fonksiyonları bunun dışında onun için de aynıdır.
MCP mi yoksa bir REST aracı mı — hangisini seçmeliyim?
Ajan zaten bir MCP istemcisinde yaşıyorsa, MCP: tek bir satırdır ve bekleme sunucu tarafındadır. Ajanı bir framework içinde yazıyorsanız, bir araç fonksiyonu: kırk satırdır, herhangi bir modelle çalışır ve modelin okuduğu açıklamayı siz denetlersiniz. İkisi de aynı posta kutusuna ulaşır.
Bekleme, token cinsinden ne kadara mal olur?
Her iki durumda da hiçbir şeye. MCP beklemesi sunucuda bloklar; REST aracı sizin kodunuzda döngüye girer. Model, token'ları araç çağrısına ve sonucu okumaya harcar, aradaki altmış saniyeye değil — bir modelin bir posta kutusunu kendi başına yoklamasına izin vermemenin bütün nedeni de budur.
Birden fazla ajan aynı anda çalışabilir mi?
Evet. Her görev kendi gelen kutusunu alır ve bir oturum durumu yoktur. Sınırlar, REST üzerinden adres başına saniyede bir okuma ve istemci başına dakikada 1200 istek, MCP üzerinden ise 8 eşzamanlı wait_for_message çağrısıdır — bunun ötesinde araç anında timed_out yanıtı verir ve ajan yeniden çağırır.
Ajan, gelen kutusundan e-posta gönderebilir mi?
Hayır. Hizmet, tasarım gereği yalnızca alır — gönderim yapabilen, hesapsız ücretsiz bir gelen kutusu bir saat içinde bir spam aktarıcısına dönüşürdü. Posta göndermesi gereken bir ajanın bir gönderim sağlayıcısına ve kendi kimlik bilgilerine ihtiyacı vardır.
Site, takma adın alan adını reddederse ne olur?
O zaman bir tek kullanımlık alan adı kara listesindedir ve @ işaretinin önündeki hiçbir isim bunu değiştirmez. Sahibi olduğunuz bir alan adını hizmete yönlendirin (tek bir MX kaydı, ücretsiz) ya da listelerin dışında tutulan ücretli havuzdan bir alan adı kullanın — ikisi de alan adı sabitini değiştirerek aynı araçlara takılır. Kayıt formları neden tek kullanımlık e-postayı engeller rehberi, hangi kontrolün sizi reddettiğini anlatır.
Gelen kutusu ajanıma özel mi?
Hayır. Adresi bilen herkes onu okuyabilir; takma adın var olma nedeni de budur ve oraya asla gizli hiçbir şey gönderilmemesi gerekmesinin nedeni de budur. On dakika yaşayan bir kod için bu sorun değildir; bu, ajanın talimatlarının açıkça belirtmesi gereken tek kuraldır.


