Infer types from the agent

Type useChat with the message type inferred from your agent.

AI SDK docsai-sdk.dev
Full type safety for your messages and tools
Agents · End-to-end Type SafetyRead docs ↗

From ai-sdk, “Building and Consuming Agents”.

The server renames a field

Press Rename city → location, then read each result.

Untyped useChatBad
agent.ts
    part.input as { city: string }
    Typed useChatGood
    agent.ts
      InferAgentUIMessage<typeof weatherAgent>
      // agent/weather-agent.ts
      import { ToolLoopAgent, tool, type InferAgentUIMessage } from 'ai';
      import { z } from 'zod';
      
      export const weatherAgent = new ToolLoopAgent({
        model: 'anthropic/claude-sonnet-5',
        tools: {
          weather: tool({
            inputSchema: z.object({ city: z.string() }),
            execute: async ({ city }) => getWeather(city),
          }),
        },
      });
      
      export type WeatherAgentUIMessage = InferAgentUIMessage<typeof weatherAgent>;
      
      // components/chat.tsx
      'use client';
      import { useChat } from '@ai-sdk/react';
      import type { WeatherAgentUIMessage } from '@/agent/weather-agent';
      
      const { messages } = useChat<WeatherAgentUIMessage>();
      // part.type === 'tool-weather' → part.input is { city: string }

      Why it matters, pros and cons

      Why it matters

      Tools are defined on the server and rendered on the client. One inferred type keeps both sides in step.

      Pros

      • A renamed tool or field fails the build, not the user.
      • Tool part names and inputs autocomplete.
      • No hand-written copy of the schema to drift.

      Cons

      • The client imports a type from the agent file; keep it type-only.
      • Stored old messages still have the old shape.
      • A cast like as { city } switches it off again.