- MCP Server ToolsUpdated
- Managed MCP with chat()Updated
- Manual MCPUpdated
- MCP Type GenerationUpdated
- MCP Client InputUpdated
- MCP Server Tools: AdvancedNew
- MCP EventsNew
- MCP AppsUpdated
- Serve Tools over HTTPUpdated
- Serve Resources and PromptsUpdated
- Ask for InputUpdated
- Draft Text from a ToolUpdated
- MCP Server TasksUpdated
- MCP Server AuthUpdated
- MCP Server SessionsUpdated
- MCP Server on stdioUpdated
Persistence (Advanced)
- GenerationsUpdated
- Realtime Voice ChatUpdated
- Text-to-SpeechUpdated
- TranscriptionUpdated
- Transcription: AdvancedNew
- Voice CreationUpdated
- Audio RecordingUpdated
- Audio GenerationUpdated
- Image GenerationUpdated
- Image Generation: AdvancedNew
- Video GenerationUpdated
- Video Generation: AdvancedNew
- Live GenerationUpdated
- World GenerationUpdated
- Generation HooksUpdated
- OverviewUpdated
- Quick StartUpdated
- ProvidersUpdated
- HarnessesUpdated
- Harness AuthUpdated
- WorkspaceUpdated
- ToolsUpdated
- PolicyUpdated
- Lifecycle & SnapshotsUpdated
- Portable SnapshotsUpdated
- Keep Files After ReloadUpdated
- Save a Named VersionUpdated
- Branch From a VersionUpdated
- Send a Frozen FileUpdated
- Let the Agent Save and ForkUpdated
- Pick Which Files to KeepUpdated
- What a Snapshot StoresUpdated
- Instance DurabilityUpdated
- Durable RunsUpdated
- EventsUpdated
Sandboxes (Advanced)
- Runtime ContextUpdated
- Bring Your Own KeyUpdated
- Debug LoggingUpdated
- Multimodal ContentUpdated
- Files APIUpdated
- Per-Model Type SafetyUpdated
- Runtime Adapter SwitchingUpdated
- Tree-ShakingUpdated
- Extend AdapterUpdated
- Extend Adapter: AdvancedNew
- Typed Pre-Configured OptionsUpdated
- Test with a Fake ModelUpdated
- Prompt CachingUpdated
- Mid-Conversation ChangesUpdated
- Approval Flow ProcessingUpdated
Per-Model Type Safety
Provider options are typed from the model you pass to the adapter. An option that model does not support is a type error.
TanStack AI gives modelOptions the type of the exact model that you select. Your editor shows only the options that the model has, and TypeScript rejects the others.
Quick setup#
There is nothing to turn on. Pass the model to the adapter factory, then write modelOptions:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// gpt-6.1-sol supports structured outputs, so `text` is allowed.
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Give me a short answer.' }],
threadId: 'thread-1',
modelOptions: {
text: {
format: {
type: 'json_schema',
name: 'my_schema',
schema: {
type: 'object',
properties: { answer: { type: 'string' } },
required: ['answer'],
additionalProperties: false,
},
},
},
},
})Now pick a model without structured outputs. The same option is a type error:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// gpt-4-turbo does not support structured outputs, so `text` is rejected.
const stream = chat({
adapter: openaiText('gpt-4-turbo'),
messages: [{ role: 'user', content: 'Give me a short answer.' }],
threadId: 'thread-1',
modelOptions: {
text: {}, // TypeScript error: 'text' does not exist in type
},
})TypeScript reports:
error TS2353: Object literal may only specify known properties, and 'text' does not exist in type ...'.Tip
For structured output, use chat({ outputSchema }) in most apps. It works with every provider and validates the result. The raw text option is for control that is specific to the provider. See Structured Outputs.
What you get#
- Wrong model options fail at compile time, not in production.
- Autocomplete shows only the options of the selected model.
- The types document what each model can do.
- There is no runtime cost. All checks happen at compile time.
How it works#
Each adapter factory keeps the model name as a type parameter: openaiText<TModel>(model). So the adapter knows the exact model at the type level.
chat() looks up the options for that model in a map for each model (ResolveProviderOptions<TModel>). Each entry lists only the options that the model supports. A model without structured outputs has no text property in its options type. TypeScript then rejects text as an unknown property.
The same types also work in these places:
- Typed Pre-Configured Options: keep the options for one model in a reusable object.
- Extend Adapter: attach typed modelOptions to your own models.