Skip to content

Build TypeScript Agents with Mastra & Vercel AI SDK

Mastra and Vercel AI SDK give TypeScript developers typed tools and Zod-validated outputs. Build a typed production AI agent step by step.

· · 6 min read
TypeScript code with AI agent tool definitions and type annotations visible in a code editor

Quick Take

Two TypeScript-native frameworks now cover the whole AI agent stack: Mastra handles orchestration and tool wiring, Vercel AI SDK handles the generation layer. Together they give you typed tools, Zod-validated outputs, and streaming, with the TypeScript compiler catching mistakes before the agent ever runs.

Most TypeScript AI agent tutorials skip the part that actually matters: what type is that tool's output? What does the agent return when it's done? "It returns a string" is not an answer a production TypeScript codebase accepts.

I've been building TypeScript AI agents for eight months. The shift that made them production-ready wasn't a better model or a more powerful framework. It was defining types before writing agent logic, the same pattern that works for TypeScript contracts in LLM prompting.

TL;DR:

  • Mastra handles agent orchestration, tools, memory, multi-step reasoning
  • Vercel AI SDK handles the generation layer, model calls, streaming, structured outputs
  • TypeScript with Zod schemas catches tool argument errors and output mismatches at compile time
  • The full stack installs in under 10 minutes; a typed agent runs in under 30

Why These Two SDKs Together

Mastra and Vercel AI SDK solve different problems. Mastra is the agent architecture layer: you define tools, register them with agents, and describe what the agent should do. It handles multi-step reasoning, the agent deciding which tool to call, in what order, with what arguments.

Vercel AI SDK is the model communication layer. It abstracts over OpenAI, Anthropic, Google Gemini, and a dozen other providers. Its generateText, generateObject, and streamText functions have consistent TypeScript signatures regardless of which model you're talking to.

Neither replaces the other. What you get by combining them: Mastra's opinionated agent patterns on top of Vercel AI SDK's polished provider integrations. Here's what installing them looks like:

npm install @mastra/core ai @ai-sdk/openai zod

Four packages. Mastra core, the Vercel AI SDK, the OpenAI provider, and Zod. That's the entire typed agent stack.

Typed Tools Are the Foundation

Tools are where TypeScript earns its place. A tool with any types isn't safer than a raw API call. A tool with Zod schemas is something the compiler can reason about.

import { createTool } from '@mastra/core';
import { z } from 'zod';

const searchFiles = createTool({
  id: 'search-files',
  description: 'Search TypeScript files for a pattern or function name',
  inputSchema: z.object({
    pattern: z.string().describe('Search pattern or function name'),
    fileExtension: z.enum(['.ts', '.tsx']).optional().default('.ts'),
    maxResults: z.number().int().positive().max(20).default(10),
  }),
  outputSchema: z.object({
    matches: z.array(z.object({
      file: z.string(),
      line: z.number(),
      snippet: z.string(),
    })),
    totalCount: z.number(),
  }),
  execute: async ({ context }) => {
    // context is fully typed: { pattern: string, fileExtension: '.ts' | '.tsx', maxResults: number }
    const results = await runSearch(context.pattern, context.fileExtension);
    return { matches: results, totalCount: results.length };
  },
});

The inputSchema tells the model exactly what arguments to pass. The outputSchema validates what your handler returns and types the result throughout the rest of your code. Wrong argument type? Compiler error before the agent runs. Handler returns the wrong shape? Compiler error at the return statement.

This is what separates TypeScript AI agents from Python AI agents in practice. Python agents fail at runtime with attribute errors. TypeScript agents fail at compile time with specific messages.

A 3D neural-network sculpture of glowing nodes and connections floating above a metallic base
Photo by Growtika on Unsplash

Structured Outputs With Zod Schemas

Some agents return free text. Most production agents return structured data. Vercel AI SDK's generateObject handles this with a Zod schema:

import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';

const ReviewResult = z.object({
  issues: z.array(z.object({
    severity: z.enum(['error', 'warning', 'suggestion']),
    file: z.string(),
    description: z.string(),
    suggestedFix: z.string().optional(),
  })),
  overallAssessment: z.enum(['approve', 'request-changes', 'needs-discussion']),
  confidence: z.number().min(0).max(1),
});

type ReviewResult = z.infer<typeof ReviewResult>;

const { object } = await generateObject({
  model: openai('gpt-4o'),
  schema: ReviewResult,
  prompt: `Review this TypeScript PR diff for issues:\n\n${diff}`,
});

