Chapter 15

Structured Outputs

Return validated JSON from agent workflows using JSON Schema, Zod, or Pydantic. Get type-safe, structured data after multi-turn tool use.

Structured outputs let you define the exact shape of data you want back from an agent. The agent can use any tools it needs to complete the task, and you still get validated JSON matching your schema at the end. Define a JSON Schema for the structure you need, and the SDK guarantees the output matches it.

For full type safety, use Zod (TypeScript) or Pydantic (Python) to define your schema and get strongly-typed objects back.

Why structured outputs?

Agents return free-form text by default, which works for chat but not when you need to use the output programmatically. Structured outputs give you typed data you can pass directly to your application logic, database, or UI components.

Consider a recipe app where an agent searches the web and brings back recipes. Without structured outputs, you get free-form text that you'd need to parse yourself. With structured outputs, you define the shape you want and get typed data you can use directly in your app.

Without structured outputs

Here's a classic chocolate chip cookie recipe!

Chocolate Chip Cookies

Prep time: 15 minutes | Cook time: 10 minutes

Ingredients:

- 2 1/4 cups all-purpose flour

- 1 cup butter, softened

...

To use this in your app, you'd need to parse out the title, convert "15 minutes" to a number, separate ingredients from instructions, and handle inconsistent formatting.

With structured outputs

{ "name": "Chocolate Chip Cookies", "prep_time_minutes": 15, "cook_time_minutes": 10, "ingredients": [ { item, amount, unit }... ] }

Typed data you can use directly in your UI.

Quick Start

Define a JSON Schema describing the shape of data you want, then pass it to query() via the outputFormat option. When the agent finishes, the result message includes a structured_output field with validated data matching your schema.

structured-output-quickstart.ts
import { query } from '@anthropic-ai/claude-agent-sdk'

// Define the shape of data you want back
const schema = {
  type: 'object',
  properties: {
    company_name: { type: 'string' },
    founded_year: { type: 'number' },
    headquarters: { type: 'string' }
  },
  required: ['company_name']
}

for await (const message of query({
  prompt: 'Research Anthropic and provide key company information',
  options: {
    outputFormat: {
      type: 'json_schema',
      schema: schema
    }
  }
})) {
  // The result message contains structured_output with validated data
  if (message.type === 'result' && message.structured_output) {
    console.log(message.structured_output)
    // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
  }
}

Type-safe schemas with Zod

Instead of writing JSON Schema by hand, use Zod to define your schema. Zod generates the JSON Schema for you and lets you parse the response into a fully-typed object with autocomplete and type checking.

zod-schema.ts
import { z } from 'zod'
import { query } from '@anthropic-ai/claude-agent-sdk'

// Define schema with Zod
const FeaturePlan = z.object({
  feature_name: z.string(),
  summary: z.string(),
  steps: z.array(z.object({
    step_number: z.number(),
    description: z.string(),
    estimated_complexity: z.enum(['low', 'medium', 'high'])
  })),
  risks: z.array(z.string())
})

type FeaturePlan = z.infer<typeof FeaturePlan>

// Convert to JSON Schema
const schema = z.toJSONSchema(FeaturePlan)

// Use in query
for await (const message of query({
  prompt: 'Plan how to add dark mode support to a React app',
  options: { outputFormat: { type: 'json_schema', schema } }
})) {
  if (message.type === 'result' && message.structured_output) {
    // Validate and get fully typed result
    const parsed = FeaturePlan.safeParse(message.structured_output)
    if (parsed.success) {
      const plan: FeaturePlan = parsed.data
      console.log(`Feature: ${plan.feature_name}`)
    }
  }
}

Benefits

  • - Full type inference with TypeScript
  • - Runtime validation with safeParse()
  • - Better error messages
  • - Composable, reusable schemas

Output Format Configuration

The outputFormat option accepts an object with:

PropertyTypeDescription
type"json_schema"Set to "json_schema" for structured outputs
schemaobjectA JSON Schema object defining your output structure

The SDK supports standard JSON Schema features including all basic types (object, array, string, number, boolean, null), enum, const, required, nested objects, and $ref definitions.

Example: TODO Tracking Agent

This example demonstrates how structured outputs work with multi-step tool use. The agent needs to find TODO comments in the codebase, then look up git blame information for each one. It autonomously decides which tools to use (Grep to search, Bash to run git commands) and combines the results into a single structured response.

todo-tracker.ts
import { query } from '@anthropic-ai/claude-agent-sdk'

// Define structure for TODO extraction
const todoSchema = {
  type: 'object',
  properties: {
    todos: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          text: { type: 'string' },
          file: { type: 'string' },
          line: { type: 'number' },
          author: { type: 'string' },
          date: { type: 'string' }
        },
        required: ['text', 'file', 'line']
      }
    },
    total_count: { type: 'number' }
  },
  required: ['todos', 'total_count']
}

// Agent uses Grep to find TODOs, Bash to get git blame info
for await (const message of query({
  prompt: 'Find all TODO comments and identify who added them',
  options: { outputFormat: { type: 'json_schema', schema: todoSchema } }
})) {
  if (message.type === 'result' && message.structured_output) {
    const data = message.structured_output
    console.log(`Found ${data.total_count} TODOs`)
  }
}

The schema includes optional fields (author and date) since git blame information might not be available for all files. The agent fills in what it can find and omits the rest.

Error Handling

Structured output generation can fail when the agent cannot produce valid JSON matching your schema. When an error occurs, the result message has a subtype indicating what went wrong.

SubtypeMeaning
successOutput was generated and validated successfully
error_max_structured_output_retriesAgent couldn't produce valid output after multiple attempts
error-handling.ts
for await (const msg of query({ prompt, options })) {
  if (msg.type === 'result') {
    if (msg.subtype === 'success' && msg.structured_output) {
      // Use the validated output
      console.log(msg.structured_output)
    } else if (msg.subtype === 'error_max_structured_output_retries') {
      // Handle the failure
      console.error('Could not produce valid output')
    }
  }
}

Tips for Avoiding Errors

1

Keep schemas focused

Deeply nested schemas with many required fields are harder to satisfy. Start simple and add complexity as needed.

2

Match schema to task

If the task might not have all the information your schema requires, make those fields optional.

3

Use clear prompts

Ambiguous prompts make it harder for the agent to know what output to produce.