Documentation Index Fetch the complete documentation index at: https://mintlify.com/mastra-ai/mastra/llms.txt
Use this file to discover all available pages before exploring further.
Structured Output
Structured output enables agents to return type-safe, validated data instead of plain text. This is essential for building reliable applications that integrate with your codebase.
Why Structured Output?
Benefits:
Type safety - Full TypeScript types inferred from schemas
Validation - Automatic validation of LLM responses
Reliability - Guaranteed output format
Integration - Easy to use in applications
Error handling - Graceful fallbacks when parsing fails
Basic Usage
From: packages/core/src/agent/agent.test.ts
Generate with Schema
import { Agent } from '@mastra/core/agent' ;
import { z } from 'zod' ;
const agent = new Agent ({
id: 'election-agent' ,
name: 'Election Agent' ,
instructions: 'You know about US presidential elections' ,
model: 'openai/gpt-4o' ,
});
const result = await agent . generate (
'Who won the 2012 US presidential election?' ,
{
structuredOutput: {
schema: z . object ({
winner: z . string (). describe ( 'Name of the winner' ),
year: z . number (). describe ( 'Election year' ),
party: z . enum ([ 'Democratic' , 'Republican' , 'Other' ]),
}),
},
}
);
// Type-safe access
console . log ( result . object . winner ); // "Barack Obama"
console . log ( result . object . year ); // 2012
console . log ( result . object . party ); // "Democratic"
Stream Structured Output
const stream = await agent . stream (
'Who won the 2012 election?' ,
{
structuredOutput: {
schema: z . object ({
winner: z . string (),
party: z . string (),
}),
},
}
);
// Stream partial objects as they're generated
for await ( const partial of stream . objectStream ) {
console . log ( 'Partial:' , partial );
// { winner: "Barack" }
// { winner: "Barack Obama" }
// { winner: "Barack Obama", party: "Democratic" }
}
// Get final validated object
const finalObject = await stream . object ;
console . log ( 'Complete:' , finalObject );
Schema Definition
Simple Schema
const weatherSchema = z . object ({
temperature: z . number (). describe ( 'Temperature in Fahrenheit' ),
condition: z . enum ([ 'sunny' , 'cloudy' , 'rainy' , 'snowy' ]),
humidity: z . number (). min ( 0 ). max ( 100 ),
forecast: z . string (). optional (),
});
const result = await agent . generate ( 'What is the weather in NYC?' , {
structuredOutput: { schema: weatherSchema },
});
type Weather = z . infer < typeof weatherSchema >;
const weather : Weather = result . object ;
Complex Nested Schema
const analysisSchema = z . object ({
summary: z . string (). describe ( 'Brief summary of findings' ),
sentiment: z . object ({
score: z . number (). min ( - 1 ). max ( 1 ),
label: z . enum ([ 'positive' , 'neutral' , 'negative' ]),
confidence: z . number (). min ( 0 ). max ( 1 ),
}),
topics: z . array (
z . object ({
name: z . string (),
relevance: z . number (),
})
). describe ( 'Key topics mentioned' ),
metadata: z . object ({
wordCount: z . number (),
readingTime: z . number (). describe ( 'Estimated reading time in minutes' ),
}),
});
const result = await agent . generate ( 'Analyze this article: ...' , {
structuredOutput: { schema: analysisSchema },
});
Array Output
const listSchema = z . object ({
items: z . array (
z . object ({
title: z . string (),
description: z . string (),
priority: z . enum ([ 'low' , 'medium' , 'high' ]),
})
),
total: z . number (),
});
const result = await agent . generate (
'List 5 steps to deploy a Next.js app' ,
{
structuredOutput: { schema: listSchema },
}
);
result . object . items . forEach (( item , i ) => {
console . log ( ` ${ i + 1 } . ${ item . title } [ ${ item . priority } ]` );
console . log ( ` ${ item . description } ` );
});
Configuration Options
Custom Instructions
Override the default structuring instructions:
const result = await agent . generate (
'Extract key information' ,
{
structuredOutput: {
schema: dataSchema ,
instructions: `Extract and structure the key information.
Focus on accuracy over completeness.
If a field is uncertain, use null.` ,
},
}
);
Error Strategies
Handle parsing failures gracefully:
// Strict mode (default) - throws on error
const result = await agent . generate ( prompt , {
structuredOutput: {
schema: mySchema ,
errorStrategy: 'strict' ,
},
});
// Warn mode - logs warning but continues
const result = await agent . generate ( prompt , {
structuredOutput: {
schema: mySchema ,
errorStrategy: 'warn' ,
},
});
// Fallback mode - returns fallback value
const result = await agent . generate ( prompt , {
structuredOutput: {
schema: mySchema ,
errorStrategy: 'fallback' ,
fallbackValue: {
status: 'error' ,
data: null ,
},
},
});
Custom Model
Use a different model for structuring:
import { openai } from '@ai-sdk/openai' ;
const result = await agent . generate (
'Analyze this data' ,
{
structuredOutput: {
schema: analysisSchema ,
model: openai ( 'gpt-4o-mini' ), // Faster, cheaper for extraction
},
}
);
JSON Prompt Injection
For models without native structured output:
const result = await agent . generate ( prompt , {
structuredOutput: {
schema: mySchema ,
jsonPromptInjection: true , // Use prompt engineering instead of native support
},
});
Real-World Examples
import { Agent } from '@mastra/core/agent' ;
import { z } from 'zod' ;
const extractionAgent = new Agent ({
id: 'extractor' ,
name: 'Data Extractor' ,
instructions: 'Extract structured data from unstructured text' ,
model: 'openai/gpt-4o' ,
});
const contactSchema = z . object ({
name: z . string (),
email: z . string (). email (). nullable (),
phone: z . string (). nullable (),
company: z . string (). nullable (),
role: z . string (). nullable (),
});
const unstructuredText = `
John Smith works as Senior Developer at Acme Corp.
You can reach him at john.smith@acme.com or call 555-1234.
` ;
const result = await extractionAgent . generate ( unstructuredText , {
structuredOutput: { schema: contactSchema },
});
console . log ( result . object );
/*
{
name: "John Smith",
email: "john.smith@acme.com",
phone: "555-1234",
company: "Acme Corp",
role: "Senior Developer"
}
*/
Classification
const classificationSchema = z . object ({
category: z . enum ([
'bug' ,
'feature-request' ,
'question' ,
'documentation' ,
'other'
]),
priority: z . enum ([ 'low' , 'medium' , 'high' , 'urgent' ]),
tags: z . array ( z . string ()). describe ( 'Relevant tags' ),
assignee: z . string (). nullable (). describe ( 'Suggested assignee' ),
confidence: z . number (). min ( 0 ). max ( 1 ),
});
const result = await agent . generate (
'The app crashes when I click the submit button on the payment form' ,
{
structuredOutput: { schema: classificationSchema },
}
);
console . log ( result . object );
/*
{
category: "bug",
priority: "high",
tags: ["payment", "crash", "ui"],
assignee: "frontend-team",
confidence: 0.95
}
*/
const formSchema = z . object ({
fields: z . array (
z . object ({
name: z . string (),
label: z . string (),
type: z . enum ([ 'text' , 'email' , 'number' , 'select' , 'textarea' ]),
required: z . boolean (),
placeholder: z . string (). optional (),
options: z . array ( z . string ()). optional (),
validation: z . object ({
min: z . number (). optional (),
max: z . number (). optional (),
pattern: z . string (). optional (),
}). optional (),
})
),
submitButton: z . string (),
});
const result = await agent . generate (
'Create a contact form with name, email, message, and a dropdown for department' ,
{
structuredOutput: { schema: formSchema },
}
);
// Use result to render a form
const form = result . object ;
form . fields . forEach ( field => {
renderFormField ( field );
});
const apiSchema = z . object ({
status: z . enum ([ 'success' , 'error' ]),
data: z . object ({
users: z . array (
z . object ({
id: z . string (),
name: z . string (),
email: z . string (),
active: z . boolean (),
})
),
}),
metadata: z . object ({
total: z . number (),
page: z . number (),
perPage: z . number (),
}),
});
const result = await agent . generate (
'Transform this CSV data into a paginated API response format' ,
{
structuredOutput: { schema: apiSchema },
}
);
// Result matches your API contract exactly
return Response . json ( result . object );
Test Case Generation
const testCaseSchema = z . object ({
testCases: z . array (
z . object ({
name: z . string (). describe ( 'Test case name' ),
description: z . string (),
given: z . string (). describe ( 'Initial state/setup' ),
when: z . string (). describe ( 'Action to perform' ),
then: z . string (). describe ( 'Expected result' ),
priority: z . enum ([ 'low' , 'medium' , 'high' ]),
})
),
});
const result = await agent . generate (
'Generate test cases for a login form' ,
{
structuredOutput: { schema: testCaseSchema },
}
);
result . object . testCases . forEach ( test => {
console . log ( `Test: ${ test . name } ` );
console . log ( ` Given: ${ test . given } ` );
console . log ( ` When: ${ test . when } ` );
console . log ( ` Then: ${ test . then } ` );
});
From: packages/core/src/agent/agent.test.ts
import { createTool } from '@mastra/core/tools' ;
const searchTool = createTool ({
id: 'search' ,
description: 'Search the knowledge base' ,
inputSchema: z . object ({ query: z . string () }),
execute : async ({ query }) => {
return { results: [ ... ] };
},
});
const agent = new Agent ({
id: 'research-agent' ,
name: 'Research Agent' ,
instructions: 'Research topics and provide structured summaries' ,
model: 'openai/gpt-4o' ,
tools: { searchTool },
});
// Agent can use tools AND return structured output
const result = await agent . generate (
'Research quantum computing' ,
{
maxSteps: 5 , // Allow tool calls
structuredOutput: {
schema: z . object ({
topic: z . string (),
summary: z . string (),
keyPoints: z . array ( z . string ()),
sources: z . array ( z . object ({
title: z . string (),
url: z . string (),
})),
confidence: z . number (),
}),
},
}
);
// Agent searched using tools, then returned structured data
console . log ( result . object );
console . log ( 'Tools used:' , result . toolCalls . length );
Type Inference
Zod schemas provide full TypeScript types:
const userSchema = z . object ({
id: z . string (),
name: z . string (),
email: z . string (),
age: z . number (),
verified: z . boolean (),
});
// Infer TypeScript type from schema
type User = z . infer < typeof userSchema >;
const result = await agent . generate ( prompt , {
structuredOutput: { schema: userSchema },
});
// result.object is typed as User
const user : User = result . object ;
// Type-safe access with autocomplete
console . log ( user . name );
console . log ( user . email );
// TypeScript error - property doesn't exist
// console.log(user.invalidField);
Best Practices
Add descriptions to fields
Help the LLM understand what each field represents: const schema = z . object ({
temperature: z . number (). describe ( 'Temperature in Fahrenheit' ),
condition: z . string (). describe ( 'Weather condition (sunny, cloudy, etc.)' ),
});
Use enums for constrained values
Ensure valid categories: const schema = z . object ({
status: z . enum ([ 'pending' , 'approved' , 'rejected' ]),
priority: z . enum ([ 'low' , 'medium' , 'high' ]),
});
Handle optional fields properly
Use .optional() or .nullable() for fields that may not exist: const schema = z . object ({
name: z . string (),
email: z . string (). optional (),
phone: z . string (). nullable (),
});
Validate ranges and formats
Use error strategies in production
Handle parsing failures gracefully: const result = await agent . generate ( prompt , {
structuredOutput: {
schema: mySchema ,
errorStrategy: 'fallback' ,
fallbackValue: getDefaultValue (),
},
});
Next Steps
Tools Combine structured output with tools
Memory Persist structured conversations
Workflows Use structured output in workflows
API Reference Complete API documentation