TL;DR: Use the AI SDK's MCP client to import Ballet's tools, or define a single tool() that calls the execute endpoint. Point the MCP client at https://api.ballet.dev/mcp with an Authorization: Bearer header, await mcpClient.tools(), and pass them to generateText.
TypeScript developers building agents with the Vercel AI SDK.
Configure the AI SDK's MCP client with the HTTP transport and your token. The returned tools (run_playbook, get_run, list_playbooks, and the rest) drop straight into generateText or streamText.
import { openai } from "@ai-sdk/openai";
import { createMCPClient } from "@ai-sdk/mcp";
import { generateText, stepCountIs } from "ai";
const mcpClient = await createMCPClient({
transport: {
type: "http",
url: "https://api.ballet.dev/mcp",
headers: { Authorization: `Bearer ${process.env.BALLET_API_TOKEN}` },
},
});
try {
const tools = await mcpClient.tools();
const { text } = await generateText({
model: openai("gpt-4o"),
tools,
stopWhen: stepCountIs(10),
prompt: "Run the lead-enrichment playbook for cus_123 and summarize the result.",
});
console.log(text);
} finally {
await mcpClient.close();
}
For interactive apps you can supply an authProvider instead of a static header to use Ballet's browser-based OAuth — see the MCP endpoint.
When you only need to expose a single playbook, define a tool() that calls the REST execute endpoint and returns the run output.
import { tool } from "ai";
import { z } from "zod";
export const runPlaybook = tool({
description: "Run the customer onboarding playbook in Ballet.",
inputSchema: z.object({ customerId: z.string() }),
execute: async ({ customerId }) => {
const res = await fetch(
`https://api.ballet.dev/api/playbooks/${process.env.BALLET_PLAYBOOK_ID}/execute`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BALLET_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ input: { customerId } }),
},
);
let result;
for (const frame of (await res.text()).split("\n\n")) {
const line = frame.split("\n").find((l) => l.startsWith("data:"));
if (!line) continue;
const event = JSON.parse(line.slice(5).trim());
if (event.type === "run_stop") result = event.run;
}
return { success: result?.success, output: result?.output };
},
});
See Run playbooks over the REST API for the streaming details.
Both surfaces are served from api.ballet.dev. app.ballet.dev serves Ballet Studio and will not answer API or MCP requests.