Build a WhatsApp agent with the Vercel AI SDK
Incoming messages reach a route handler set as the number's webhook destination. The handler calls generateText and sends the answer through the Cloud API. For tools that act on the number, createMCPClient from @ai-sdk/mcp connects to the EasyCoexistence MCP server over HTTP, signing in with OAuth through an authProvider.
The package that provides createMCPClient, the AI SDK's MCP client.
OAuth is supported on the AI SDK's MCP HTTP transport by providing an authProvider.
Fixed throughput for a number used with both the WhatsApp Business app and the Cloud API.
How does the route handler receive WhatsApp messages?
With two methods on one route, set as the number's webhook destination with a long random secret in the path.
The GET answers Meta's verification. Read hub.mode, hub.verify_token and hub.challenge from the query, compare the token, and return the challenge as plain text. Returning it through a JSON helper is the classic mistake: the body arrives with quotes around it and Meta never starts delivering.
The POST receives events. Return 200 quickly and do the model call afterwards, because Meta retries slow responses and a cold start that runs the model inline can answer the same message twice. On platforms that end the function when the response is sent, hand the work to a queue or to the platform's mechanism for work after the response.
The path secret is the authentication. Meta signs each delivery with the Tech Provider's app secret, so the X-Hub-Signature-256 header cannot be checked on your side.
How does the AI SDK load the WhatsApp tools?
With createMCPClient from @ai-sdk/mcp and the HTTP transport, which the AI SDK documentation recommends for production. The transport takes the server URL and an authProvider, and OAuth through an authProvider is how the EasyCoexistence MCP server is authorised: it publishes OAuth metadata, supports dynamic client registration and PKCE, and issues its own tokens after a sign-in. There is no API key to put in a header.
The client's tools go straight into generateText or streamText. The model then sees list_numbers, send_message, send_template, get_number_health and the others, with descriptions that state the 24 hour window. Close the client when the call is done, in the onEnd callback for a stream or in a finally block otherwise, as the AI SDK documentation asks.
Use it where the model should decide. For replying to the message that just arrived, a direct Cloud API call from the handler is shorter.
import { createMCPClient } from "@ai-sdk/mcp";
const mcp = await createMCPClient({
transport: {
type: "http",
url: "https://easycoexistence.com/api/mcp",
authProvider, // your OAuthClientProvider implementation
},
});
try {
const tools = await mcp.tools();
// pass tools to generateText({ model, tools, prompt })
} finally {
await mcp.close();
}What changes because the number is on Coexistence?
Two things the code should know about. First, a human may already be in the conversation. The WhatsApp Business app keeps working on the number, and every reply typed on the phone arrives at the route as an echo event. A handler that pauses the model for a contact after an echo keeps it from answering over a person.
Second, sending is capped at a fixed 20 messages per second for the number. A conversational agent never gets near that, but a broadcast loop written with Promise.all over a contact list will, so batch it.
Everything else is the ordinary Cloud API. Inside 24 hours of the customer's last message, free text is allowed. After that, only an approved template goes through, and free text returns error 131047 without the customer seeing anything.
Common mistakes
- Returning the challenge with a JSON response helper. The quotes around it fail Meta's check.
- Running generateText before responding to Meta. On a cold start the retry produces a second answer.
- Forgetting to close the MCP client. Each request leaves a connection open until the platform kills it.
Connect the number at easycoexistence.com, set the webhook destination to your route handler, and add the MCP client with one OAuth sign-in. From US$ 9 per number per month, falling to US$ 1 as you add numbers, with the first 3 days free.
Frequently asked questions
Does this only work on Vercel?
No. The AI SDK is a TypeScript library, and the route can run on any host that serves HTTPS and answers a GET and a POST.
Do I need the MCP client at all?
Not for replying. A fetch to the Cloud API messages endpoint with the number's token does that. MCP earns its place when the model chooses what to do.
Which provider can the model be?
Any the AI SDK supports. The WhatsApp side is independent of the model, so switching providers does not touch the route or the Cloud API call.
Does EasyCoexistence see the messages?
No. Meta delivers to your route directly and message content is never stored on our side.
Keep reading
Ready to get started?
Set up WhatsApp Coexistence in minutes, not months. The app keeps working on the phone.
Start Free TrialVerified on