// object is typed as ReviewResult, no type assertions, no casting
object.issues.forEach(issue => {
  console.log(`[${issue.severity}] ${issue.file}: ${issue.description}`);
});

The schema does two things simultaneously. It generates the structured output prompt that tells the model what format to return. And it validates and types the response so your downstream code has full TypeScript inference. If the model returns something that doesn't match the schema, the SDK throws at the boundary, loud failure close to the source, not a silent type mismatch three function calls deep.

The Complete Agent Pattern

A Mastra agent combines tools and instructions into a unit that reasons across multiple steps:

import { Agent } from '@mastra/core';

const codeReviewAgent = new Agent({
  name: 'code-reviewer',
  instructions: `You review TypeScript code for correctness, type safety, and style.
Use the searchFiles tool to find relevant context before making judgments.
Return structured findings with severity levels and suggested fixes.`,
  model: openai('gpt-4o'),
  tools: { searchFiles },
});

const result = await codeReviewAgent.generate(
  'Review the authentication module for type safety issues',
  { output: ReviewResult }
);

// result.object is ReviewResult
console.log(`Decision: ${result.object.overallAssessment}`);
console.log(`Issues: ${result.object.issues.length}`);

The agent can call searchFiles multiple times across reasoning steps, searching for the auth module, then for usage sites, then for related types, before returning the final ReviewResult. The TypeScript types flow through the entire chain.

A glowing circuit-board pattern shaped like a human brain
Photo by Sumaid pal Singh Bakshi on Unsplash

Streaming Long-Running Agents

Some agent runs take 20-40 seconds on complex tasks. Don't make users stare at a spinner. Stream the reasoning:

const stream = await codeReviewAgent.stream(
  'Review all API route handlers for input validation gaps'
);

for await (const chunk of stream.textStream) {
  process.stdout.write(chunk); // or push to a UI
}

const finalResult = await stream.object; // typed as ReviewResult

stream.object carries the same type as generate's result. The Zod schema validates the final structured output when the stream completes. You get streaming UX without giving up type safety.

The Pattern Applied

The same principles that make TypeScript strict for AI apply to AI agents: define the shape first, let the tools enforce it. An agent with untyped tools and any return values gives you the same class of silent runtime failures that untyped AI code does.

Zod schemas as tool contracts, generateObject for structured outputs, TypeScript inference throughout, these aren't nice-to-haves. They're the difference between an agent that fails loudly at compile time and one that fails silently at 2am in production. The CodeRabbit + GitHub Actions setup catches any any that slips through into your agent code.

Once your agent is running, the next question is how to connect it to external databases, filesystems, and APIs in a standardized way. That's exactly what Model Context Protocol addresses, a JSON-RPC-based standard for wiring AI agents to developer tools without custom integrations for every service.

Define the types. Build the agent. Let the compiler verify the chain.

Frequently Asked Questions

What is Mastra and how does it differ from Vercel AI SDK?
Mastra is a TypeScript agent orchestration framework, it handles the agent architecture: defining tools, managing multi-step reasoning, wiring memory, and composing workflows. Vercel AI SDK is the generation layer, it talks to OpenAI, Anthropic, Google, and other models through a consistent interface and handles streaming, structured outputs, and React hooks. They're complementary: Mastra for 'how the agent thinks', Vercel AI SDK for 'how it talks to models'.
Do I need both Mastra and Vercel AI SDK together?
No, you can use either independently. Vercel AI SDK alone is great for adding AI to a Next.js app: streaming chat, structured outputs, simple tool use. Mastra alone can work with any provider. But using both gives you Mastra's opinionated agent patterns plus Vercel AI SDK's polished provider integrations and streaming primitives. The combination is what most production TypeScript agent projects use.
How does TypeScript improve AI agent reliability?
AI agent failures tend to cluster around two problems: tools called with wrong arguments, and outputs with unexpected shapes. TypeScript with Zod schemas catches both at compile time. When you define a tool's inputSchema as a Zod object, the agent framework validates arguments before calling the tool, and TypeScript infers the exact type of the context parameter inside your handler. Same for outputs: generateObject with a Zod schema validates the model's response before it reaches your code.
What version of Mastra and Vercel AI SDK does this tutorial use?
This tutorial uses Mastra 0.1.x and Vercel AI SDK 4.x (the ai package). Mastra launched in January 2025 and has been iterating rapidly, check the changelog for breaking changes. Vercel AI SDK 4.0 introduced the current generateObject and streamObject APIs. The core patterns here (typed tools, Zod output schemas, streaming) are stable across both SDKs' recent versions.