Mails.ai with the Vercel AI SDK — tool definitions for serverless agents
sdk.vercel.aiInstall with `npm install ai @ai-sdk/anthropic @mailsai/sdk zod`. Then wrap mails.ai as tool() definitions:
import { generateText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { mails } from "@mailsai/sdk";
const client = mails({ apiKey: process.env.MAILS_API_KEY! });
const sarah = client.agent("sarah");
const sendEmail = tool({
description: "Send an email from the sarah agent.",
parameters: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
execute: async ({ to, subject, body }) => {
const result = await sarah.send({ to, subject, body });
return { send_id: result.id, pool: result.pool };
},
});
const listThreads = tool({
description: "List recent inbound threads on the sarah agent.",
parameters: z.object({
since_days: z.number().default(1),
}),
execute: async ({ since_days }) => {
return await sarah.listThreads({ sinceDays: since_days });
},
});
const result = await generateText({
model: anthropic("claude-sonnet-4-6"),
tools: { sendEmail, listThreads },
prompt: "Email lead@example.com a follow-up about Tuesday's demo.",
});Vercel AI SDK is the dominant TypeScript SDK for AI features in modern web apps — Next.js, SvelteKit, Nuxt, Remix all use it as the standard AI integration layer. Provider-agnostic (Anthropic, OpenAI, Google, Mistral, all behind one API). Wire mails.ai as tool() definitions and your serverless agent can send + read email as part of generateText, streamText, or experimental_streamUI flows.
Why Vercel AI SDK + mails.ai
The combination is opinionated for the most common modern web-app shape: Next.js + Vercel + Anthropic + serverless. mails.ai bolts onto that stack as a tool layer:
- In-app AI chat that can email. User chats with your AI assistant in your Next.js app; the assistant offers to email a summary or follow-up; the SDK fires mails.send via the tool definition.
- Server-Action-driven email automations.User clicks “email our sales team about my use case” in your form; a Server Action invokes generateText with the mails tool, drafts the email, sends it, returns confirmation.
- Stream UI for live email composition. Use streamText with tool calls to show the agent drafting and sending in real-time, with the UI updating as tool results stream back.
- Edge-runtime agents. The mails.ai SDK is fetch-based and edge- compatible. Latency-sensitive flows can run in edge runtime without trade-offs.
Setup
- Install dependencies.
npm install ai @ai-sdk/anthropic @mailsai/sdk zod - Set environment variables.
MAILS_API_KEYfor mails,ANTHROPIC_API_KEYfor the model. Vercel project settings handle both. - Define tool() wrappers. The pattern in the install card. Wrap mails.send, list_threads, and identity as needed by your use case.
- Bind to generateText / streamText. Pass the tools object as the
toolsparameter. The model picks tools by name during the conversation.
Common patterns
Server Action with tool-driven email:
// app/actions/contact.ts
"use server";
import { generateText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { mails } from "@mailsai/sdk";
const sarah = mails({ apiKey: process.env.MAILS_API_KEY! }).agent("sarah");
const sendEmail = tool({
description: "Send an email from sarah.",
parameters: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
execute: async ({ to, subject, body }) => {
const r = await sarah.send({ to, subject, body });
return { send_id: r.id };
},
});
export async function emailSalesTeam(userMessage: string) {
const { text } = await generateText({
model: anthropic("claude-sonnet-4-6"),
tools: { sendEmail },
prompt: `User said: "${userMessage}". Email sales@ourcompany.com with a brief summary asking them to follow up.`,
});
return text;
}Streaming chat with email tool:
// app/api/chat/route.ts
import { streamText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { mails } from "@mailsai/sdk";
const sarah = mails({ apiKey: process.env.MAILS_API_KEY! }).agent("sarah");
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: anthropic("claude-sonnet-4-6"),
messages,
tools: {
sendEmail: tool({
description: "Send an email from sarah",
parameters: /* ... */,
execute: async (args) => sarah.send(args),
}),
},
});
return result.toDataStreamResponse();
}Inbound webhook handling
For inbound replies, set up a Next.js Route Handler that receives the mails.ai webhook and processes the typed event:
// app/api/webhooks/mails-inbound/route.ts
import { NextRequest, NextResponse } from "next/server";
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export async function POST(req: NextRequest) {
const event = await req.json();
// Refuse high-injection-score events at the door
if (event.injection_score > 0.5) {
return NextResponse.json({ ok: true, action: "refused_injection" });
}
// Use AI SDK to draft a response based on intent
if (event.intent === "schedule_demo") {
const { text } = await generateText({
model: anthropic("claude-sonnet-4-6"),
prompt: `Draft a calendar-confirmation reply for: ${event.body}`,
});
// Send it via mails.ai
// ...
}
return NextResponse.json({ ok: true });
}Security considerations
- Server-side only. Tool executions run server-side (generateText/streamText in Server Actions or Route Handlers). The mails.ai key never reaches the browser.
- Verify webhook signatures. Mails.ai webhooks are signed; verify the signature in your Route Handler before processing the event. Reject mismatches.
- Per-route mails.ai key. If you have multiple agents (different brand voices, different use cases), use Vercel project environment variables to scope keys per-route.
Compare against the Anthropic SDK setup for a more direct integration when not on Vercel AI SDK, or OpenAI Agents SDK for the framework-driven approach.
Questions developers ask after wiring this up.
Why no MCP-client integration like the Anthropic SDK has?
Vercel AI SDK is provider-agnostic — it normalizes Anthropic, OpenAI, Google, Mistral, etc., behind a single API. MCP-client support is on their roadmap but not yet first-class. The tool() helper pattern is the SDK's idiomatic way to add tools today and works identically with any model provider. When MCP-client lands in the SDK, swap to that for consistency with other runtimes.
Does this work with streamText / experimental_streamUI?
Yes. Tools fire during streaming the same way they fire in generateText. For streamUI, the tool result returns a React component spec the SDK can render — useful for showing 'Email sent: ID xyz' confirmations in the UI as the agent works.
Edge runtime or Node runtime — does it matter?
Both work. The @mailsai/sdk client is HTTP-based (just fetch under the hood) and edge-compatible. Edge wins for latency on simple send-and-confirm flows; Node wins if you have other Node-only dependencies (e.g., the React Email rendering pipeline). Pick based on your wider stack.
Can I use this with Server Actions in Next.js App Router?
Yes. Define the tools in a 'use server' file and call generateText from a Server Action invoked by your form/button. The mails.* tool calls happen server-side; the Action returns the agent's final response to the client. Common pattern for chat interfaces where the user types a request and the agent emails on their behalf.
What to read next.
Built for agents.
Self-serve at every volume.
Public API opens Q3 2026. Drop ~6 lines into your agent and ship.
$ npm install @mailsai/sdk