MailFlatDocs
Documentation/AI agents & assistants/Vercel AI SDK

Using MailFlat with the Vercel AI SDK

mailflatToolSuite() spreads ready-made inbox tools into any Vercel AI SDK call, so the model can create an address, wait for the code and finish a signup on its own.

How it fits together

The suite is a dictionary of ready-made tools — you spread it into the tools option and the model does the rest. It speaks both the older parameters shape and the newer inputSchema one, so it works with the AI SDK version you already have rather than forcing an upgrade.

The tools

Named in camelCase here — createInbox, waitForOtp, deleteMessage — because they are TypeScript object keys you spread and destructure.
ToolArgumentsWhat it does
create_inboxprefix? · label? · retention_hours?Opens a real inbox and returns its address.
list_inboxesEvery inbox this API key can see.
read_messagesaddressAll messages in an inbox, newest first.
wait_for_otpaddress · timeout?Polls until a one-time code arrives, then returns it.
send_emailaddress · to · subject? · body? · html?Sends a DKIM-signed email from the inbox.
delete_inboxaddressDeletes the inbox and every message in it.
delete_messageaddress · message_idDeletes one message; the inbox itself stays.

Install

The AI SDK itself is a peer dependency, so install it too if this is a fresh project.
Shell
npm i @mailflat/ai-sdk ai @ai-sdk/anthropic

One call, every tool

Everything after the model line is ordinary AI SDK code — the suite adds tools and nothing else.
TypeScript
import { mailflatToolSuite } from "@mailflat/ai-sdk";
import { anthropic } from "@ai-sdk/anthropic";
import { generateText } from "ai";
 
const result = await generateText({
model: anthropic("claude-opus-5"),
tools: {
...mailflatToolSuite({ apiKey: process.env.MAILFLAT_API_KEY }),
},
maxSteps: 10,
prompt:
"Open an inbox, sign up on staging.example.com with it, " +
"read the one-time code from the mail and submit it.",
});
 
console.log(result.text);
maxSteps is what lets the model call a tool, read the result and call another. AI SDK 5 renamed it — there you write stopWhen: stepCountIs(10) instead.

Only the tools this agent should have

It is a plain object, so pick from it like one. A support bot that reads mail has no business deleting inboxes.
TypeScript
const { createInbox, waitForOtp, readMessages } = mailflatToolSuite({
apiKey: process.env.MAILFLAT_API_KEY,
});
 
const result = await generateText({
model: anthropic("claude-opus-5"),
tools: { createInbox, waitForOtp, readMessages },
maxSteps: 8,
prompt: "Open an inbox and tell me the first code that arrives.",
});

Share one client with the rest of your app

Pass a configured @mailflat/sdk instance and both paths use the same base URL, retries and key. Also the seam a test fills with a fake.
TypeScript
import { MailFlat } from "@mailflat/sdk";
import { mailflatToolSuite } from "@mailflat/ai-sdk";
 
const mf = new MailFlat({
apiKey: process.env.MAILFLAT_API_KEY!,
baseUrl: "https://mail.example.com",
});
 
const tools = mailflatToolSuite({ client: mf });
 
// your own code and the model now share one client
const inbox = await mf.create({ label: "checkout", retentionHours: 2 });

Watch what it actually did

Tool results come back as data, so a run that looks fine can hide a timeout. Read the steps rather than trusting the final sentence.
TypeScript
for (const step of result.steps) {
for (const call of step.toolResults ?? []) {
console.log(call.toolName, call.result);
// e.g. waitForOtp { otp: null, error: "timeout" }
}
}

Worth knowing

Tool errors arrive as { error: "..." } instead of throwing, so the model can recover — but nothing surfaces them for you. Log tool results, or a timed-out waitForOtp reads as a successful run that simply found no code.
The key belongs on the server. Spreading this suite into a call that runs in the browser ships an account key to every visitor.
waitForOtp takes milliseconds and holds the step open while it polls; an inbox with end-to-end encryption returns { otp: null, encrypted: true } because the server cannot read it.