Vercel AI SDK
An email inbox for your AI SDK agent.
npm install @pidgeon-ai/ai-sdk, and generateText gets every Pidgeon tool: an address of its own, the code from the next email, and a person’s approval before it sends. It runs with a real model against our production after every deploy.
import { openai } from '@ai-sdk/openai';
import {
WRITE_TOOLS,
pidgeonTools,
} from '@pidgeon-ai/ai-sdk';
import { generateText, isStepCount } from 'ai';
// Anything that sends, replies, creates or hands
// over waits for a person.
const approval = Object.fromEntries(
WRITE_TOOLS.map((name) => [
name,
'user-approval' as const,
]),
);
const settings = {
model: openai('gpt-5.4-nano'),
tools: pidgeonTools({
apiKey: process.env.PIDGEON_API_KEY,
identityId,
}),
toolApproval: approval,
stopWhen: isStepCount(5),
};
const first = await generateText({
...settings,
messages,
});What it does
Every tool, and a stop before the send.
One function turns the hosted MCP server’s tools into AI SDK tools.
- All 28 tools
pidgeonTools() turns every tool on the hosted MCP server into an AI SDK tool, with the same names and descriptions. await_message waits up to 280 seconds and returns the next email with its codes already pulled out.
- Approval before anything is sent
WRITE_TOOLS names the ten tools that change something. Hand them to toolApproval and the call stops at the send, as a request a person answers.
- The key is the boundary
Nothing in the package decides what may be reached: the key does, on the server. Give the agent a key scoped to its own address, and it cannot read or send as anyone else.
Approve the send
Two calls, and a yes between them.
The first call reads the code and stops at the reply. A person approves, and the second call sends it.
The whole program, with an address, a code and a checked reply, is in the AI SDK guide.
// The reply stopped as a request for approval;
// nothing has been sent.
const request = first.content.find(
(part) => part.type === 'tool-approval-request',
);
messages.push(
...first.responseMessages,
{
role: 'tool',
content: [
{
type: 'tool-approval-response',
approvalId: request.approvalId,
approved: true,
},
],
},
);
// A person said yes: the second call sends it.
const second = await generateText({
...settings,
messages,
});Questions
Asked before adding it.
Which tools does @pidgeon-ai/ai-sdk give the model?
All 28 of the hosted MCP server’s tools, read from the same list, so the model sees the same names and descriptions whichever way it reaches Pidgeon. Ten of them change something, and WRITE_TOOLS names those.
Which model and provider does it need?
Any the AI SDK supports. The program we run after every deploy uses @ai-sdk/openai; a model string through the Vercel AI Gateway needs AI_GATEWAY_API_KEY set.
Where is the result of the approved call?
In the second generateText call’s responseMessages, as a tool message. The approved call runs before that call’s first step, so it is not in steps.
Does new email wake the agent?
No. The agent has to be running and call await_message, which waits up to 280 seconds for the next message. To start work when mail arrives, use a Pidgeon webhook.
Is it on the free plan?
Yes. The package is free and MIT-licensed, and the free plan includes three addresses and your own domain.
Give your agent an address.
Paste one prompt, or make the call yourself. It is free to start.
Read https://pidgeon.ai/skill.md and follow the instructions to get your own inbox.
The full API reference is at https://pidgeon.ai/openapi.json