Your agent is moving fast. It navigates to the registration page, fills in the name field, generates a strong password, clicks "Create account" — and then the response comes back: Check your email to verify your address. Dead stop.
There's no API endpoint to poll. No webhook you can register. The verification code is sitting in an inbox that only a human with a browser can reach. So the agent loop breaks, you paste in an OTP manually, and wonder why you're babysitting a process you spent two days automating.
This is the wall that kills most autonomous registration flows. It's not a reasoning failure — Claude Code made all the right decisions. The problem is that standard email inboxes don't have an API. They weren't built for agents.
The UnCorreoTemporal MCP server solves exactly this. It gives Claude Code a set of tools to create disposable inboxes on demand, wait for incoming email, and extract OTPs or verification links — all within the same agent loop, no human in the middle. This guide walks through the full setup.
What You'll Build
By the end of this guide, Claude Code will be able to run this flow completely autonomously:
Claude Code agent loop
└─► create_signup_inbox() ──► generates mx_abc123@mail.uncorreotemporal.com
└─► register at app.example.com using that address
└─► wait_for_verification_email() ──► blocks until email arrives (up to 90s)
└─► extract_otp_code() ──► pulls "847291" from the email body
└─► submit OTP ──► registration complete ✓
Claude Code acts as the autonomous agent. The UnCorreoTemporal MCP server provides the inbox tooling — create_signup_inbox, wait_for_verification_email, extract_otp_code, extract_verification_link, and a convenience wrapper called complete_signup_flow that chains all of them in a single call.
The target service is whatever you're actually automating. In this guide I'll use app.example.com as a generic placeholder, but the same pattern applies to any SaaS trial, developer portal, or API sign-up that gates access behind an email confirmation.
Prerequisites
-
Claude Code installed:
npm install -g @anthropic-ai/claude-code -
uv installed (for running the MCP server via
uvx):curl -LsSf https://astral.sh/uv/install.sh | sh - An UnCorreoTemporal API key — free tier at uncorreotemporal.com, 100 inboxes/month, no credit card
- Python 3.10+ — optional, only needed for the standalone script variant in the extraction section
Step 1 — Configure the MCP Server in claude_desktop_config.json
This is the step most guides skip over. If the MCP server isn't wired up correctly, Claude Code has no email tools and the whole thing is dead before it starts.
The quick way: claude mcp add
Claude Code ships with a CLI command for registering MCP servers:
claude mcp add uncorreotemporal \
-e UCT_API_KEY=uct_your_key_here \
-- uvx uncorreotemporal-mcp
That's it. Claude Code writes the config for you. If you prefer to edit the JSON directly, the equivalent block in ~/.claude/claude_desktop_config.json looks like this:
{
"mcpServers": {
"uncorreotemporal": {
"command": "uvx",
"args": ["uncorreotemporal-mcp"],
"env": {
"UCT_API_KEY": "uct_your_key_here"
}
}
}
}
What this does
The mcpServers block tells Claude Code which external tool servers to launch when it starts. Each entry specifies a command to run — in this case uvx uncorreotemporal-mcp, which downloads and runs the server from PyPI using uv's isolated environment runner — plus any environment variables the server needs.
The API key goes in env, not hardcoded in args. This keeps it out of shell history, out of version control, and makes it easy to rotate without touching the config structure.
Verify it loaded
Start a new Claude Code session and type /tools. You should see a list that includes:
create_signup_inbox
wait_for_verification_email
get_latest_email
extract_otp_code
extract_verification_link
complete_signup_flow
If the tools don't show up, check that uvx is on your PATH (which uvx). If it's not, uv isn't installed or isn't in your shell's PATH — run source $HOME/.local/bin/env or restart your terminal after installing uv.
The full MCP tool reference is at uncorreotemporal.com/en/docs/mcp/.
Step 2 — Create a Disposable Inbox
Once Claude Code has the tools loaded, creating an inbox is a single tool call. Here's what Claude Code actually sends and receives:
Tool: create_signup_inbox
Input: {
"service_name": "app.example.com"
}
Output: {
"inbox_id": "mx_abc123@mail.uncorreotemporal.com",
"email": "mx_abc123@mail.uncorreotemporal.com",
"expires_at": "2026-05-06T10:04:00Z",
"service_name": "app.example.com"
}
A few things worth noting:
-
service_nameis required. It's a label for your own reference — it doesn't affect routing, but it helps when you're running multiple inboxes and debugging which one belongs to which flow. -
inbox_idis the email address. There's no separate opaque ID — the email itself is the identifier you pass to subsequent tool calls. - The inbox is live immediately. No DNS propagation, no warming period. Mail sent to that address is queued within seconds of creation.
-
Default TTL is 30 minutes, configurable via
ttl_minutes. For most signup flows, 30 minutes is more than enough — the whole loop should complete in under 60 seconds.
Claude Code stores the returned email address in its context and uses it for the next step.
Step 3 — Register on the Target Service
Now Claude Code takes that address and uses it to sign up. The prompt you give it controls how it does this — browser automation, API call, form fill, whatever fits the target service. For a typical SaaS registration flow:
Register a free account on app.example.com using the email address from the
inbox you just created. Fill in all required fields with realistic placeholder
data. Use the name "Alex Developer" and generate a strong password. Once
you submit the form, stop and wait for my next instruction.
Claude Code will substitute the generated email address into the registration form. Everything else in the form — name, password, company, timezone — it can fill with plausible placeholder data. The email is the only field that must match the inbox.
This step doesn't require any additional MCP tool calls. Claude Code handles the form interaction through its existing browser automation or API capabilities. The MCP tools come back into play for the next step.
One thing I've learned from running these flows: don't ask Claude Code to wait for the email in the same prompt as the registration. Keep registration and email-waiting as separate steps. It's easier to debug when one fails, and it avoids the agent making assumptions about delivery timing.
Step 4 — Wait for the Verification Email
This is where the flow either works end-to-end or breaks down in a standard setup. With the MCP server, it's a single blocking tool call:
Tool: wait_for_verification_email
Input: {
"inbox_id": "mx_abc123@mail.uncorreotemporal.com",
"timeout_seconds": 90
}
Output: {
"status": "received",
"message_id": "msg_9x2p1q",
"subject": "Verify your account",
"from_address": "noreply@app.example.com",
"received_at": "2026-05-06T10:01:34Z",
"timeout_seconds": 90
}
The tool polls the inbox every 3 seconds until a message arrives or the timeout is reached. From Claude Code's perspective this is opaque — it makes one tool call and gets back a result. No retry loop, no repeated tool invocations burning your context budget.
If the timeout fires before anything arrives, you get:
{
"status": "timeout",
"timeout_seconds": 90
}
I've found 90 seconds covers the vast majority of transactional email delivery in practice. For services with aggressive rate limiting or slow SMTP relay, you can push it to 120 or 180. For CI pipelines where speed matters more, 60 seconds is usually sufficient.
The response includes message_id and from_address but not the email body — that's intentional. Body retrieval is a separate concern handled by the extraction tools in the next step, which can work from message_id directly without an additional get_latest_email call.
You can also filter by sender or subject if you're dealing with services that send multiple emails on signup:
Tool: wait_for_verification_email
Input: {
"inbox_id": "mx_abc123@mail.uncorreotemporal.com",
"timeout_seconds": 90,
"subject_contains": "verify",
"from_contains": "noreply@app.example.com"
}
Step 5 — Extract the OTP with Regex
Once wait_for_verification_email returns "status": "received", you have a message_id. Pass it directly to extract_otp_code:
Tool: extract_otp_code
Input: {
"message_id": "msg_9x2p1q",
"inbox_id": "mx_abc123@mail.uncorreotemporal.com"
}
Output: {
"otp_code": "847291",
"candidates": ["847291"]
}
The tool fetches the message body internally, runs a regex sweep for numeric sequences in the otp_length_min–otp_length_max range (default 4–8 digits), and returns the most likely match as otp_code. If there are multiple numeric sequences, they all appear in candidates — useful when the email body contains things like reference numbers or ZIP codes that could false-positive.
You can narrow the range explicitly:
Tool: extract_otp_code
Input: {
"message_id": "msg_9x2p1q",
"inbox_id": "mx_abc123@mail.uncorreotemporal.com",
"otp_length_min": 6,
"otp_length_max": 6
}
Doing it yourself in Python
If you're building a script rather than using Claude Code interactively, here's the extraction logic without any SDK dependency:
import re
def extract_otp(text: str, digits: int = 6) -> str | None:
pattern = rf'\b\d{{{digits}}}\b'
matches = re.findall(pattern, text)
return matches[0] if matches else None
# Usage
otp = extract_otp(email_body_text)
# -> "847291"
Common OTP patterns you'll encounter in the wild:
-
6-digit numeric (most common, e.g. Twilio, Auth0, most SaaS):
\b\d{6}\b -
8-digit numeric (banking, government):
\b\d{8}\b -
Alphanumeric codes (e.g.
B4K9X2TZ):\b[A-Z0-9]{8}\b -
Magic links: handled separately by
extract_verification_link(see the next section)
Step 6 — Complete the Registration
You have the OTP. Now Claude Code submits it. The prompt is straightforward:
The OTP extracted from the verification email is 847291. Submit it in the
verification form on app.example.com. Once submitted, confirm that the
account dashboard is visible and report whether registration succeeded.
Claude Code navigates back to the verification form, enters the code, and submits. If the service uses a magic link instead of a code, the flow is identical — you just pass the URL from extract_verification_link instead of a 6-digit code.
At this point the agent loop is complete. No human touched it.
Putting It All Together — A Complete Agent Loop Prompt
Here's the prompt I use for end-to-end autonomous registration. Copy it, swap app.example.com for your actual target, and adjust the password and name as needed.
You are an autonomous registration agent. Complete the following workflow
in order, stopping if any step fails:
1. Call create_signup_inbox with service_name "app.example.com" and store
the returned email address.
2. Register a free account on app.example.com using:
- Email: the address from step 1
- Name: Alex Developer
- Password: TempP@ss2026!
Fill all required fields. Submit the registration form.
3. Call wait_for_verification_email with the inbox_id from step 1 and
timeout_seconds 90. Wait for status "received".
4. Call extract_otp_code with the message_id and inbox_id from the
previous steps.
5. Submit the extracted OTP code in the verification form on app.example.com.
6. Confirm the account dashboard is accessible, then report:
- Email address used
- OTP extracted
- Whether registration succeeded (yes/no)
- Any unexpected behavior during the flow
If any step returns an error or unexpected result, report exactly what
happened and stop. Do not retry automatically.
Why the prompt is structured this way
Numbered steps enforce sequence. Claude Code can parallelize tool calls when it reasons that two actions are independent. An explicit numbered list signals that order matters and each step depends on the previous one.
Explicit tool names in the prompt. Saying "call create_signup_inbox" rather than "create an inbox" removes ambiguity about which MCP tool to use. It also makes the agent's reasoning trace easier to follow when you're debugging.
"Do not retry automatically." This is the most important line. When a step fails — email never arrives, OTP is wrong, form submission errors — you want the agent to stop and report, not loop into a retry spiral that burns API quota and potentially triggers rate limits on the target service.
Structured report at the end. The bulleted output at step 6 gives you something machine-parseable if you're chaining this into a larger pipeline.
Handling Failure Cases
Real-world flows break in predictable ways. Here's how to handle them.
Email never arrives
wait_for_verification_email returns {"status": "timeout"} after the configured seconds. Common causes:
- The service's SMTP relay is slow (some providers queue outbound email for 30–60 seconds under load)
- The email was delivered but classified as spam and filtered before it reached the inbox
- The registration form rejected the address silently and never sent the email
Mitigation: increase timeout_seconds to 120 or 180 for slow senders. If you're seeing consistent timeouts, call get_latest_email after the timeout to check whether anything arrived at all — sometimes the email arrives just after the timeout window closes.
OTP already expired by the time you submit it
Most OTPs expire in 10 minutes. The entire flow — create inbox, register, wait for email, extract OTP, submit — should complete in well under 60 seconds in practice. If your agent is slow (large context, slow browser automation), request a new OTP from the service before submitting.
Service blocks the email domain
Some services — Google, Apple, Stripe, most banking platforms — maintain blocklists of known disposable email providers and reject addresses from those domains at signup. If you hit this, you'll know immediately: the registration form will return an error like "Please use a valid email address."
The UCT dedicated plans include support for custom domains — you can receive email at agent@yourdomain.com instead of the default @mail.uncorreotemporal.com. That bypasses most domain-level blocklists.
Beyond OTPs — Magic Links and Multi-Step Flows
Not every service sends a 6-digit code. Plenty of them send a magic link — a one-time URL that logs you in or confirms your email when you visit it. The MCP server handles this with extract_verification_link:
Tool: extract_verification_link
Input: {
"message_id": "msg_9x2p1q",
"inbox_id": "mx_abc123@mail.uncorreotemporal.com",
"preferred_domains": ["app.example.com"]
}
Output: {
"verification_link": "https://app.example.com/verify?token=eyJhbGci...",
"candidates": [
"https://app.example.com/verify?token=eyJhbGci...",
"https://app.example.com/unsubscribe?email=..."
]
}
preferred_domains nudges the extraction toward links from the service's own domain, filtering out unsubscribe links, tracking pixels, and other noise that appears in most HTML email.
Multi-step flows
The same tool chain composes naturally for flows that span multiple emails. A common pattern in developer-facing SaaS:
- Register -> receive email confirmation link -> click it -> account activated
- Account activated -> receive welcome email with API key -> extract API key
You don't need to restart the agent or re-create the inbox between steps. The inbox stays live for its full TTL, and wait_for_verification_email can be called multiple times against the same inbox_id. Chain two wait_for_verification_email + extract_* call pairs and you have the whole flow in one agent session.
The complete_signup_flow shortcut
If your flow is straightforward — register, wait for one verification email, extract the link or OTP — there's a single tool that wraps all of it:
Tool: complete_signup_flow
Input: {
"service_name": "app.example.com",
"timeout_seconds": 90
}
Output: {
"inbox_id": "mx_abc123@mail.uncorreotemporal.com",
"email": "mx_abc123@mail.uncorreotemporal.com",
"otp_code": "847291",
"verification_link": "https://app.example.com/verify?token=...",
"message_id": "msg_9x2p1q",
"status": "received"
}
complete_signup_flow creates the inbox, waits for the email, and runs both OTP and link extraction in sequence. You get everything back in one response. For simple flows this is the cleanest approach; for multi-step or filtered flows, calling the individual tools gives you more control.
Try It Yourself
The fastest path from zero to a working agent loop is:
- Sign up at uncorreotemporal.com — no credit card, API key in the dashboard immediately
- Run
claude mcp add uncorreotemporal -e UCT_API_KEY=uct_your_key_here -- uvx uncorreotemporal-mcp - Open a Claude Code session, type
/tools, confirm the six tools are listed - Paste the agent loop prompt from the "Putting It All Together" section, point it at a real service, and watch it run
The free tier gives you 100 inboxes per month, which is enough to wire up and test a full registration flow. If you're running this in CI — automated integration tests, synthetic monitoring, agent-driven QA — the full MCP tool reference covers filtering options, TTL configuration, and the get_latest_email tool for cases where you need the raw message body.
The email verification wall is a solved problem. Your agent loop doesn't have to stop there.
Top comments (0)